resolved conflicts for merge of 9f2a2259 to lmp-mr1-dev-plus-aosp

Change-Id: I1aa72203e0a24250b78e7af866d1961ecd272472
diff --git a/Android.mk b/Android.mk
index 15e8308..d11d011 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,16 +23,18 @@
 #
 
 include $(art_path)/build/Android.common_path.mk
+include $(art_path)/build/Android.oat.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*, build-art*, or related target.
+art_test_bother := false
+ifneq (,$(filter %tests test-art% valgrind-test-art% build-art%,$(MAKECMDGOALS)))
+  art_test_bother := true
 endif
 
 .PHONY: clean-oat
@@ -40,20 +42,14 @@
 
 .PHONY: clean-oat-host
 clean-oat-host:
-	rm -f $(HOST_CORE_IMG_OUT)
-	rm -f $(HOST_CORE_OAT_OUT)
+	rm -f $(HOST_CORE_IMG_OUTS)
+	rm -f $(HOST_CORE_OAT_OUTS)
 	rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/*.odex
 ifneq ($(HOST_PREFER_32_BIT),true)
-	rm -f $(2ND_HOST_CORE_IMG_OUT)
-	rm -f $(2ND_HOST_CORE_OAT_OUT)
 	rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/*.odex
 endif
-	rm -f $(TARGET_CORE_IMG_OUT)
-	rm -f $(TARGET_CORE_OAT_OUT)
-ifdef TARGET_2ND_ARCH
-	rm -f $(2ND_TARGET_CORE_IMG_OUT)
-	rm -f $(2ND_TARGET_CORE_OAT_OUT)
-endif
+	rm -f $(TARGET_CORE_IMG_OUTS)
+	rm -f $(TARGET_CORE_OAT_OUTS)
 	rm -rf $(DEXPREOPT_PRODUCT_DIR_FULL_PATH)
 	rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*.odex
 	rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*/*.oat
@@ -77,7 +73,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)/*/*
@@ -107,7 +104,6 @@
 include $(art_path)/patchoat/Android.mk
 include $(art_path)/dalvikvm/Android.mk
 include $(art_path)/tools/Android.mk
-include $(art_path)/build/Android.oat.mk
 include $(art_path)/sigchainlib/Android.mk
 
 
@@ -123,10 +119,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 :=
 
@@ -136,9 +137,17 @@
 
 # Sync test files to the target, depends upon all things that must be pushed to the target.
 .PHONY: test-art-target-sync
+ifeq ($(ART_TEST_ANDROID_ROOT),)
 test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
-	adb remount
+	adb root
+	adb wait-for-device remount
 	adb sync
+else
+test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
+	adb root
+	adb wait-for-device push $(ANDROID_PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT)
+	adb push $(ANDROID_PRODUCT_OUT)/data /data
+endif
 
 # Undefine variable now its served its purpose.
 TEST_ART_TARGET_SYNC_DEPS :=
@@ -229,6 +238,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 +304,8 @@
 	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
 endif
 
+endif  # art_test_bother
+
 ########################################################################
 # oat-target and oat-target-sync rules
 
@@ -314,9 +330,9 @@
 .PHONY: oat-target-$(1)
 oat-target-$(1): $$(OUT_OAT_FILE)
 
-$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OATD_DEPENDENCY)
+$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OAT_DEPENDENCY)
 	@mkdir -p $$(dir $$@)
-	$(DEX2OATD) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
+	$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
 		--boot-image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --dex-file=$(PRODUCT_OUT)/$(1) \
 		--dex-location=/$(1) --oat-file=$$@ \
 		--instruction-set=$(DEX2OAT_TARGET_ARCH) \
@@ -340,7 +356,8 @@
 
 .PHONY: oat-target-sync
 oat-target-sync: oat-target
-	adb remount
+	adb root
+	adb wait-for-device remount
 	adb sync
 
 ########################################################################
@@ -349,51 +366,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_OUTS)
 
 .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_OUTS)
 
 ########################################################################
 # 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 +407,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 +417,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 +427,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 +437,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 +447,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 +458,7 @@
 ########################################################################
 
 endif # !art_dont_bother
+
+# Clear locally used variables.
+art_dont_bother :=
+art_test_bother :=
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 39a734d..39e78fa 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -42,10 +42,17 @@
     $(error Do not know what to do with this multi-target configuration!)
   endif
 else
-  ART_PHONY_TEST_TARGET_SUFFIX := 32
-  2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
-  ART_TARGET_ARCH_32 := $(TARGET_ARCH)
-  ART_TARGET_ARCH_64 :=
+  ifneq ($(filter %64,$(TARGET_ARCH)),)
+    ART_PHONY_TEST_TARGET_SUFFIX := 64
+    2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
+    ART_TARGET_ARCH_32 :=
+    ART_TARGET_ARCH_64 := $(TARGET_ARCH)
+  else
+    ART_PHONY_TEST_TARGET_SUFFIX := 32
+    2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
+    ART_TARGET_ARCH_32 := $(TARGET_ARCH)
+    ART_TARGET_ARCH_64 :=
+  endif
 endif
 
 ART_HOST_SHLIB_EXTENSION := $(HOST_SHLIB_SUFFIX)
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 0dcefea..7e58f5c 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -84,27 +84,10 @@
 endif
 
 #
-# Used to enable optimizing compiler
-#
-ART_USE_OPTIMIZING_COMPILER := false
-ifneq ($(wildcard art/USE_OPTIMIZING_COMPILER),)
-$(info Enabling ART_USE_OPTIMIZING_COMPILER because of existence of art/USE_OPTIMIZING_COMPILER)
-ART_USE_OPTIMIZING_COMPILER := true
-endif
-ifeq ($(WITH_ART_USE_OPTIMIZING_COMPILER), true)
-ART_USE_OPTIMIZING_COMPILER := true
-endif
-
-ifeq ($(ART_USE_OPTIMIZING_COMPILER),true)
-DEX2OAT_FLAGS := --compiler-backend=Optimizing
-DALVIKVM_FLAGS += -Xcompiler-option --compiler-backend=Optimizing
-endif
-
-#
 # Used to change the default GC. Valid values are CMS, SS, GSS. The default is CMS.
 #
-ART_DEFAULT_GC_TYPE ?= CMS
-ART_DEFAULT_GC_TYPE_CFLAGS := -DART_DEFAULT_GC_TYPE_IS_$(ART_DEFAULT_GC_TYPE)
+art_default_gc_type ?= CMS
+art_default_gc_type_cflags := -DART_DEFAULT_GC_TYPE_IS_$(art_default_gc_type)
 
 ifeq ($(ART_USE_PORTABLE_COMPILER),true)
   LLVM_ROOT_PATH := external/llvm
@@ -112,6 +95,9 @@
   -include $(LLVM_ROOT_PATH)/llvm.mk
 endif
 
+ART_HOST_CFLAGS :=
+ART_TARGET_CFLAGS :=
+
 # Clang build support.
 
 # Host.
@@ -122,7 +108,11 @@
 endif
 
 # Clang on the target. Target builds use GCC by default.
-ART_TARGET_CLANG :=
+ifneq ($(USE_CLANG_PLATFORM_BUILD),)
+ART_TARGET_CLANG := $(USE_CLANG_PLATFORM_BUILD)
+else
+ART_TARGET_CLANG := false
+endif
 ART_TARGET_CLANG_arm :=
 ART_TARGET_CLANG_arm64 :=
 ART_TARGET_CLANG_mips :=
@@ -137,6 +127,66 @@
       endif)
 endef
 
+ART_TARGET_CLANG_CFLAGS :=
+ART_TARGET_CLANG_CFLAGS_arm :=
+ART_TARGET_CLANG_CFLAGS_arm64 :=
+ART_TARGET_CLANG_CFLAGS_mips :=
+ART_TARGET_CLANG_CFLAGS_x86 :=
+ART_TARGET_CLANG_CFLAGS_x86_64 :=
+
+# These are necessary for Clang ARM64 ART builds. TODO: remove.
+ART_TARGET_CLANG_CFLAGS_arm64  += \
+  -Wno-implicit-exception-spec-mismatch \
+  -DNVALGRIND \
+  -Wno-unused-value
+
+# FIXME: upstream LLVM has a vectorizer bug that needs to be fixed
+ART_TARGET_CLANG_CFLAGS_arm64 += \
+  -fno-vectorize
+
+# Colorize clang compiler warnings.
+art_clang_cflags := -fcolor-diagnostics
+
+# Warn about thread safety violations with clang.
+art_clang_cflags += -Wthread-safety
+
+# Warn if switch fallthroughs aren't annotated.
+art_clang_cflags += -Wimplicit-fallthrough
+
+# Enable float equality warnings.
+art_clang_cflags += -Wfloat-equal
+
+# Enable warning of converting ints to void*.
+art_clang_cflags += -Wint-to-void-pointer-cast
+
+# GCC-only warnings.
+art_gcc_cflags := -Wunused-but-set-parameter
+# Suggest const: too many false positives, but good for a trial run.
+#                  -Wsuggest-attribute=const
+# Useless casts: too many, as we need to be 32/64 agnostic, but the compiler knows.
+#                  -Wuseless-cast
+# Zero-as-null: Have to convert all NULL and "diagnostic ignore" all includes like libnativehelper
+# that are still stuck pre-C++11.
+#                  -Wzero-as-null-pointer-constant \
+# Suggest final: Have to move to a more recent GCC.
+#                  -Wsuggest-final-types
+
+
+ifeq ($(ART_HOST_CLANG),true)
+  ART_HOST_CFLAGS += $(art_clang_cflags)
+else
+  ART_HOST_CFLAGS += $(art_gcc_cflags)
+endif
+ifeq ($(ART_TARGET_CLANG),true)
+  ART_TARGET_CFLAGS += $(art_clang_cflags)
+else
+  ART_TARGET_CFLAGS += $(art_gcc_cflags)
+endif
+
+# Clear local variables now their use has ended.
+art_clang_cflags :=
+art_gcc_cflags :=
+
 ART_CPP_EXTENSION := .cc
 
 ART_C_INCLUDES := \
@@ -147,6 +197,7 @@
   external/zlib \
   frameworks/compile/mclinker/include
 
+# Base set of cflags used by all things ART.
 art_cflags := \
   -fno-rtti \
   -std=gnu++11 \
@@ -154,24 +205,18 @@
   -Wall \
   -Werror \
   -Wextra \
-  -Wno-sign-promo \
-  -Wno-unused-parameter \
   -Wstrict-aliasing \
   -fstrict-aliasing \
-  -fvisibility=protected
+  -Wunreachable-code \
+  -Wno-conversion-null \
+  -Wredundant-decls \
+  -Wshadow \
+  -fvisibility=protected \
+  $(art_default_gc_type_cflags)
 
-ART_TARGET_CLANG_CFLAGS :=
-ART_TARGET_CLANG_CFLAGS_arm :=
-ART_TARGET_CLANG_CFLAGS_arm64 :=
-ART_TARGET_CLANG_CFLAGS_mips :=
-ART_TARGET_CLANG_CFLAGS_x86 :=
-ART_TARGET_CLANG_CFLAGS_x86_64 :=
+# Missing declarations: too many at the moment, as we use "extern" quite a bit.
+#  -Wmissing-declarations \
 
-# these are necessary for Clang ARM64 ART builds
-ART_TARGET_CLANG_CFLAGS_arm64  += \
-  -Wno-implicit-exception-spec-mismatch \
-  -DNVALGRIND \
-  -Wno-unused-value
 
 ifeq ($(ART_SMALL_MODE),true)
   art_cflags += -DART_SMALL_MODE=1
@@ -181,14 +226,18 @@
   art_cflags += -DART_SEA_IR_MODE=1
 endif
 
+# Cflags for non-debug ART and ART tools.
 art_non_debug_cflags := \
   -O3
 
-art_host_non_debug_cflags := \
-  $(art_non_debug_cflags)
+# Cflags for debug ART and ART tools.
+art_debug_cflags := \
+  -O2 \
+  -DDYNAMIC_ANNOTATIONS_ENABLED=1 \
+  -UNDEBUG
 
-art_target_non_debug_cflags := \
-  $(art_non_debug_cflags)
+art_host_non_debug_cflags := $(art_non_debug_cflags)
+art_target_non_debug_cflags := $(art_non_debug_cflags)
 
 ifeq ($(HOST_OS),linux)
   # Larger frame-size for host clang builds today
@@ -196,26 +245,21 @@
   art_target_non_debug_cflags += -Wframe-larger-than=1728
 endif
 
-# FIXME: upstream LLVM has a vectorizer bug that needs to be fixed
-ART_TARGET_CLANG_CFLAGS_arm64 += \
-  -fno-vectorize
-
-art_debug_cflags := \
-  -O1 \
-  -DDYNAMIC_ANNOTATIONS_ENABLED=1 \
-  -UNDEBUG
-
 ifndef LIBART_IMG_HOST_BASE_ADDRESS
   $(error LIBART_IMG_HOST_BASE_ADDRESS unset)
 endif
-ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
+ART_HOST_CFLAGS += $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
 ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default
-ART_HOST_CFLAGS += $(ART_DEFAULT_GC_TYPE_CFLAGS)
 
 ifndef LIBART_IMG_TARGET_BASE_ADDRESS
   $(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
 endif
-ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS)
+ART_TARGET_CFLAGS += $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS)
+
+ART_HOST_NON_DEBUG_CFLAGS := $(art_host_non_debug_cflags)
+ART_TARGET_NON_DEBUG_CFLAGS := $(art_target_non_debug_cflags)
+ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags)
+ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags)
 
 ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA
   LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA=-0x1000000
@@ -235,7 +279,6 @@
 ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA)
 ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA)
 
-ART_TARGET_LDFLAGS :=
 ifeq ($(TARGET_CPU_SMP),true)
   ART_TARGET_CFLAGS += -DANDROID_SMP=1
 else
@@ -247,60 +290,26 @@
     ART_TARGET_CFLAGS += -DANDROID_SMP=1
   endif
 endif
-ART_TARGET_CFLAGS += $(ART_DEFAULT_GC_TYPE_CFLAGS)
-
-# DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is set in ../build/core/dex_preopt.mk based on
-# the TARGET_CPU_VARIANT
-ifeq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),)
-$(error Required DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is not set)
-endif
-ART_TARGET_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)
-
-# Enable thread-safety for GCC 4.6, and clang, but not for GCC 4.7 or later where this feature was
-# removed. Warn when -Wthread-safety is not used.
-ifneq ($(filter 4.6 4.6.%, $(TARGET_GCC_VERSION)),)
-  ART_TARGET_CFLAGS += -Wthread-safety
-else
-  # FIXME: add -Wthread-safety when the problem is fixed
-  ifeq ($(ART_TARGET_CLANG),true)
-    ART_TARGET_CFLAGS +=
-  else
-    # Warn if -Wthread-safety is not supported and not doing a top-level or 'mma' build.
-    ifneq ($(ONE_SHOT_MAKEFILE),)
-      # Enable target GCC 4.6 with: export TARGET_GCC_VERSION_EXP=4.6
-      $(info Using target GCC $(TARGET_GCC_VERSION) disables thread-safety checks.)
-    endif
-  endif
-endif
-# We compile with GCC 4.6 or clang on the host, both of which support -Wthread-safety.
-ART_HOST_CFLAGS += -Wthread-safety
 
 # To use oprofile_android --callgraph, uncomment this and recompile with "mmm art -B -j16"
 # ART_TARGET_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
 
-# Addition CPU specific CFLAGS.
-ifeq ($(TARGET_ARCH),arm)
-  ifneq ($(filter cortex-a15, $(TARGET_CPU_VARIANT)),)
-    # Fake a ARM feature for LPAE support.
-    ART_TARGET_CFLAGS += -D__ARM_FEATURE_LPAE=1
-  endif
+# Clear locals now they've served their purpose.
+art_cflags :=
+art_debug_cflags :=
+art_non_debug_cflags :=
+art_host_non_debug_cflags :=
+art_target_non_debug_cflags :=
+art_default_gc_type :=
+art_default_gc_type_cflags :=
+
+ART_HOST_LDLIBS :=
+ifneq ($(ART_HOST_CLANG),true)
+  # GCC lacks libc++ assumed atomic operations, grab via libatomic.
+  ART_HOST_LDLIBS += -latomic
 endif
 
-ART_HOST_NON_DEBUG_CFLAGS := $(art_host_non_debug_cflags)
-ART_TARGET_NON_DEBUG_CFLAGS := $(art_target_non_debug_cflags)
-
-# TODO: move -fkeep-inline-functions to art_debug_cflags when target gcc > 4.4 (and -lsupc++)
-ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags) -fkeep-inline-functions
-ART_HOST_DEBUG_LDLIBS := -lsupc++
-
-ifneq ($(HOST_OS),linux)
-  # Some Mac OS pthread header files are broken with -fkeep-inline-functions.
-  ART_HOST_DEBUG_CFLAGS := $(filter-out -fkeep-inline-functions,$(ART_HOST_DEBUG_CFLAGS))
-  # Mac OS doesn't have libsupc++.
-  ART_HOST_DEBUG_LDLIBS := $(filter-out -lsupc++,$(ART_HOST_DEBUG_LDLIBS))
-endif
-
-ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags)
+ART_TARGET_LDFLAGS :=
 
 # $(1): ndebug_or_debug
 define set-target-local-cflags-vars
@@ -324,6 +333,7 @@
   art_target_cflags_ndebug_or_debug :=
 endef
 
+# Support for disabling certain builds.
 ART_BUILD_TARGET := false
 ART_BUILD_HOST := false
 ART_BUILD_NDEBUG := false
@@ -345,12 +355,4 @@
   ART_BUILD_DEBUG := true
 endif
 
-# Clear locally defined variables that aren't necessary in the rest of the build system.
-ART_DEFAULT_GC_TYPE :=
-ART_DEFAULT_GC_TYPE_CFLAGS :=
-art_cflags :=
-art_target_non_debug_cflags :=
-art_host_non_debug_cflags :=
-art_non_debug_cflags :=
-
 endif # ANDROID_COMMON_BUILD_MK
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index 10695b6..281d189 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -47,25 +47,33 @@
 2ND_TARGET_CORE_OAT := $(ART_TARGET_TEST_DIR)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core.oat
 endif
 
+CORE_OAT_SUFFIX := .oat
+
 # Core.oat locations under the out directory.
-HOST_CORE_OAT_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/core.oat
+HOST_CORE_OAT_OUT_BASE := $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/core
 ifneq ($(HOST_PREFER_32_BIT),true)
-2ND_HOST_CORE_OAT_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/core.oat
+2ND_HOST_CORE_OAT_OUT_BASE := $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/core
 endif
-TARGET_CORE_OAT_OUT := $(ART_TARGET_TEST_OUT)/$(DEX2OAT_TARGET_ARCH)/core.oat
+HOST_CORE_OAT_OUTS :=
+TARGET_CORE_OAT_OUT_BASE := $(ART_TARGET_TEST_OUT)/$(DEX2OAT_TARGET_ARCH)/core
 ifdef TARGET_2ND_ARCH
-2ND_TARGET_CORE_OAT_OUT := $(ART_TARGET_TEST_OUT)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core.oat
+2ND_TARGET_CORE_OAT_OUT_BASE := $(ART_TARGET_TEST_OUT)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core
 endif
+TARGET_CORE_OAT_OUTS :=
+
+CORE_IMG_SUFFIX := .art
 
 # Core.art locations under the out directory.
-HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/core.art
+HOST_CORE_IMG_OUT_BASE := $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/core
 ifneq ($(HOST_PREFER_32_BIT),true)
-2ND_HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/core.art
+2ND_HOST_CORE_IMG_OUT_BASE := $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/core
 endif
-TARGET_CORE_IMG_OUT := $(ART_TARGET_TEST_OUT)/$(DEX2OAT_TARGET_ARCH)/core.art
+HOST_CORE_IMG_OUTS :=
+TARGET_CORE_IMG_OUT_BASE := $(ART_TARGET_TEST_OUT)/$(DEX2OAT_TARGET_ARCH)/core
 ifdef TARGET_2ND_ARCH
-2ND_TARGET_CORE_IMG_OUT := $(ART_TARGET_TEST_OUT)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core.art
+2ND_TARGET_CORE_IMG_OUT_BASE := $(ART_TARGET_TEST_OUT)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core
 endif
+TARGET_CORE_IMG_OUTS :=
 
 # Oat location of core.art.
 HOST_CORE_IMG_LOCATION := $(HOST_OUT_JAVA_LIBRARIES)/core.art
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 7e38157..2493565 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,11 +52,23 @@
 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)
 
+# Do we want to test a PIC-compiled core image?
+ART_TEST_PIC_IMAGE ?= $(ART_TEST_FULL)
+
+# Do we want to test PIC-compiled tests ("apps")?
+ART_TEST_PIC_TEST ?= $(ART_TEST_FULL)
+
 # Do you want tracing tests run?
 ART_TEST_TRACE ?= $(ART_TEST_FULL)
 
@@ -69,17 +78,35 @@
 # 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 prebuilding?
+ART_TEST_RUN_TEST_PREBUILD ?= true
+
+# 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 run-tests with libartd.so?
+ART_TEST_RUN_TEST_DEBUG ?= true
+
+# Do you want run-tests with libart.so?
+ART_TEST_RUN_TEST_NDEBUG ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with the host/target's second arch?
+ART_TEST_RUN_TEST_2ND_ARCH ?= true
 
 # Do you want failed tests to have their artifacts cleaned up?
 ART_TEST_RUN_TEST_ALWAYS_CLEAN ?= true
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 02252ab..86f445f 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -54,9 +54,10 @@
   include $(CLEAR_VARS)
   LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
   LOCAL_MODULE_TAGS := optional
-  LOCAL_SRC_FILES := $$(art_source) ../sigchainlib/sigchain.cc
+  LOCAL_SRC_FILES := $$(art_source)
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes)
   LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries)
+  LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
 
   ifeq ($$(art_ndebug_or_debug),ndebug)
     LOCAL_MODULE := $$(art_executable)
@@ -76,6 +77,7 @@
     LOCAL_SHARED_LIBRARIES += libdl
   else # host
     LOCAL_CLANG := $(ART_HOST_CLANG)
+    LOCAL_LDLIBS := $(ART_HOST_LDLIBS)
     LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
     ifeq ($$(art_ndebug_or_debug),debug)
       LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 807c5cc..8753868 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 Main 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
@@ -59,8 +59,12 @@
 ART_GTEST_transaction_test_DEX_DEPS := Transaction
 
 # 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_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_default_no-pic_64) $(TARGET_CORE_IMAGE_default_no-pic_32)
+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_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_dex_method_iterator_test_TARGET_DEPS := $(TARGET_CORE_DEX_FILES)
 
 # The path for which all the source files are relative, not actually the current directory.
 LOCAL_PATH := art
@@ -80,7 +84,6 @@
   runtime/base/stringprintf_test.cc \
   runtime/base/timing_logger_test.cc \
   runtime/base/unix_file/fd_file_test.cc \
-  runtime/base/unix_file/mapped_file_test.cc \
   runtime/base/unix_file/null_file_test.cc \
   runtime/base/unix_file/random_access_file_utils_test.cc \
   runtime/base/unix_file/string_file_test.cc \
@@ -109,6 +112,8 @@
   runtime/indirect_reference_table_test.cc \
   runtime/instruction_set_test.cc \
   runtime/intern_table_test.cc \
+  runtime/interpreter/safe_math_test.cc \
+  runtime/java_vm_ext_test.cc \
   runtime/leb128_test.cc \
   runtime/mem_map_test.cc \
   runtime/mirror/dex_cache_test.cc \
@@ -138,18 +143,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_folding_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 \
@@ -192,7 +203,7 @@
 LOCAL_CFLAGS := $(ART_TARGET_CFLAGS)
 LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test.cc
 LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/compiler
-LOCAL_SHARED_LIBRARIES := libcutils libartd libartd-compiler libdl
+LOCAL_SHARED_LIBRARIES := libartd libartd-compiler libdl
 LOCAL_STATIC_LIBRARIES += libgtest_libc++
 LOCAL_CLANG := $(ART_TARGET_CLANG)
 LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
@@ -208,11 +219,7 @@
 LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test.cc
 LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/compiler
 LOCAL_SHARED_LIBRARIES := libartd libartd-compiler
-LOCAL_STATIC_LIBRARIES := libcutils
-ifneq ($(WITHOUT_HOST_CLANG),true)
-  # GCC host compiled tests fail with this linked, presumably due to destructors that run.
-  LOCAL_STATIC_LIBRARIES += libgtest_libc++_host
-endif
+LOCAL_STATIC_LIBRARIES := libgtest_host
 LOCAL_LDLIBS += -ldl -lpthread
 LOCAL_MULTILIB := both
 LOCAL_CLANG := $(ART_HOST_CLANG)
@@ -232,9 +239,15 @@
 ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_GTEST_RULES :=
 
+ART_GTEST_TARGET_ANDROID_ROOT := '/system'
+ifneq ($(ART_TEST_ANDROID_ROOT),)
+  ART_GTEST_TARGET_ANDROID_ROOT := $(ART_TEST_ANDROID_ROOT)
+endif
+
 # Define a make rule for a target device gtest.
 # $(1): gtest name - the name of the test we're building such as leb128_test.
 # $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
+# $(3): LD_LIBRARY_PATH or undefined - used in case libartd.so is not in /system/lib/
 define define-art-gtest-rule-target
   gtest_rule := test-art-target-gtest-$(1)$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
 
@@ -244,7 +257,8 @@
     $$(ART_GTEST_$(1)_TARGET_DEPS) \
     $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_TARGET_GTEST_$(file)_DEX)) \
     $$(ART_TARGET_NATIVETEST_OUT)/$$(TARGET_$(2)ARCH)/$(1) \
-    $$($(2)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so
+    $$($(2)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so \
+    $$(TARGET_OUT_JAVA_LIBRARIES)/core-libart.jar
 
 .PHONY: $$(gtest_rule)
 $$(gtest_rule): test-art-target-sync
@@ -252,11 +266,12 @@
 	$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID
 	$(hide) adb shell chmod 755 $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1)
 	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  (adb shell "$(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID" \
+	  (adb shell "LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
+	    $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID" \
 	  && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID /tmp/ \
 	      && $$(call ART_TEST_PASSED,$$@)) \
 	  || $$(call ART_TEST_FAILED,$$@))
-	$(hide) rm /tmp/$$@-$$$$PPID
+	$(hide) rm -f /tmp/$$@-$$$$PPID
 
   ART_TEST_TARGET_GTEST$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(gtest_rule)
   ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule)
@@ -327,9 +342,10 @@
     LOCAL_MODULE_TAGS := tests
   endif
   LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION)
-  LOCAL_SRC_FILES := $$(art_gtest_filename) sigchainlib/sigchain.cc
+  LOCAL_SRC_FILES := $$(art_gtest_filename)
   LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes)
   LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest
+  LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
 
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk
@@ -350,12 +366,26 @@
     LOCAL_MODULE_PATH_64 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_64)
     LOCAL_MULTILIB := both
     include $$(BUILD_EXECUTABLE)
+    library_path :=
+    2nd_library_path :=
+    ifneq ($$(ART_TEST_ANDROID_ROOT),)
+      ifdef TARGET_2ND_ARCH
+        2nd_library_path := $$(ART_TEST_ANDROID_ROOT)/lib
+        library_path := $$(ART_TEST_ANDROID_ROOT)/lib64
+      else
+        ifneq ($(filter %64,$(TARGET_ARCH)),)
+          library_path := $$(ART_TEST_ANDROID_ROOT)/lib64
+        else
+          library_path := $$(ART_TEST_ANDROID_ROOT)/lib
+        endif
+      endif
+    endif
 
     ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES :=
     ifdef TARGET_2ND_ARCH
-      $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),2ND_))
+      $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),2ND_,$$(2nd_library_path)))
     endif
-    $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),))
+    $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),,$$(library_path)))
 
     # A rule to run the different architecture versions of the gtest.
 .PHONY: test-art-target-gtest-$$(art_gtest_name)
@@ -367,9 +397,8 @@
   else # host
     LOCAL_CLANG := $$(ART_HOST_CLANG)
     LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) $$(ART_HOST_DEBUG_CFLAGS)
-    LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libz-host
-    LOCAL_STATIC_LIBRARIES += libcutils libvixl
-    LOCAL_LDLIBS += -lpthread -ldl
+    LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libziparchive-host libz-host libvixl
+    LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -lpthread -ldl
     LOCAL_IS_HOST_MODULE := true
     LOCAL_MULTILIB := both
     LOCAL_MODULE_STEM_32 := $$(art_gtest_name)32
@@ -403,6 +432,8 @@
   art_gtest_extra_c_includes :=
   art_gtest_extra_shared_libraries :=
   art_gtest_name :=
+  library_path :=
+  2nd_library_path :=
 endef  # define-art-gtest
 
 ifeq ($(ART_BUILD_TARGET),true)
@@ -441,7 +472,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)
@@ -487,6 +525,7 @@
 ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_GTEST_RULES :=
+ART_GTEST_TARGET_ANDROID_ROOT :=
 ART_GTEST_class_linker_test_DEX_DEPS :=
 ART_GTEST_compiler_driver_test_DEX_DEPS :=
 ART_GTEST_dex_file_test_DEX_DEPS :=
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 10936a4..9fe3807 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -21,74 +21,205 @@
 # The main rules to build the default "boot" image are in
 # build/core/dex_preopt_libart.mk
 
-include art/build/Android.common_path.mk
+include art/build/Android.common_build.mk
 
 # Use dex2oat debug version for better error reporting
-# $(1): 2ND_ or undefined, 2ND_ for 32-bit host builds.
+# $(1): compiler - default, optimizing or interpreter.
+# $(2): pic/no-pic
+# $(3): 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)
+  core_compile_options :=
+  core_image_name :=
+  core_oat_name :=
+  core_infix :=
+  core_pic_infix :=
+  core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
+
+  ifeq ($(1),optimizing)
+    core_compile_options += --compiler-backend=Optimizing
+    # With the optimizing compiler, we want to rerun dex2oat whenever there is
+    # a dex2oat change to catch regressions early.
+    core_dex2oat_dependency := $(DEX2OAT)
+    core_infix := -optimizing
+  endif
+  ifeq ($(1),interpreter)
+    core_compile_options += --compiler-filter=interpret-only
+    core_infix := -interpreter
+  endif
+  ifeq ($(1),default)
+    # Default has no infix, no compile options.
+  endif
+  ifneq ($(filter-out default interpreter optimizing,$(1)),)
+    #Technically this test is not precise, but hopefully good enough.
+    $$(error found $(1) expected default, interpreter or optimizing)
+  endif
+
+  ifeq ($(2),pic)
+    core_compile_options += --compile-pic
+    core_pic_infix := -pic
+  endif
+  ifeq ($(2),no-pic)
+    # No change for non-pic
+  endif
+  ifneq ($(filter-out pic no-pic,$(2)),)
+    # Technically this test is not precise, but hopefully good enough.
+    $$(error found $(2) expected pic or no-pic)
+  endif
+
+  core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_IMG_SUFFIX)
+  core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_OAT_SUFFIX)
+
+  # Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
+  ifeq ($(3),)
+    HOST_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
+  else
+    HOST_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+  endif
+  HOST_CORE_IMG_OUTS += $$(core_image_name)
+  HOST_CORE_OAT_OUTS += $$(core_oat_name)
+
+$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
+$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
+$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
+$$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
 	@echo "host dex2oat: $$@ ($$?)"
 	@mkdir -p $$(dir $$@)
-	$$(hide) $$(DEX2OATD) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
+	$$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
 	  --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(HOST_CORE_DEX_FILES)) \
-	  $$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$($(1)HOST_CORE_OAT_OUT) \
-	  --oat-location=$$($(1)HOST_CORE_OAT) --image=$$($(1)HOST_CORE_IMG_OUT) \
-	  --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(1)ART_HOST_ARCH) \
-	  --instruction-set-features=$$($(1)HOST_INSTRUCTION_SET_FEATURES) \
-	  --host --android-root=$$(HOST_OUT) --include-patch-information
+	  $$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
+	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
+	  --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(3)ART_HOST_ARCH) \
+	  --instruction-set-features=$$($(3)HOST_INSTRUCTION_SET_FEATURES) \
+	  --host --android-root=$$(HOST_OUT) --include-patch-information \
+	  $$(PRIVATE_CORE_COMPILE_OPTIONS)
 
-# This "renaming" eases declaration in art/Android.mk
-HOST_CORE_IMG_OUT$($(1)ART_PHONY_TEST_HOST_SUFFIX) := $($(1)HOST_CORE_IMG_OUT)
+$$(core_oat_name): $$(core_image_name)
 
-$$($(1)HOST_CORE_OAT_OUT): $$($(1)HOST_CORE_IMG_OUT)
+  # Clean up locally used variables.
+  core_dex2oat_dependency :=
+  core_compile_options :=
+  core_image_name :=
+  core_oat_name :=
+  core_infix :=
+  core_pic_infix :=
 endef  # create-core-oat-host-rules
 
-$(eval $(call create-core-oat-host-rules,))
-ifneq ($(HOST_PREFER_32_BIT),true)
-$(eval $(call create-core-oat-host-rules,2ND_))
-endif
+# $(1): compiler - default, optimizing or interpreter.
+define create-core-oat-host-rule-combination
+  $(call create-core-oat-host-rules,$(1),no-pic,)
+  $(call create-core-oat-host-rules,$(1),pic,)
+
+  ifneq ($(HOST_PREFER_32_BIT),true)
+    $(call create-core-oat-host-rules,$(1),no-pic,2ND_)
+    $(call create-core-oat-host-rules,$(1),pic,2ND_)
+  endif
+endef
+
+$(eval $(call create-core-oat-host-rule-combination,default))
+$(eval $(call create-core-oat-host-rule-combination,optimizing))
+$(eval $(call create-core-oat-host-rule-combination,interpreter))
+
 
 define create-core-oat-target-rules
-$$($(1)TARGET_CORE_IMG_OUT): $$($(1)TARGET_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENCY)
+  core_compile_options :=
+  core_image_name :=
+  core_oat_name :=
+  core_infix :=
+  core_pic_infix :=
+  core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
+
+  ifeq ($(1),optimizing)
+    ifeq ($($(3)TARGET_ARCH),arm64)
+      # TODO: Enable image generation on arm64 once the backend
+      # is on par with other architectures.
+      core_compile_options += --compiler-backend=Quick
+    else
+      core_compile_options += --compiler-backend=Optimizing
+      # With the optimizing compiler, we want to rerun dex2oat whenever there is
+      # a dex2oat change to catch regressions early.
+      core_dex2oat_dependency := $(DEX2OAT)
+    endif
+    core_infix := -optimizing
+  endif
+  ifeq ($(1),interpreter)
+    core_compile_options += --compiler-filter=interpret-only
+    core_infix := -interpreter
+  endif
+  ifeq ($(1),default)
+    # Default has no infix, no compile options.
+  endif
+  ifneq ($(filter-out default interpreter optimizing,$(1)),)
+    # Technically this test is not precise, but hopefully good enough.
+    $$(error found $(1) expected default, interpreter or optimizing)
+  endif
+
+  ifeq ($(2),pic)
+    core_compile_options += --compile-pic
+    core_pic_infix := -pic
+  endif
+  ifeq ($(2),no-pic)
+    # No change for non-pic
+  endif
+  ifneq ($(filter-out pic no-pic,$(2)),)
+    #Technically this test is not precise, but hopefully good enough.
+    $$(error found $(2) expected pic or no-pic)
+  endif
+
+  core_image_name := $($(3)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_IMG_SUFFIX)
+  core_oat_name := $($(3)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_OAT_SUFFIX)
+
+  # Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
+  ifeq ($(3),)
+    ifdef TARGET_2ND_ARCH
+      TARGET_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
+    else
+      TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+    endif
+  else
+    TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+  endif
+  TARGET_CORE_IMG_OUTS += $$(core_image_name)
+  TARGET_CORE_OAT_OUTS += $$(core_oat_name)
+
+$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
+$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
+$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
+$$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency)
 	@echo "target dex2oat: $$@ ($$?)"
 	@mkdir -p $$(dir $$@)
-	$$(hide) $$(DEX2OATD) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
+	$$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
 	  --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \
-	  $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$($(1)TARGET_CORE_OAT_OUT) \
-	  --oat-location=$$($(1)TARGET_CORE_OAT) --image=$$($(1)TARGET_CORE_IMG_OUT) \
-	  --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(1)TARGET_ARCH) \
-	  --instruction-set-features=$$($(1)TARGET_INSTRUCTION_SET_FEATURES) \
-	  --android-root=$$(PRODUCT_OUT)/system --include-patch-information
+	  $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
+	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
+	  --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(3)TARGET_ARCH) \
+	  --instruction-set-features=$$($(3)TARGET_INSTRUCTION_SET_FEATURES) \
+	  --android-root=$$(PRODUCT_OUT)/system --include-patch-information \
+	  $$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
 
-# This "renaming" eases declaration in art/Android.mk
-TARGET_CORE_IMG_OUT$($(1)ART_PHONY_TEST_TARGET_SUFFIX) := $($(1)TARGET_CORE_IMG_OUT)
+$$(core_oat_name): $$(core_image_name)
 
-$$($(1)TARGET_CORE_OAT_OUT): $$($(1)TARGET_CORE_IMG_OUT)
+  # Clean up locally used variables.
+  core_dex2oat_dependency :=
+  core_compile_options :=
+  core_image_name :=
+  core_oat_name :=
+  core_infix :=
+  core_pic_infix :=
 endef  # create-core-oat-target-rules
 
-ifdef TARGET_2ND_ARCH
-$(eval $(call create-core-oat-target-rules,2ND_))
-endif
-$(eval $(call create-core-oat-target-rules,))
+# $(1): compiler - default, optimizing or interpreter.
+define create-core-oat-target-rule-combination
+  $(call create-core-oat-target-rules,$(1),no-pic,)
+  $(call create-core-oat-target-rules,$(1),pic,)
 
+  ifdef TARGET_2ND_ARCH
+    $(call create-core-oat-target-rules,$(1),no-pic,2ND_)
+    $(call create-core-oat-target-rules,$(1),pic,2ND_)
+  endif
+endef
 
-ifeq ($(ART_BUILD_HOST),true)
-include $(CLEAR_VARS)
-LOCAL_MODULE := core.art-host
-LOCAL_MODULE_TAGS := optional
-LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk
-LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk
-LOCAL_ADDITIONAL_DEPENDENCIES += $(HOST_CORE_IMG_OUT)
-include $(BUILD_PHONY_PACKAGE)
-endif # ART_BUILD_HOST
-
-# If we aren't building the host toolchain, skip building the target core.art.
-ifeq ($(ART_BUILD_TARGET),true)
-include $(CLEAR_VARS)
-LOCAL_MODULE := core.art
-LOCAL_MODULE_TAGS := optional
-LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk
-LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk
-LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_CORE_IMG_OUT)
-include $(BUILD_PHONY_PACKAGE)
-endif # ART_BUILD_TARGET
+$(eval $(call create-core-oat-target-rule-combination,default))
+$(eval $(call create-core-oat-target-rule-combination,optimizing))
+$(eval $(call create-core-oat-target-rule-combination,interpreter))
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 69f9387..610f453 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 \
@@ -57,21 +58,22 @@
 	dex/quick/x86/target_x86.cc \
 	dex/quick/x86/utility_x86.cc \
 	dex/dex_to_dex_compiler.cc \
+	dex/bb_optimizations.cc \
+	dex/compiler_ir.cc \
+	dex/frontend.cc \
+	dex/mir_analysis.cc \
 	dex/mir_dataflow.cc \
 	dex/mir_field_info.cc \
+	dex/mir_graph.cc \
 	dex/mir_method_info.cc \
 	dex/mir_optimization.cc \
-	dex/bb_optimizations.cc \
 	dex/post_opt_passes.cc \
 	dex/pass_driver_me_opts.cc \
 	dex/pass_driver_me_post_opt.cc \
-	dex/frontend.cc \
-	dex/mir_graph.cc \
-	dex/mir_analysis.cc \
+	dex/ssa_transformation.cc \
 	dex/verified_method.cc \
 	dex/verification_results.cc \
 	dex/vreg_analysis.cc \
-	dex/ssa_transformation.cc \
 	dex/quick_compiler_callbacks.cc \
 	driver/compiler_driver.cc \
 	driver/dex_compilation_unit.cc \
@@ -82,16 +84,25 @@
 	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_arm64.cc \
 	optimizing/code_generator_x86.cc \
 	optimizing/code_generator_x86_64.cc \
+	optimizing/constant_folding.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/optimization.cc \
 	optimizing/optimizing_compiler.cc \
 	optimizing/parallel_move_resolver.cc \
+	optimizing/prepare_for_register_allocation.cc \
 	optimizing/register_allocator.cc \
 	optimizing/ssa_builder.cc \
 	optimizing/ssa_liveness_analysis.cc \
@@ -107,6 +118,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,16 +127,13 @@
 	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 \
 	file_output_stream.cc \
 	image_writer.cc \
 	oat_writer.cc \
+	output_stream.cc \
 	vector_output_stream.cc
 
 ifeq ($(ART_SEA_IR_MODE),true)
@@ -160,7 +169,17 @@
 endif
 
 LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \
-	dex/compiler_enums.h
+  dex/quick/arm/arm_lir.h \
+  dex/quick/arm64/arm64_lir.h \
+  dex/quick/mips/mips_lir.h \
+  dex/quick/resource_mask.h \
+  dex/compiler_enums.h \
+  dex/global_value_numbering.h \
+  dex/pass_me.h \
+  driver/compiler_driver.h \
+  driver/compiler_options.h \
+  optimizing/locations.h \
+  utils/arm/constants_arm.h
 
 # $(1): target or host
 # $(2): ndebug or debug
@@ -217,6 +236,7 @@
   else # host
     LOCAL_CLANG := $(ART_HOST_CLANG)
     LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+    LOCAL_LDLIBS := $(ART_HOST_LDLIBS)
     ifeq ($$(art_ndebug_or_debug),debug)
       LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
     else
@@ -224,9 +244,6 @@
     endif
   endif
 
-  # TODO: clean up the compilers and remove this.
-  LOCAL_CFLAGS += -Wno-unused-parameter
-
   ifeq ($(ART_USE_PORTABLE_COMPILER),true)
     LOCAL_SHARED_LIBRARIES += libLLVM
     LOCAL_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
@@ -252,15 +269,18 @@
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
 
   ifeq ($$(art_target_or_host),host)
-    LOCAL_LDLIBS += -ldl -lpthread
+    # For compiler driver TLS.
+    LOCAL_LDLIBS += -lpthread
   endif
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+  # Vixl assembly support for ARM64 targets.
+  LOCAL_SHARED_LIBRARIES += libvixl
   ifeq ($$(art_target_or_host),target)
-    LOCAL_SHARED_LIBRARIES += libcutils libvixl
+    # For atrace.
+    LOCAL_SHARED_LIBRARIES += libcutils
     include $(BUILD_SHARED_LIBRARY)
   else # host
-    LOCAL_STATIC_LIBRARIES += libcutils libvixl
     LOCAL_MULTILIB := both
     include $(BUILD_HOST_SHARED_LIBRARY)
   endif
@@ -282,10 +302,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/buffered_output_stream.h b/compiler/buffered_output_stream.h
index 75a3f24..bbc49df 100644
--- a/compiler/buffered_output_stream.h
+++ b/compiler/buffered_output_stream.h
@@ -23,7 +23,7 @@
 
 namespace art {
 
-class BufferedOutputStream : public OutputStream {
+class BufferedOutputStream FINAL : public OutputStream {
  public:
   explicit BufferedOutputStream(OutputStream* out);
 
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 86167ec..bfdb537 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -16,18 +16,12 @@
 
 #include "common_compiler_test.h"
 
-#if defined(__arm__)
-#include <sys/ucontext.h>
-#endif
-#include <fstream>
-
 #include "class_linker.h"
 #include "compiled_method.h"
 #include "dex/quick_compiler_callbacks.h"
 #include "dex/verification_results.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "driver/compiler_driver.h"
-#include "entrypoints/entrypoint_utils.h"
 #include "interpreter/interpreter.h"
 #include "mirror/art_method.h"
 #include "mirror/dex_cache.h"
@@ -38,128 +32,9 @@
 
 namespace art {
 
-// Normally the ClassLinker supplies this.
-extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
-
-#if defined(__arm__)
-// A signal handler called when have an illegal instruction.  We record the fact in
-// a global boolean and then increment the PC in the signal context to return to
-// the next instruction.  We know the instruction is an sdiv (4 bytes long).
-static void baddivideinst(int signo, siginfo *si, void *data) {
-  UNUSED(signo);
-  UNUSED(si);
-  struct ucontext *uc = (struct ucontext *)data;
-  struct sigcontext *sc = &uc->uc_mcontext;
-  sc->arm_r0 = 0;     // set R0 to #0 to signal error
-  sc->arm_pc += 4;    // skip offending instruction
-}
-
-// This is in arch/arm/arm_sdiv.S.  It does the following:
-// mov r1,#1
-// sdiv r0,r1,r1
-// bx lr
-//
-// the result will be the value 1 if sdiv is supported.  If it is not supported
-// a SIGILL signal will be raised and the signal handler (baddivideinst) called.
-// The signal handler sets r0 to #0 and then increments pc beyond the failed instruction.
-// Thus if the instruction is not supported, the result of this function will be #0
-
-extern "C" bool CheckForARMSDIVInstruction();
-
-static InstructionSetFeatures GuessInstructionFeatures() {
-  InstructionSetFeatures f;
-
-  // Uncomment this for processing of /proc/cpuinfo.
-  if (false) {
-    // Look in /proc/cpuinfo for features we need.  Only use this when we can guarantee that
-    // the kernel puts the appropriate feature flags in here.  Sometimes it doesn't.
-    std::ifstream in("/proc/cpuinfo");
-    if (in) {
-      while (!in.eof()) {
-        std::string line;
-        std::getline(in, line);
-        if (!in.eof()) {
-          if (line.find("Features") != std::string::npos) {
-            if (line.find("idivt") != std::string::npos) {
-              f.SetHasDivideInstruction(true);
-            }
-          }
-        }
-        in.close();
-      }
-    } else {
-      LOG(INFO) << "Failed to open /proc/cpuinfo";
-    }
-  }
-
-  // See if have a sdiv instruction.  Register a signal handler and try to execute
-  // an sdiv instruction.  If we get a SIGILL then it's not supported.  We can't use
-  // the /proc/cpuinfo method for this because Krait devices don't always put the idivt
-  // feature in the list.
-  struct sigaction sa, osa;
-  sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
-  sa.sa_sigaction = baddivideinst;
-  sigaction(SIGILL, &sa, &osa);
-
-  if (CheckForARMSDIVInstruction()) {
-    f.SetHasDivideInstruction(true);
-  }
-
-  // Restore the signal handler.
-  sigaction(SIGILL, &osa, nullptr);
-
-  // Other feature guesses in here.
-  return f;
-}
-#endif
-
-// Given a set of instruction features from the build, parse it.  The
-// input 'str' is a comma separated list of feature names.  Parse it and
-// return the InstructionSetFeatures object.
-static InstructionSetFeatures ParseFeatureList(std::string str) {
-  InstructionSetFeatures result;
-  typedef std::vector<std::string> FeatureList;
-  FeatureList features;
-  Split(str, ',', features);
-  for (FeatureList::iterator i = features.begin(); i != features.end(); i++) {
-    std::string feature = Trim(*i);
-    if (feature == "default") {
-      // Nothing to do.
-    } else if (feature == "div") {
-      // Supports divide instruction.
-      result.SetHasDivideInstruction(true);
-    } else if (feature == "nodiv") {
-      // Turn off support for divide instruction.
-      result.SetHasDivideInstruction(false);
-    } else {
-      LOG(FATAL) << "Unknown instruction set feature: '" << feature << "'";
-    }
-  }
-  // Others...
-  return result;
-}
-
 CommonCompilerTest::CommonCompilerTest() {}
 CommonCompilerTest::~CommonCompilerTest() {}
 
-OatFile::OatMethod CommonCompilerTest::CreateOatMethod(const void* code, const uint8_t* gc_map) {
-  CHECK(code != nullptr);
-  const byte* base;
-  uint32_t code_offset, gc_map_offset;
-  if (gc_map == nullptr) {
-    base = reinterpret_cast<const byte*>(code);  // Base of data points at code.
-    base -= kPointerSize;  // Move backward so that code_offset != 0.
-    code_offset = kPointerSize;
-    gc_map_offset = 0;
-  } else {
-    // TODO: 64bit support.
-    base = nullptr;  // Base of data in oat file, ie 0.
-    code_offset = PointerToLowMemUInt32(code);
-    gc_map_offset = PointerToLowMemUInt32(gc_map);
-  }
-  return OatFile::OatMethod(base, code_offset, gc_map_offset);
-}
-
 void CommonCompilerTest::MakeExecutable(mirror::ArtMethod* method) {
   CHECK(method != nullptr);
 
@@ -174,7 +49,8 @@
   if (compiled_method != nullptr) {
     const std::vector<uint8_t>* code = compiled_method->GetQuickCode();
     const void* code_ptr;
-    if (code != nullptr) {
+    bool is_portable = (code == nullptr);
+    if (!is_portable) {
       uint32_t code_size = code->size();
       CHECK_NE(0u, code_size);
       const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
@@ -210,41 +86,12 @@
     const void* method_code = CompiledMethod::CodePointer(code_ptr,
                                                           compiled_method->GetInstructionSet());
     LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code;
-    OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr);
-    oat_method.LinkMethod(method);
-    method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
+    class_linker_->SetEntryPointsToCompiledCode(method, method_code, is_portable);
   } else {
     // 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
-      OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr);
-      oat_method.LinkMethod(method);
-      method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
-    } else {
-      const void* method_code = reinterpret_cast<void*>(art_quick_generic_jni_trampoline);
-
-      OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr);
-      oat_method.LinkMethod(method);
-      method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
-    }
+    class_linker_->SetEntryPointsToInterpreter(method);
   }
-  // Create bridges to transition between different kinds of compiled bridge.
-#if defined(ART_USE_PORTABLE_COMPILER)
-  if (method->GetEntryPointFromPortableCompiledCode() == nullptr) {
-    method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
-  } else {
-    CHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
-    method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
-    method->SetIsPortableCompiled();
-  }
-#else
-  CHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
-#endif
 }
 
 void CommonCompilerTest::MakeExecutable(const void* code_start, size_t code_length) {
@@ -264,7 +111,7 @@
 #else
   // Only warn if not Intel as Intel doesn't have cache flush instructions.
 #if !defined(__i386__) && !defined(__x86_64__)
-  LOG(WARNING) << "UNIMPLEMENTED: cache flush";
+  UNIMPLEMENTED(WARNING) << "cache flush";
 #endif
 #endif
 }
@@ -289,40 +136,28 @@
   {
     ScopedObjectAccess soa(Thread::Current());
 
-    InstructionSet instruction_set = kRuntimeISA;
-
+    const InstructionSet instruction_set = kRuntimeISA;
     // Take the default set of instruction features from the build.
-    InstructionSetFeatures instruction_set_features =
-        ParseFeatureList(Runtime::GetDefaultInstructionSetFeatures());
-
-#if defined(__arm__)
-    InstructionSetFeatures runtime_features = GuessInstructionFeatures();
-
-    // for ARM, do a runtime check to make sure that the features we are passed from
-    // the build match the features we actually determine at runtime.
-    ASSERT_LE(instruction_set_features, runtime_features);
-#endif
+    instruction_set_features_.reset(InstructionSetFeatures::FromCppDefines());
 
     runtime_->SetInstructionSet(instruction_set);
     for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
       Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
       if (!runtime_->HasCalleeSaveMethod(type)) {
-        runtime_->SetCalleeSaveMethod(
-            runtime_->CreateCalleeSaveMethod(type), type);
+        runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(), type);
       }
     }
 
     // TODO: make selectable
-    Compiler::Kind compiler_kind
-    = (kUsePortableCompiler) ? Compiler::kPortable : Compiler::kQuick;
+    Compiler::Kind compiler_kind = kUsePortableCompiler ? Compiler::kPortable : Compiler::kQuick;
     timer_.reset(new CumulativeLogger("Compilation times"));
     compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
                                               verification_results_.get(),
                                               method_inliner_map_.get(),
                                               compiler_kind, instruction_set,
-                                              instruction_set_features,
+                                              instruction_set_features_.get(),
                                               true, new std::set<std::string>,
-                                              2, true, true, timer_.get()));
+                                              2, true, true, timer_.get(), ""));
   }
   // We typically don't generate an image in unit tests, disable this optimization by default.
   compiler_driver_->SetSupportBootImageFixup(false);
@@ -387,9 +222,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);
@@ -406,7 +241,7 @@
   std::string error_msg;
   MemMap::Init();
   image_reservation_.reset(MemMap::MapAnonymous("image reservation",
-                                                reinterpret_cast<byte*>(ART_BASE_ADDRESS),
+                                                reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS),
                                                 (size_t)100 * 1024 * 1024,  // 100MB
                                                 PROT_NONE,
                                                 false /* no need for 4gb flag with fixed mmap*/,
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index df06b71..20b750c 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -81,6 +81,8 @@
   std::unique_ptr<CompilerCallbacks> callbacks_;
   std::unique_ptr<CompilerDriver> compiler_driver_;
   std::unique_ptr<CumulativeLogger> timer_;
+  std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
+
 
  private:
   std::unique_ptr<MemMap> image_reservation_;
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..0361cd1 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 (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..b9fcf5b 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -15,25 +15,34 @@
  */
 
 #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 {
 
 #ifdef ART_SEA_IR_MODE
-extern "C" art::CompiledMethod* SeaIrCompileMethod(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);
+constexpr bool kCanUseSeaIR = true;
+#else
+constexpr bool kCanUseSeaIR = false;
+#endif
+
+extern "C" art::CompiledMethod* SeaIrCompileMethod(const art::DexFile::CodeItem* code_item ATTRIBUTE_UNUSED,
+                                                   uint32_t access_flags ATTRIBUTE_UNUSED,
+                                                   art::InvokeType invoke_type ATTRIBUTE_UNUSED,
+                                                   uint16_t class_def_idx ATTRIBUTE_UNUSED,
+                                                   uint32_t method_idx ATTRIBUTE_UNUSED,
+                                                   jobject class_loader ATTRIBUTE_UNUSED,
+                                                   const art::DexFile& dex_file ATTRIBUTE_UNUSED)
+#ifdef ART_SEA_IR_MODE
+;   // NOLINT(whitespace/semicolon)
+#else
+{
+  UNREACHABLE();
+}
 #endif
 
 
@@ -44,157 +53,62 @@
                                               uint32_t method_idx,
                                               jobject class_loader,
                                               const art::DexFile& dex_file) {
-#ifdef ART_SEA_IR_MODE
-    bool use_sea = (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci"));
-    if (use_sea) {
-      LOG(INFO) << "Using SEA IR to compile..." << std::endl;
-      return SeaIrCompileMethod(code_item,
-                                access_flags,
-                                invoke_type,
-                                class_def_idx,
-                                method_idx,
-                                class_loader,
-                                dex_file);
+  bool use_sea = kCanUseSeaIR &&
+      (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci"));
+  if (use_sea) {
+    LOG(INFO) << "Using SEA IR to compile..." << std::endl;
+    return SeaIrCompileMethod(code_item,
+                              access_flags,
+                              invoke_type,
+                              class_def_idx,
+                              method_idx,
+                              class_loader,
+                              dex_file);
   }
-#endif
   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";
+      UNREACHABLE();
   }
-  return nullptr;
+}
+
+bool Compiler::IsPathologicalCase(const DexFile::CodeItem& code_item,
+                                  uint32_t method_idx,
+                                  const DexFile& dex_file) {
+  /*
+   * Skip compilation for pathologically large methods - either by instruction count or num vregs.
+   * Dalvik uses 16-bit uints for instruction and register counts.  We'll limit to a quarter
+   * of that, which also guarantees we cannot overflow our 16-bit internal Quick SSA name space.
+   */
+  if (code_item.insns_size_in_code_units_ >= UINT16_MAX / 4) {
+    LOG(INFO) << "Method exceeds compiler instruction limit: "
+              << code_item.insns_size_in_code_units_
+              << " in " << PrettyMethod(method_idx, dex_file);
+    return true;
+  }
+  if (code_item.registers_size_ >= UINT16_MAX / 4) {
+    LOG(INFO) << "Method exceeds compiler virtual register limit: "
+              << code_item.registers_size_ << " in " << PrettyMethod(method_idx, dex_file);
+    return true;
+  }
+  return false;
 }
 
 }  // namespace art
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 4caebf3..c2c15ff 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,
@@ -106,9 +115,20 @@
    */
   virtual std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver)
       const {
+    UNUSED(driver);
     return nullptr;
   }
 
+  virtual CompilerTls* CreateNewCompilerTls() {
+    return nullptr;
+  }
+
+  // Returns whether the method to compile is such a pathological case that
+  // it's not worth compiling.
+  static bool IsPathologicalCase(const DexFile::CodeItem& code_item,
+                                 uint32_t method_idx,
+                                 const DexFile& dex_file);
+
  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..9cad933 100644
--- a/compiler/dex/backend.h
+++ b/compiler/dex/backend.h
@@ -38,14 +38,18 @@
 
     /*
      * 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) {
+      UNUSED(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..764a4cf 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,77 +127,99 @@
 
   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;
 };
 
 /**
- * @class NullCheckEliminationAndTypeInference
- * @brief Null check elimination and type inference.
+ * @class NullCheckElimination
+ * @brief Null check elimination pass.
  */
-class NullCheckEliminationAndTypeInference : public PassME {
+class NullCheckElimination : public PassME {
  public:
-  NullCheckEliminationAndTypeInference()
-    : PassME("NCE_TypeInference", kRepeatingTopologicalSortTraversal, "4_post_nce_cfg") {
+  NullCheckElimination()
+    : PassME("NCE", kRepeatingPreOrderDFSTraversal, "3_post_nce_cfg") {
   }
 
-  void Start(PassDataHolder* data) const {
+  bool Gate(const 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<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->EliminateNullChecksGate();
   }
 
-  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->EliminateNullChecks(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->EliminateNullChecksEnd();
+  }
+};
+
+/**
+ * @class TypeInference
+ * @brief Type inference pass.
+ */
+class TypeInference : public PassME {
+ public:
+  TypeInference()
+    : PassME("TypeInference", kRepeatingPreOrderDFSTraversal, "4_post_type_cfg") {
+  }
+
+  bool Worker(PassDataHolder* data) const {
+    DCHECK(data != 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 c_unit->mir_graph->InferTypes(bb);
   }
 };
 
 class ClassInitCheckElimination : public PassME {
  public:
   ClassInitCheckElimination()
-    : PassME("ClInitCheckElimination", kLoopRepeatingTopologicalSortTraversal) {
+    : PassME("ClInitCheckElimination", kRepeatingPreOrderDFSTraversal) {
   }
 
   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 +235,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 +269,13 @@
 
   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->mir_graph->HasTryCatchBlocks() ||
+        ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0);
   }
 
-  bool Worker(const PassDataHolder* data) const;
+  bool Worker(PassDataHolder* data) const;
 };
 
 /**
@@ -265,9 +289,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..5d877fd 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -28,6 +28,7 @@
   kRefReg,
   kAnyReg,
 };
+std::ostream& operator<<(std::ostream& os, const RegisterClass& rhs);
 
 enum BitsUsed {
   kSize32Bits,
@@ -37,6 +38,7 @@
   kSize512Bits,
   kSize1024Bits,
 };
+std::ostream& operator<<(std::ostream& os, const BitsUsed& rhs);
 
 enum SpecialTargetRegister {
   kSelf,            // Thread pointer.
@@ -60,6 +62,14 @@
   kFArg5,
   kFArg6,
   kFArg7,
+  kFArg8,
+  kFArg9,
+  kFArg10,
+  kFArg11,
+  kFArg12,
+  kFArg13,
+  kFArg14,
+  kFArg15,
   kRet0,
   kRet1,
   kInvokeTgt,
@@ -67,6 +77,7 @@
   kHiddenFpArg,
   kCount
 };
+std::ostream& operator<<(std::ostream& os, const SpecialTargetRegister& code);
 
 enum RegLocationType {
   kLocDalvikFrame = 0,  // Normal Dalvik register
@@ -74,6 +85,7 @@
   kLocCompilerTemp,
   kLocInvalid
 };
+std::ostream& operator<<(std::ostream& os, const RegLocationType& rhs);
 
 enum BBType {
   kNullBlock,
@@ -83,6 +95,7 @@
   kExceptionHandling,
   kDead,
 };
+std::ostream& operator<<(std::ostream& os, const BBType& code);
 
 // Shared pseudo opcodes - must be < 0.
 enum LIRPseudoOpcode {
@@ -103,18 +116,57 @@
   kPseudoEHBlockLabel = -2,
   kPseudoNormalBlockLabel = -1,
 };
+std::ostream& operator<<(std::ostream& os, const LIRPseudoOpcode& rhs);
 
 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 +270,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,
 };
 
@@ -236,13 +319,17 @@
   kMIRNullCheckOnly,
   kMIRIgnoreRangeCheck,
   kMIRRangeCheckOnly,
-  kMIRIgnoreClInitCheck,
+  kMIRClassIsInitialized,
+  kMIRClassIsInDexCache,
+  kMirIgnoreDivZeroCheck,
   kMIRInlined,                        // Invoke is inlined (ie dead).
   kMIRInlinedPred,                    // Invoke is inlined via prediction.
   kMIRCallee,                         // Instruction is inlined from callee.
   kMIRIgnoreSuspendCheck,
   kMIRDup,
-  kMIRMark,                           // Temporary node mark.
+  kMIRMark,                           // Temporary node mark can be used by
+                                      // opt passes for their private needs.
+  kMIRStoreNonTemporal,
   kMIRLastMIRFlag,
 };
 
@@ -253,11 +340,13 @@
   kPackedSwitch,
   kSparseSwitch,
 };
+std::ostream& operator<<(std::ostream& os, const BlockListType& rhs);
 
 enum AssemblerStatus {
   kSuccess,
   kRetryAll,
 };
+std::ostream& operator<<(std::ostream& os, const AssemblerStatus& rhs);
 
 enum OpSize {
   kWord,            // Natural word size of target (32/64).
@@ -271,7 +360,6 @@
   kUnsignedByte,
   kSignedByte,
 };
-
 std::ostream& operator<<(std::ostream& os, const OpSize& kind);
 
 enum OpKind {
@@ -313,6 +401,7 @@
   kOpBx,
   kOpInvalid,
 };
+std::ostream& operator<<(std::ostream& os, const OpKind& rhs);
 
 enum MoveType {
   kMov8GP,      // Move 8-bit general purpose register.
@@ -329,8 +418,7 @@
   kMovLo128FP,  // Move low 64-bits of 128-bit FP register.
   kMovHi128FP,  // Move high 64-bits of 128-bit FP register.
 };
-
-std::ostream& operator<<(std::ostream& os, const OpKind& kind);
+std::ostream& operator<<(std::ostream& os, const MoveType& kind);
 
 enum ConditionCode {
   kCondEq,  // equal
@@ -352,7 +440,6 @@
   kCondAl,  // always
   kCondNv,  // never
 };
-
 std::ostream& operator<<(std::ostream& os, const ConditionCode& kind);
 
 // Target specific condition encodings
@@ -374,7 +461,6 @@
   kArmCondAl = 0xe,  // 1110
   kArmCondNv = 0xf,  // 1111
 };
-
 std::ostream& operator<<(std::ostream& os, const ArmConditionCode& kind);
 
 enum X86ConditionCode {
@@ -422,7 +508,6 @@
   kX86CondNle = 0xf,    // not-less-than
   kX86CondG   = kX86CondNle,  // greater
 };
-
 std::ostream& operator<<(std::ostream& os, const X86ConditionCode& kind);
 
 enum DividePattern {
@@ -431,7 +516,6 @@
   Divide5,
   Divide7,
 };
-
 std::ostream& operator<<(std::ostream& os, const DividePattern& pattern);
 
 /**
@@ -447,14 +531,16 @@
  * -# 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);
 
 enum OpFeatureFlags {
@@ -511,6 +597,7 @@
   kDefHi,
   kDefLo
 };
+std::ostream& operator<<(std::ostream& os, const OpFeatureFlags& rhs);
 
 enum SelectInstructionKind {
   kSelectNone,
@@ -518,7 +605,6 @@
   kSelectMove,
   kSelectGoto
 };
-
 std::ostream& operator<<(std::ostream& os, const SelectInstructionKind& kind);
 
 // LIR fixup kinds for Arm
@@ -528,6 +614,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
@@ -539,14 +626,12 @@
   kFixupMovImmHST,   // kThumb2MovImm16HST.
   kFixupAlign4,      // Align to 4-byte boundary.
 };
-
 std::ostream& operator<<(std::ostream& os, const FixupKind& kind);
 
 enum VolatileKind {
   kNotVolatile,      // Load/Store is not volatile
   kVolatile          // Load/Store is volatile
 };
-
 std::ostream& operator<<(std::ostream& os, const VolatileKind& kind);
 
 enum WideKind {
@@ -554,7 +639,6 @@
   kWide,         // Wide view
   kRef           // Ref width
 };
-
 std::ostream& operator<<(std::ostream& os, const WideKind& kind);
 
 }  // namespace art
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..a2b3fe4
--- /dev/null
+++ b/compiler/dex/compiler_ir.cc
@@ -0,0 +1,78 @@
+/*
+ * 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 "base/dumpable.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..34585c1 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.
@@ -66,15 +68,9 @@
   InstructionSet instruction_set;
   bool target64;
 
-  InstructionSetFeatures GetInstructionSetFeatures() {
+  const 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..6e25db6 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,52 @@
   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* TopologicalSortIterator::Next(bool had_change) {
+  // Update changed: if had_changed is true, we remember it for the whole iteration.
+  changed_ |= had_change;
+
+  while (loop_head_stack_->size() != 0u &&
+      (*loop_ends_)[loop_head_stack_->back().first] == idx_) {
+    loop_head_stack_->pop_back();
+  }
+
+  if (idx_ == end_idx_) {
+    return nullptr;
+  }
+
+  // Get next block and return it.
+  BasicBlockId idx = idx_;
+  idx_ += 1;
+  BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx]);
+  DCHECK(bb != nullptr);
+  if ((*loop_ends_)[idx] != 0u) {
+    loop_head_stack_->push_back(std::make_pair(idx, false));  // Not recalculating.
+  }
+  return bb;
+}
+
 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 +156,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 +179,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..9f17a3e 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,12 @@
        * @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()),
+            loop_ends_(&mir_graph->GetTopologicalSortOrderLoopEnds()),
+            loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) {
         // Extra setup for TopologicalSortIterator.
         idx_ = start_idx_;
-        block_id_list_ = mir_graph->GetTopologicalSortOrder();
+        block_id_list_ = &mir_graph->GetTopologicalSortOrder();
       }
 
       /**
@@ -348,44 +346,11 @@
        * @param had_change did the user of the iteration change the previous BasicBlock.
        * @return the next BasicBlock following the iteration order, 0 if finished.
        */
-      virtual BasicBlock* Next(bool had_change = false) {
-        // Update changed: if had_changed is true, we remember it for the whole iteration.
-        changed_ |= had_change;
+      virtual BasicBlock* Next(bool had_change = false) OVERRIDE;
 
-        return ForwardSingleNext();
-      }
-  };
-
-  /**
-   * @class RepeatingTopologicalSortIterator
-   * @brief Used to perform a Topological Sort Iteration of a MIRGraph.
-   * @details If there is a change during an iteration, the iteration starts over at the end of the
-   *          iteration.
-   */
-  class RepeatingTopologicalSortIterator : public DataflowIterator {
-    public:
-     /**
-      * @brief The constructor, using all of the reachable blocks of the MIRGraph.
-      * @param mir_graph The MIRGraph considered.
-      */
-     explicit RepeatingTopologicalSortIterator(MIRGraph* mir_graph)
-         : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()) {
-       // Extra setup for RepeatingTopologicalSortIterator.
-       idx_ = start_idx_;
-       block_id_list_ = mir_graph->GetTopologicalSortOrder();
-     }
-
-     /**
-      * @brief Get the next BasicBlock depending on iteration order.
-      * @param had_change did the user of the iteration change the previous BasicBlock.
-      * @return the next BasicBlock following the iteration order, 0 if finished.
-      */
-     virtual BasicBlock* Next(bool had_change = false) {
-       // Update changed: if had_changed is true, we remember it for the whole iteration.
-       changed_ |= had_change;
-
-       return ForwardRepeatNext();
-     }
+    private:
+     const ArenaVector<BasicBlockId>* const loop_ends_;
+     ArenaVector<std::pair<uint16_t, bool>>* const loop_head_stack_;
   };
 
   /**
@@ -408,19 +373,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 +396,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..205a521 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;
@@ -272,10 +282,11 @@
 }  // namespace art
 
 extern "C" void ArtCompileDEX(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,
-                  art::DexToDexCompilationLevel dex_to_dex_compilation_level) {
+                              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,
+                              art::DexToDexCompilationLevel dex_to_dex_compilation_level) {
+  UNUSED(invoke_type);
   if (dex_to_dex_compilation_level != art::kDontDexToDexCompile) {
     art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(),
                                  dex_file, code_item, class_def_idx, method_idx, access_flags,
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 45fc19e..3f6231c 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
+#include "frontend.h"
+
 #include <cstdint>
 
+#include "backend.h"
+#include "base/dumpable.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 +34,15 @@
 
 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 << kLocalValueNumbering) |
   // (1 << kPromoteRegs) |
   // (1 << kTrackLiveTemps) |
   // (1 << kSafeOptimizations) |
@@ -81,569 +76,20 @@
   // (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,
                                      jobject class_loader, const DexFile& dex_file,
                                      void* llvm_compilation_unit) {
   VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
-  /*
-   * Skip compilation for pathologically large methods - either by instruction count or num vregs.
-   * Dalvik uses 16-bit uints for instruction and register counts.  We'll limit to a quarter
-   * of that, which also guarantees we cannot overflow our 16-bit internal SSA name space.
-   */
-  if (code_item->insns_size_in_code_units_ >= UINT16_MAX / 4) {
-    LOG(INFO) << "Method exceeds compiler instruction limit: "
-              << code_item->insns_size_in_code_units_
-              << " in " << PrettyMethod(method_idx, dex_file);
-    return NULL;
-  }
-  if (code_item->registers_size_ >= UINT16_MAX / 4) {
-    LOG(INFO) << "Method exceeds compiler virtual register limit: "
-              << code_item->registers_size_ << " in " << PrettyMethod(method_idx, dex_file);
-    return NULL;
-  }
-
-  if (!driver.GetCompilerOptions().IsCompilationEnabled()) {
+  if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
     return nullptr;
   }
 
+  DCHECK(driver.GetCompilerOptions().IsCompilationEnabled());
+
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   CompilationUnit cu(driver.GetArenaPool());
 
@@ -662,8 +108,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();
@@ -676,15 +120,8 @@
         (cu.enable_debug & (1 << kDebugVerbose));
   }
 
-  if (gVerboseMethods.size() != 0) {
-    cu.verbose = false;
-    for (size_t i = 0; i < gVerboseMethods.size(); ++i) {
-      if (PrettyMethod(method_idx, dex_file).find(gVerboseMethods[i])
-          != std::string::npos) {
-        cu.verbose = true;
-        break;
-      }
-    }
+  if (driver.GetCompilerOptions().HasVerboseMethods()) {
+    cu.verbose = driver.GetCompilerOptions().IsVerboseMethod(PrettyMethod(method_idx, dex_file));
   }
 
   if (cu.verbose) {
@@ -698,9 +135,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 +154,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 +236,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 +246,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..bed3b97 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
@@ -46,6 +41,7 @@
   kNullCheckElimination,
   kClassInitCheckElimination,
   kGlobalValueNumbering,
+  kLocalValueNumbering,
   kPromoteRegs,
   kTrackLiveTemps,
   kSafeOptimizations,
@@ -81,48 +77,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..d311bc7 100644
--- a/compiler/dex/global_value_numbering.cc
+++ b/compiler/dex/global_value_numbering.cc
@@ -20,14 +20,16 @@
 
 namespace art {
 
-GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator)
+GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator,
+                                           Mode mode)
     : cu_(cu),
       mir_graph_(cu->mir_graph.get()),
       allocator_(allocator),
       bbs_processed_(0u),
       max_bbs_to_process_(kMaxBbsToProcessMultiplyFactor * mir_graph_->GetNumReachableBlocks()),
       last_value_(0u),
-      modifications_allowed_(false),
+      modifications_allowed_(true),
+      mode_(mode),
       global_value_map_(std::less<uint64_t>(), allocator->Adapter()),
       field_index_map_(FieldReferenceComparator(), allocator->Adapter()),
       field_index_reverse_map_(allocator->Adapter()),
@@ -48,29 +50,28 @@
   if (UNLIKELY(!Good())) {
     return nullptr;
   }
-  if (UNLIKELY(bb->data_flow_info == nullptr)) {
-    return nullptr;
-  }
-  if (UNLIKELY(bb->block_type == kExitBlock)) {
+  if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) {
     DCHECK(bb->first_mir_insn == nullptr);
     return nullptr;
   }
-  if (UNLIKELY(bbs_processed_ == max_bbs_to_process_)) {
+  if (mode_ == kModeGvn && UNLIKELY(bbs_processed_ == max_bbs_to_process_)) {
+    // If we're still trying to converge, stop now. Otherwise, proceed to apply optimizations.
     last_value_ = kNoValue;  // Make bad.
     return nullptr;
   }
+  if (mode_ == kModeGvnPostProcessing &&
+    mir_graph_->GetTopologicalSortOrderLoopHeadStack()->empty()) {
+    // Modifications outside loops are performed during the main phase.
+    return nullptr;
+  }
   if (allocator == nullptr) {
     allocator = allocator_;
   }
   DCHECK(work_lvn_.get() == nullptr);
   work_lvn_.reset(new (allocator) LocalValueNumbering(this, bb->id, allocator));
   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;
-      uint16_t value_name = work_lvn_->GetSRegValueName(this_reg);
-      work_lvn_->SetValueNameNullChecked(value_name);
-    }
+    work_lvn_->PrepareEntryBlock();
+    DCHECK(bb->first_mir_insn == nullptr);  // modifications_allowed_ is irrelevant.
   } else {
     // To avoid repeated allocation on the ArenaStack, reuse a single vector kept as a member.
     DCHECK(merge_lvns_.empty());
@@ -80,27 +81,25 @@
     // topological order, or we're recalculating a loop head and need to merge all incoming
     // LVNs. When we're not at a loop head (including having an empty loop head stack) all
     // predecessors should be preceding blocks and we shall merge all of them anyway.
-    //
-    // If we're running the modification phase of the full GVN, the loop head stack will be
-    // empty and we need to merge all incoming LVNs. If we're running just a simple LVN,
-    // 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 (mode_ == kModeGvn && 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();
+      modifications_allowed_ = false;
+      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];
+    } else {
+      modifications_allowed_ = true;
     }
-    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.
@@ -123,12 +122,6 @@
     CHECK(!merge_lvns_.empty());
     if (merge_lvns_.size() == 1u) {
       work_lvn_->MergeOne(*merge_lvns_[0], merge_type);
-      BasicBlock* pred_bb = mir_graph_->GetBasicBlock(merge_lvns_[0]->Id());
-      if (HasNullCheckLastInsn(pred_bb, bb->id)) {
-        int s_reg = pred_bb->last_mir_insn->ssa_rep->uses[0];
-        uint16_t value_name = merge_lvns_[0]->GetSRegValueName(s_reg);
-        work_lvn_->SetValueNameNullChecked(value_name);
-      }
     } else {
       work_lvn_->Merge(merge_type);
     }
diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h
index c06ff6f..72d1112 100644
--- a/compiler/dex/global_value_numbering.h
+++ b/compiler/dex/global_value_numbering.h
@@ -19,16 +19,27 @@
 
 #include "base/macros.h"
 #include "compiler_internals.h"
-#include "utils/scoped_arena_containers.h"
+#include "utils/arena_object.h"
 
 namespace art {
 
 class LocalValueNumbering;
 class MirFieldInfo;
 
-class GlobalValueNumbering {
+class GlobalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> {
  public:
-  GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator);
+  enum Mode {
+    kModeGvn,
+    kModeGvnPostProcessing,
+    kModeLvn
+  };
+
+  static bool Skip(CompilationUnit* cu) {
+    return (cu->disable_opt & (1u << kGlobalValueNumbering)) != 0u ||
+        cu->mir_graph->GetMaxNestedLoops() > kMaxAllowedNestedLoops;
+  }
+
+  GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator, Mode mode);
   ~GlobalValueNumbering();
 
   // Prepare LVN for the basic block.
@@ -44,34 +55,17 @@
   }
 
   // Allow modifications.
-  void AllowModifications() {
-    DCHECK(Good());
-    modifications_allowed_ = true;
-  }
+  void StartPostProcessing();
 
   bool CanModify() const {
-    // TODO: DCHECK(Good()), see AllowModifications() and NewValueName().
     return modifications_allowed_ && Good();
   }
 
-  // GlobalValueNumbering should be allocated on the ArenaStack (or the native stack).
-  static void* operator new(size_t size, ScopedArenaAllocator* allocator) {
-    return allocator->Alloc(sizeof(GlobalValueNumbering), kArenaAllocMisc);
-  }
-
-  // Allow delete-expression to destroy a GlobalValueNumbering object without deallocation.
-  static void operator delete(void* ptr) { UNUSED(ptr); }
-
  private:
   static constexpr uint16_t kNoValue = 0xffffu;
 
   // Allocate a new value name.
-  uint16_t NewValueName() {
-    // TODO: No new values should be needed once we allow modifications.
-    // DCHECK(!modifications_allowed_);
-    ++last_value_;
-    return last_value_;
-  }
+  uint16_t NewValueName();
 
   // Key is concatenation of opcode, operand1, operand2 and modifier, value is value name.
   typedef ScopedArenaSafeMap<uint64_t, uint16_t> ValueMap;
@@ -79,7 +73,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 +87,20 @@
       global_value_map_.PutBefore(lb, key, res);
     }
     return res;
-  };
+  }
+
+  // Look up a value in the global value map, don't add a new entry if there was none before.
+  uint16_t FindValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) {
+    uint16_t res;
+    uint64_t key = BuildKey(op, operand1, operand2, modifier);
+    ValueMap::iterator lb = global_value_map_.lower_bound(key);
+    if (lb != global_value_map_.end() && lb->first == key) {
+      res = lb->second;
+    } else {
+      res = kNoValue;
+    }
+    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 +112,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 {
@@ -205,16 +212,19 @@
   }
 
   CompilationUnit* const cu_;
-  MIRGraph* mir_graph_;
+  MIRGraph* const mir_graph_;
   ScopedArenaAllocator* const allocator_;
 
+  // The maximum number of nested loops that we accept for GVN.
+  static constexpr size_t kMaxAllowedNestedLoops = 6u;
+
   // The number of BBs that we need to process grows exponentially with the number
   // of nested loops. Don't allow excessive processing for too many nested loops or
   // otherwise expensive methods.
   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.
@@ -225,6 +235,9 @@
   // LVN once for each BasicBlock.
   bool modifications_allowed_;
 
+  // Specifies the mode of operation.
+  Mode mode_;
+
   ValueMap global_value_map_;
   FieldIndexMap field_index_map_;
   ScopedArenaVector<const FieldIndexMap::value_type*> field_index_reverse_map_;
@@ -237,9 +250,23 @@
   ScopedArenaVector<const LocalValueNumbering*> merge_lvns_;  // Not owning.
 
   friend class LocalValueNumbering;
+  friend class GlobalValueNumberingTest;
 
   DISALLOW_COPY_AND_ASSIGN(GlobalValueNumbering);
 };
+std::ostream& operator<<(std::ostream& os, const GlobalValueNumbering::Mode& rhs);
+
+inline  void GlobalValueNumbering::StartPostProcessing() {
+  DCHECK(Good());
+  DCHECK_EQ(mode_, kModeGvn);
+  mode_ = kModeGvnPostProcessing;
+}
+
+inline uint16_t GlobalValueNumbering::NewValueName() {
+  DCHECK_NE(mode_, kModeGvnPostProcessing);
+  ++last_value_;
+  return last_value_;
+}
 
 }  // namespace art
 
diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc
index e8501cd..35d5b99 100644
--- a/compiler/dex/global_value_numbering_test.cc
+++ b/compiler/dex/global_value_numbering_test.cc
@@ -25,6 +25,8 @@
 
 class GlobalValueNumberingTest : public testing::Test {
  protected:
+  static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue;
+
   struct IFieldDef {
     uint16_t field_idx;
     uintptr_t declaring_dex_file;
@@ -125,12 +127,14 @@
     { bb, opcode, 0u, 0u, 1, { reg }, 0, { } }
 #define DEF_MOVE(bb, opcode, reg, src) \
     { bb, opcode, 0u, 0u, 1, { src }, 1, { reg } }
+#define DEF_MOVE_WIDE(bb, opcode, reg, src) \
+    { bb, opcode, 0u, 0u, 2, { src, src + 1 }, 2, { reg, reg + 1 } }
 #define DEF_PHI2(bb, reg, src1, src2) \
     { 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 +144,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,20 +154,21 @@
   }
 
   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);
       // Mark even unresolved fields as initialized.
       field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic |
-          MirSFieldLoweringInfo::kFlagIsInitialized;
+          MirSFieldLoweringInfo::kFlagClassIsInitialized;
+      // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by GVN.
       if (def->declaring_dex_file != 0u) {
         field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
         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 +179,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 +213,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 +232,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 +262,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>
@@ -289,8 +289,8 @@
     cu_.mir_graph->ComputeTopologicalSortOrder();
     cu_.mir_graph->SSATransformationEnd();
     ASSERT_TRUE(gvn_ == nullptr);
-    gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get()));
-    ASSERT_FALSE(gvn_->CanModify());
+    gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(),
+                                                           GlobalValueNumbering::kModeGvn));
     value_names_.resize(mir_count_, 0xffffu);
     IteratorType iterator(cu_.mir_graph.get());
     bool change = false;
@@ -309,8 +309,7 @@
   void PerformGVNCodeModifications() {
     ASSERT_TRUE(gvn_ != nullptr);
     ASSERT_TRUE(gvn_->Good());
-    ASSERT_FALSE(gvn_->CanModify());
-    gvn_->AllowModifications();
+    gvn_->StartPostProcessing();
     TopologicalSortIterator iterator(cu_.mir_graph.get());
     for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) {
       LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb);
@@ -341,12 +340,14 @@
     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);
     }
+    // Set shorty for a void-returning method without arguments.
+    cu_.shorty = "V";
   }
 
   static constexpr size_t kMaxSsaRegs = 16384u;
@@ -362,6 +363,8 @@
   ArenaBitVector* live_in_v_;
 };
 
+constexpr uint16_t GlobalValueNumberingTest::kNoValue;
+
 class GlobalValueNumberingTestDiamond : public GlobalValueNumberingTest {
  public:
   GlobalValueNumberingTestDiamond();
@@ -434,12 +437,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 {
@@ -989,6 +990,92 @@
   EXPECT_EQ(value_names_[18], value_names_[21]);
 }
 
+TEST_F(GlobalValueNumberingTestDiamond, PhiWide) {
+  static const MIRDef mirs[] = {
+      DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000),
+      DEF_CONST_WIDE(4, Instruction::CONST_WIDE, 2u, 2000),
+      DEF_CONST_WIDE(5, Instruction::CONST_WIDE, 4u, 3000),
+      DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 6u, 0u),
+      DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 8u, 2u),
+      DEF_MOVE_WIDE(5, Instruction::MOVE_WIDE, 10u, 0u),
+      DEF_MOVE_WIDE(5, Instruction::MOVE_WIDE, 12u, 4u),
+      DEF_PHI2(6, 14u, 6u, 10u),    // Same as CONST_WIDE 0u (1000).
+      DEF_PHI2(6, 15u, 7u, 11u),    // Same as CONST_WIDE 0u (1000), high word.
+      DEF_PHI2(6, 16u, 6u,  0u),    // Same as CONST_WIDE 0u (1000).
+      DEF_PHI2(6, 17u, 7u,  1u),    // Same as CONST_WIDE 0u (1000), high word.
+      DEF_PHI2(6, 18u, 0u, 10u),    // Same as CONST_WIDE 0u (1000).
+      DEF_PHI2(6, 19u, 1u, 11u),    // Same as CONST_WIDE 0u (1000), high word.
+      DEF_PHI2(6, 20u, 8u, 10u),    // Merge 2u (2000) and 0u (1000).
+      DEF_PHI2(6, 21u, 9u, 11u),    // Merge 2u (2000) and 0u (1000), high word.
+      DEF_PHI2(6, 22u, 2u, 10u),    // Merge 2u (2000) and 0u (1000).
+      DEF_PHI2(6, 23u, 3u, 11u),    // Merge 2u (2000) and 0u (1000), high word.
+      DEF_PHI2(6, 24u, 8u,  0u),    // Merge 2u (2000) and 0u (1000).
+      DEF_PHI2(6, 25u, 9u,  1u),    // Merge 2u (2000) and 0u (1000), high word.
+      DEF_PHI2(6, 26u, 2u,  0u),    // Merge 2u (2000) and 0u (1000).
+      DEF_PHI2(6, 27u, 5u,  1u),    // Merge 2u (2000) and 0u (1000), high word.
+      DEF_PHI2(6, 28u, 6u, 12u),    // Merge 0u (1000) and 4u (3000).
+      DEF_PHI2(6, 29u, 7u, 13u),    // Merge 0u (1000) and 4u (3000), high word.
+      DEF_PHI2(6, 30u, 0u, 12u),    // Merge 0u (1000) and 4u (3000).
+      DEF_PHI2(6, 31u, 1u, 13u),    // Merge 0u (1000) and 4u (3000), high word.
+      DEF_PHI2(6, 32u, 6u,  4u),    // Merge 0u (1000) and 4u (3000).
+      DEF_PHI2(6, 33u, 7u,  5u),    // Merge 0u (1000) and 4u (3000), high word.
+      DEF_PHI2(6, 34u, 0u,  4u),    // Merge 0u (1000) and 4u (3000).
+      DEF_PHI2(6, 35u, 1u,  5u),    // Merge 0u (1000) and 4u (3000), high word.
+      DEF_PHI2(6, 36u, 8u, 12u),    // Merge 2u (2000) and 4u (3000).
+      DEF_PHI2(6, 37u, 9u, 13u),    // Merge 2u (2000) and 4u (3000), high word.
+      DEF_PHI2(6, 38u, 2u, 12u),    // Merge 2u (2000) and 4u (3000).
+      DEF_PHI2(6, 39u, 3u, 13u),    // Merge 2u (2000) and 4u (3000), high word.
+      DEF_PHI2(6, 40u, 8u,  4u),    // Merge 2u (2000) and 4u (3000).
+      DEF_PHI2(6, 41u, 9u,  5u),    // Merge 2u (2000) and 4u (3000), high word.
+      DEF_PHI2(6, 42u, 2u,  4u),    // Merge 2u (2000) and 4u (3000).
+      DEF_PHI2(6, 43u, 3u,  5u),    // Merge 2u (2000) and 4u (3000), high word.
+  };
+
+  PrepareMIRs(mirs);
+  PerformGVN();
+  ASSERT_EQ(arraysize(mirs), value_names_.size());
+  EXPECT_EQ(value_names_[0], value_names_[7]);
+  EXPECT_EQ(value_names_[0], value_names_[9]);
+  EXPECT_EQ(value_names_[0], value_names_[11]);
+  EXPECT_NE(value_names_[13], value_names_[0]);
+  EXPECT_NE(value_names_[13], value_names_[1]);
+  EXPECT_NE(value_names_[13], value_names_[2]);
+  EXPECT_EQ(value_names_[13], value_names_[15]);
+  EXPECT_EQ(value_names_[13], value_names_[17]);
+  EXPECT_EQ(value_names_[13], value_names_[19]);
+  EXPECT_NE(value_names_[21], value_names_[0]);
+  EXPECT_NE(value_names_[21], value_names_[1]);
+  EXPECT_NE(value_names_[21], value_names_[2]);
+  EXPECT_NE(value_names_[21], value_names_[13]);
+  EXPECT_EQ(value_names_[21], value_names_[23]);
+  EXPECT_EQ(value_names_[21], value_names_[25]);
+  EXPECT_EQ(value_names_[21], value_names_[27]);
+  EXPECT_NE(value_names_[29], value_names_[0]);
+  EXPECT_NE(value_names_[29], value_names_[1]);
+  EXPECT_NE(value_names_[29], value_names_[2]);
+  EXPECT_NE(value_names_[29], value_names_[13]);
+  EXPECT_NE(value_names_[29], value_names_[21]);
+  EXPECT_EQ(value_names_[29], value_names_[31]);
+  EXPECT_EQ(value_names_[29], value_names_[33]);
+  EXPECT_EQ(value_names_[29], value_names_[35]);
+  // High words should get kNoValue.
+  EXPECT_EQ(value_names_[8], kNoValue);
+  EXPECT_EQ(value_names_[10], kNoValue);
+  EXPECT_EQ(value_names_[12], kNoValue);
+  EXPECT_EQ(value_names_[14], kNoValue);
+  EXPECT_EQ(value_names_[16], kNoValue);
+  EXPECT_EQ(value_names_[18], kNoValue);
+  EXPECT_EQ(value_names_[20], kNoValue);
+  EXPECT_EQ(value_names_[22], kNoValue);
+  EXPECT_EQ(value_names_[24], kNoValue);
+  EXPECT_EQ(value_names_[26], kNoValue);
+  EXPECT_EQ(value_names_[28], kNoValue);
+  EXPECT_EQ(value_names_[30], kNoValue);
+  EXPECT_EQ(value_names_[32], kNoValue);
+  EXPECT_EQ(value_names_[34], kNoValue);
+  EXPECT_EQ(value_names_[36], kNoValue);
+}
+
 TEST_F(GlobalValueNumberingTestLoop, NonAliasingIFields) {
   static const IFieldDef ifields[] = {
       { 0u, 1u, 0u, false },  // Int.
@@ -2116,12 +2203,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..c1ce2ac 100644
--- a/compiler/dex/local_value_numbering.cc
+++ b/compiler/dex/local_value_numbering.cc
@@ -107,7 +107,8 @@
 
 class LocalValueNumbering::NonAliasingArrayVersions {
  public:
-  static uint16_t StartMemoryVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn,
+  static uint16_t StartMemoryVersion(GlobalValueNumbering* gvn,
+                                     const LocalValueNumbering* lvn ATTRIBUTE_UNUSED,
                                      uint16_t array) {
     return gvn->LookupValue(kNonAliasingArrayStartVersionOp, array, kNoValue, kNoValue);
   }
@@ -129,8 +130,9 @@
         gvn, lvn, &lvn->non_aliasing_array_value_map_, array, index);
   }
 
-  static bool HasNewBaseVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn,
-                                uint16_t array) {
+  static bool HasNewBaseVersion(GlobalValueNumbering* gvn ATTRIBUTE_UNUSED,
+                                const LocalValueNumbering* lvn ATTRIBUTE_UNUSED,
+                                uint16_t array ATTRIBUTE_UNUSED) {
     return false;  // Not affected by global_memory_version_.
   }
 
@@ -164,8 +166,9 @@
     return gvn->LookupValue(kAliasingArrayOp, type, location, memory_version);
   }
 
-  static uint16_t LookupMergeValue(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn,
-                                   uint16_t type, uint16_t location) {
+  static uint16_t LookupMergeValue(GlobalValueNumbering* gvn ATTRIBUTE_UNUSED,
+                                   const LocalValueNumbering* lvn,
+                                   uint16_t type ATTRIBUTE_UNUSED, uint16_t location) {
     // If the location is non-aliasing in lvn, use the non-aliasing value.
     uint16_t array = gvn->GetArrayLocationBase(location);
     if (lvn->IsNonAliasingArray(array, type)) {
@@ -176,8 +179,11 @@
         gvn, lvn, &lvn->aliasing_array_value_map_, type, location);
   }
 
-  static bool HasNewBaseVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn,
-                                uint16_t type) {
+  static bool HasNewBaseVersion(GlobalValueNumbering* gvn ATTRIBUTE_UNUSED,
+                                const LocalValueNumbering* lvn,
+                                uint16_t type ATTRIBUTE_UNUSED) {
+    UNUSED(gvn);
+    UNUSED(type);
     return lvn->global_memory_version_ == lvn->merge_new_memory_version_;
   }
 
@@ -374,6 +380,12 @@
   range_checked_ = other.range_checked_;
   null_checked_ = other.null_checked_;
 
+  const BasicBlock* pred_bb = gvn_->GetBasicBlock(other.Id());
+  if (GlobalValueNumbering::HasNullCheckLastInsn(pred_bb, Id())) {
+    int s_reg = pred_bb->last_mir_insn->ssa_rep->uses[0];
+    null_checked_.insert(other.GetOperandValue(s_reg));
+  }
+
   if (merge_type == kCatchMerge) {
     // Memory is clobbered. Use new memory version and don't merge aliasing locations.
     global_memory_version_ = NewMemoryVersion(&merge_new_memory_version_);
@@ -464,11 +476,8 @@
     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) {
-      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);
-      }
+    if ((mir->dalvikInsn.FlagsOf() & Instruction::kInvoke) != 0) {
+      HandleInvokeArgs(mir, lvn);
     }
   }
 }
@@ -656,13 +665,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->GetOperandValue(s_reg);
+    merge_names_.clear();
+    merge_names_.resize(gvn_->merge_lvns_.size(), value_name);
+    if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
+      null_checked_.insert(value_name);
+    }
   }
 }
 
@@ -754,9 +787,9 @@
   if (same_version) {
     // Find the first non-null values.
     for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) {
-      auto it = (lvn->*map_ptr).find(key);
-      if (it != (lvn->*map_ptr).end()) {
-        cmp_values = &it->second;
+      auto value = (lvn->*map_ptr).find(key);
+      if (value != (lvn->*map_ptr).end()) {
+        cmp_values = &value->second;
         break;
       }
     }
@@ -766,21 +799,21 @@
     // field version and the values' memory_version_before_stores, last_stored_value
     // and store_loc_set are identical.
     for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) {
-      auto it = (lvn->*map_ptr).find(key);
-      if (it == (lvn->*map_ptr).end()) {
+      auto value = (lvn->*map_ptr).find(key);
+      if (value == (lvn->*map_ptr).end()) {
         if (cmp_values->memory_version_before_stores != kNoValue) {
           same_version = false;
           break;
         }
-      } else if (cmp_values->last_stored_value != it->second.last_stored_value ||
-          cmp_values->memory_version_before_stores != it->second.memory_version_before_stores ||
-          cmp_values->store_loc_set != it->second.store_loc_set) {
+      } else if (cmp_values->last_stored_value != value->second.last_stored_value ||
+          cmp_values->memory_version_before_stores != value->second.memory_version_before_stores ||
+          cmp_values->store_loc_set != value->second.store_loc_set) {
         same_version = false;
         break;
-      } else if (it->second.last_load_memory_version != kNoValue) {
+      } else if (value->second.last_load_memory_version != kNoValue) {
         DCHECK(load_memory_version_for_same_version == kNoValue ||
-               load_memory_version_for_same_version == it->second.last_load_memory_version);
-        load_memory_version_for_same_version = it->second.last_load_memory_version;
+               load_memory_version_for_same_version == value->second.last_load_memory_version);
+        load_memory_version_for_same_version = value->second.last_load_memory_version;
       }
     }
   }
@@ -795,12 +828,12 @@
     if (!cmp_values->load_value_map.empty()) {
       my_values->load_value_map = cmp_values->load_value_map;
       for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) {
-        auto it = (lvn->*map_ptr).find(key);
-        if (it == (lvn->*map_ptr).end() || it->second.load_value_map.empty()) {
+        auto value = (lvn->*map_ptr).find(key);
+        if (value == (lvn->*map_ptr).end() || value->second.load_value_map.empty()) {
           my_values->load_value_map.clear();
           break;
         }
-        InPlaceIntersectMaps(&my_values->load_value_map, it->second.load_value_map);
+        InPlaceIntersectMaps(&my_values->load_value_map, value->second.load_value_map);
         if (my_values->load_value_map.empty()) {
           break;
         }
@@ -814,20 +847,20 @@
     // Calculate the locations that have been either read from or written to in each incoming LVN.
     bool first_lvn = true;
     for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) {
-      auto it = (lvn->*map_ptr).find(key);
-      if (it == (lvn->*map_ptr).end()) {
+      auto value = (lvn->*map_ptr).find(key);
+      if (value == (lvn->*map_ptr).end()) {
         my_values->load_value_map.clear();
         break;
       }
       if (first_lvn) {
         first_lvn = false;
         // Copy the first LVN's locations. Values will be overwritten later.
-        my_values->load_value_map = it->second.load_value_map;
-        for (uint16_t location : it->second.store_loc_set) {
+        my_values->load_value_map = value->second.load_value_map;
+        for (uint16_t location : value->second.store_loc_set) {
           my_values->load_value_map.Put(location, 0u);
         }
       } else {
-        IntersectAliasingValueLocations(my_values, &it->second);
+        IntersectAliasingValueLocations(my_values, &value->second);
       }
     }
     // Calculate merged values for the intersection.
@@ -896,8 +929,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.
@@ -930,6 +962,26 @@
                 AliasingArrayVersions>>();
 }
 
+void LocalValueNumbering::PrepareEntryBlock() {
+  uint32_t vreg = gvn_->GetMirGraph()->GetFirstInVR();
+  CompilationUnit* cu = gvn_->GetCompilationUnit();
+  const char* shorty = cu->shorty;
+  ++shorty;  // Skip return value.
+  if ((cu->access_flags & kAccStatic) == 0) {
+    // If non-static method, mark "this" as non-null
+    uint16_t value_name = GetOperandValue(vreg);
+    ++vreg;
+    null_checked_.insert(value_name);
+  }
+  for ( ; *shorty != 0; ++shorty, ++vreg) {
+    if (*shorty == 'J' || *shorty == 'D') {
+      uint16_t value_name = GetOperandValueWide(vreg);
+      SetOperandValueWide(vreg, value_name);
+      ++vreg;
+    }
+  }
+}
+
 uint16_t LocalValueNumbering::MarkNonAliasingNonNull(MIR* mir) {
   uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]);
   DCHECK(null_checked_.find(res) == null_checked_.end());
@@ -1016,12 +1068,30 @@
   }
 }
 
+void LocalValueNumbering::HandleInvokeArgs(const MIR* mir, const LocalValueNumbering* mir_lvn) {
+  const int32_t* uses = mir->ssa_rep->uses;
+  const int32_t* uses_end = uses + mir->ssa_rep->num_uses;
+  while (uses != uses_end) {
+    uint16_t sreg = *uses;
+    ++uses;
+    // Avoid LookupValue() so that we don't store new values in the global value map.
+    auto local_it = mir_lvn->sreg_value_map_.find(sreg);
+    if (local_it != mir_lvn->sreg_value_map_.end()) {
+      non_aliasing_refs_.erase(local_it->second);
+    } else {
+      uint16_t value_name = gvn_->FindValue(kNoValue, sreg, kNoValue, kNoValue);
+      if (value_name != kNoValue) {
+        non_aliasing_refs_.erase(value_name);
+      }
+    }
+  }
+}
+
 uint16_t LocalValueNumbering::HandlePhi(MIR* mir) {
   if (gvn_->merge_lvns_.empty()) {
     // Running LVN without a full GVN?
     return kNoValue;
   }
-  int16_t num_uses = mir->ssa_rep->num_uses;
   int32_t* uses = mir->ssa_rep->uses;
   // Try to find out if this is merging wide regs.
   if (mir->ssa_rep->defs[0] != 0 &&
@@ -1029,18 +1099,20 @@
     // This is the high part of a wide reg. Ignore the Phi.
     return kNoValue;
   }
-  bool wide = false;
-  for (int16_t i = 0; i != num_uses; ++i) {
-    if (sreg_wide_value_map_.count(uses[i]) != 0u) {
-      wide = true;
-      break;
-    }
+  BasicBlockId* incoming = mir->meta.phi_incoming;
+  int16_t pos = 0;
+  // Check if we're merging a wide value based on the first merged LVN.
+  const LocalValueNumbering* first_lvn = gvn_->merge_lvns_[0];
+  DCHECK_LT(pos, mir->ssa_rep->num_uses);
+  while (incoming[pos] != first_lvn->Id()) {
+    ++pos;
+    DCHECK_LT(pos, mir->ssa_rep->num_uses);
   }
+  int first_s_reg = uses[pos];
+  bool wide = (first_lvn->sreg_wide_value_map_.count(first_s_reg) != 0u);
   // Iterate over *merge_lvns_ and skip incoming sregs for BBs without associated LVN.
   uint16_t value_name = kNoValue;
   merge_names_.clear();
-  BasicBlockId* incoming = mir->meta.phi_incoming;
-  int16_t pos = 0;
   bool same_values = true;
   for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) {
     DCHECK_LT(pos, mir->ssa_rep->num_uses);
@@ -1146,8 +1218,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 +1319,17 @@
 
 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.IsClassInitialized() &&
+       (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 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 {
@@ -1281,9 +1357,10 @@
 
 void LocalValueNumbering::HandleSPut(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) {
+  if (!field_info.IsClassInitialized() &&
+      (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 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 +1405,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,16 +1458,14 @@
 
     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:
       HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0]));
       // If we're running GVN and CanModify(), uneliminated null check indicates bytecode error.
-      if ((gvn_->GetCompilationUnit()->disable_opt & (1u << kGlobalValueNumbering)) == 0u &&
-          gvn_->CanModify() && (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) {
+      if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 &&
+          gvn_->work_lvn_ != nullptr && gvn_->CanModify()) {
         LOG(WARNING) << "Bytecode error: MONITOR_EXIT is still null checked at 0x" << std::hex
             << mir->offset << " in " << PrettyMethod(gvn_->cu_->method_idx, *gvn_->cu_->dex_file);
       }
@@ -1424,6 +1499,10 @@
       }
       break;
 
+    case kMirOpNullCheck:
+      HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0]));
+      break;
+
     case Instruction::INVOKE_DIRECT:
     case Instruction::INVOKE_DIRECT_RANGE:
     case Instruction::INVOKE_VIRTUAL:
@@ -1436,17 +1515,12 @@
         uint16_t reg = GetOperandValue(mir->ssa_rep->uses[0]);
         HandleNullCheck(mir, reg);
       }
-      // Intentional fall-through.
+      FALLTHROUGH_INTENDED;
     case Instruction::INVOKE_STATIC:
     case Instruction::INVOKE_STATIC_RANGE:
-      if ((mir->optimization_flags & MIR_INLINED) == 0) {
-        // Make ref args aliasing.
-        for (size_t i = 0u, count = mir->ssa_rep->num_uses; i != count; ++i) {
-          uint16_t reg = GetOperandValue(mir->ssa_rep->uses[i]);
-          non_aliasing_refs_.erase(reg);
-        }
-        HandleInvokeOrClInit(mir);
-      }
+      // Make ref args aliasing.
+      HandleInvokeArgs(mir, this);
+      HandleInvokeOrClInitOrAcquireOp(mir);
       break;
 
     case Instruction::MOVE_RESULT:
@@ -1561,7 +1635,7 @@
         uint16_t reg = GetOperandValue(mir->ssa_rep->uses[0]);
         HandleNullCheck(mir, reg);
       }
-      // Intentional fall-through.
+      FALLTHROUGH_INTENDED;
     case Instruction::NEG_INT:
     case Instruction::NOT_INT:
     case Instruction::NEG_FLOAT:
@@ -1588,7 +1662,6 @@
       }
       break;
 
-
     case Instruction::DOUBLE_TO_LONG:
     case Instruction::LONG_TO_DOUBLE:
     case Instruction::NEG_LONG:
@@ -1760,7 +1833,7 @@
 
     case Instruction::APUT_OBJECT:
       HandlePutObject(mir);
-      // Intentional fall-through.
+      FALLTHROUGH_INTENDED;
     case Instruction::APUT:
     case Instruction::APUT_WIDE:
     case Instruction::APUT_BYTE:
@@ -1782,7 +1855,7 @@
 
     case Instruction::IPUT_OBJECT:
       HandlePutObject(mir);
-      // Intentional fall-through.
+      FALLTHROUGH_INTENDED;
     case Instruction::IPUT:
     case Instruction::IPUT_WIDE:
     case Instruction::IPUT_BOOLEAN:
@@ -1804,7 +1877,7 @@
 
     case Instruction::SPUT_OBJECT:
       HandlePutObject(mir);
-      // Intentional fall-through.
+      FALLTHROUGH_INTENDED;
     case Instruction::SPUT:
     case Instruction::SPUT_WIDE:
     case Instruction::SPUT_BOOLEAN:
diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h
index 855d66d..979fd5a 100644
--- a/compiler/dex/local_value_numbering.h
+++ b/compiler/dex/local_value_numbering.h
@@ -21,8 +21,7 @@
 
 #include "compiler_internals.h"
 #include "global_value_numbering.h"
-#include "utils/scoped_arena_allocator.h"
-#include "utils/scoped_arena_containers.h"
+#include "utils/arena_object.h"
 
 namespace art {
 
@@ -31,7 +30,7 @@
 // Enable/disable tracking values stored in the FILLED_NEW_ARRAY result.
 static constexpr bool kLocalValueNumberingEnableFilledNewArrayTracking = true;
 
-class LocalValueNumbering {
+class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> {
  private:
   static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue;
 
@@ -44,14 +43,6 @@
 
   bool Equals(const LocalValueNumbering& other) const;
 
-  uint16_t GetSRegValueName(uint16_t s_reg) const {
-    return GetOperandValue(s_reg);
-  }
-
-  void SetValueNameNullChecked(uint16_t value_name) {
-    null_checked_.insert(value_name);
-  }
-
   bool IsValueNullChecked(uint16_t value_name) const {
     return null_checked_.find(value_name) != null_checked_.end();
   }
@@ -73,17 +64,10 @@
 
   void MergeOne(const LocalValueNumbering& other, MergeType merge_type);
   void Merge(MergeType merge_type);  // Merge gvn_->merge_lvns_.
+  void PrepareEntryBlock();
 
   uint16_t GetValueNumber(MIR* mir);
 
-  // LocalValueNumbering should be allocated on the ArenaStack (or the native stack).
-  static void* operator new(size_t size, ScopedArenaAllocator* allocator) {
-    return allocator->Alloc(sizeof(LocalValueNumbering), kArenaAllocMisc);
-  }
-
-  // Allow delete-expression to destroy a LocalValueNumbering object without deallocation.
-  static void operator delete(void* ptr) { UNUSED(ptr); }
-
  private:
   // A set of value names.
   typedef GlobalValueNumbering::ValueNameSet ValueNameSet;
@@ -121,20 +105,24 @@
   }
 
   void SetOperandValue(uint16_t s_reg, uint16_t value) {
+    DCHECK_EQ(sreg_wide_value_map_.count(s_reg), 0u);
     SetOperandValueImpl(s_reg, value, &sreg_value_map_);
-  };
+  }
 
   uint16_t GetOperandValue(int s_reg) const {
+    DCHECK_EQ(sreg_wide_value_map_.count(s_reg), 0u);
     return GetOperandValueImpl(s_reg, &sreg_value_map_);
-  };
+  }
 
   void SetOperandValueWide(uint16_t s_reg, uint16_t value) {
+    DCHECK_EQ(sreg_value_map_.count(s_reg), 0u);
     SetOperandValueImpl(s_reg, value, &sreg_wide_value_map_);
-  };
+  }
 
   uint16_t GetOperandValueWide(int s_reg) const {
+    DCHECK_EQ(sreg_value_map_.count(s_reg), 0u);
     return GetOperandValueImpl(s_reg, &sreg_wide_value_map_);
-  };
+  }
 
   struct RangeCheckKey {
     uint16_t array;
@@ -300,6 +288,7 @@
   void HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index);
   void HandlePutObject(MIR* mir);
   void HandleEscapingRef(uint16_t base);
+  void HandleInvokeArgs(const MIR* mir, const LocalValueNumbering* mir_lvn);
   uint16_t HandlePhi(MIR* mir);
   uint16_t HandleAGet(MIR* mir, uint16_t opcode);
   void HandleAPut(MIR* mir, uint16_t opcode);
@@ -308,7 +297,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 +332,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);
@@ -355,7 +344,7 @@
   GlobalValueNumbering* gvn_;
 
   // We're using the block id as a 16-bit operand value for some lookups.
-  COMPILE_ASSERT(sizeof(BasicBlockId) == sizeof(uint16_t), BasicBlockId_must_be_16_bit);
+  static_assert(sizeof(BasicBlockId) == sizeof(uint16_t), "BasicBlockId must be 16 bit");
   BasicBlockId id_;
 
   SregValueMap sreg_value_map_;
diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc
index e4e944e..824c323 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,20 +107,21 @@
   }
 
   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);
       // Mark even unresolved fields as initialized.
       field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic |
-          MirSFieldLoweringInfo::kFlagIsInitialized;
+          MirSFieldLoweringInfo::kFlagClassIsInitialized;
+      // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by LVN.
       if (def->declaring_dex_file != 0u) {
         field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
         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 +141,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,9 +171,9 @@
   }
 
   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_ &=
-        ~MirSFieldLoweringInfo::kFlagIsInitialized;
+    CHECK_LT(sfield_index, cu_.mir_graph->sfield_lowering_infos_.size());
+    cu_.mir_graph->sfield_lowering_infos_[sfield_index].flags_ &=
+        ~MirSFieldLoweringInfo::kFlagClassIsInitialized;
   }
 
   void PerformLVN() {
@@ -195,9 +196,9 @@
         value_names_() {
     cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
     allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack));
-    gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get()));
+    gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(),
+                                                           GlobalValueNumbering::kModeLvn));
     lvn_.reset(new (allocator_.get()) LocalValueNumbering(gvn_.get(), 0u, allocator_.get()));
-    gvn_->AllowModifications();
   }
 
   ArenaPool pool_;
@@ -338,16 +339,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 +367,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 +385,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 +414,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 +426,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..44f69ba 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;
       }
     }
@@ -1019,14 +1112,11 @@
     return true;
   }
 
-  if (!compiler_options.IsCompilationEnabled()) {
-    *skip_message = "Compilation disabled";
-    return true;
-  }
+  DCHECK(compiler_options.IsCompilationEnabled());
 
   // Set up compilation cutoffs based on current filter mode.
-  size_t small_cutoff = 0;
-  size_t default_cutoff = 0;
+  size_t small_cutoff;
+  size_t default_cutoff;
   switch (compiler_filter) {
     case CompilerOptions::kBalanced:
       small_cutoff = compiler_options.GetSmallMethodThreshold();
@@ -1037,11 +1127,13 @@
       default_cutoff = compiler_options.GetSmallMethodThreshold();
       break;
     case CompilerOptions::kSpeed:
+    case CompilerOptions::kTime:
       small_cutoff = compiler_options.GetHugeMethodThreshold();
       default_cutoff = compiler_options.GetHugeMethodThreshold();
       break;
     default:
       LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter;
+      UNREACHABLE();
   }
 
   // If size < cutoff, assume we'll compile - but allow removal.
@@ -1108,7 +1200,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 +1216,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 +1232,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 +1251,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 +1312,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 +1333,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 +1370,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 +1380,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..5b7ac3c 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -118,10 +118,10 @@
   DF_DA | DF_REF_A | DF_NON_NULL_DST,
 
   // 1D MONITOR_ENTER vAA
-  DF_UA | DF_NULL_CHK_0 | DF_REF_A,
+  DF_UA | DF_NULL_CHK_A | DF_REF_A,
 
   // 1E MONITOR_EXIT vAA
-  DF_UA | DF_NULL_CHK_0 | DF_REF_A,
+  DF_UA | DF_NULL_CHK_A | DF_REF_A,
 
   // 1F CHK_CAST vAA, type@BBBB
   DF_UA | DF_REF_A | DF_UMS,
@@ -130,7 +130,7 @@
   DF_DA | DF_UB | DF_CORE_A | DF_REF_B | DF_UMS,
 
   // 21 ARRAY_LENGTH vA, vB
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_CORE_A | DF_REF_B,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_CORE_A | DF_REF_B,
 
   // 22 NEW_INSTANCE vAA, type@BBBB
   DF_DA | DF_NON_NULL_DST | DF_REF_A | DF_UMS,
@@ -235,130 +235,130 @@
   DF_NOP,
 
   // 44 AGET vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 45 AGET_WIDE vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 46 AGET_OBJECT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 47 AGET_BOOLEAN vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 48 AGET_BYTE vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 49 AGET_CHAR vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4A AGET_SHORT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4B APUT vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4C APUT_WIDE vAA, vBB, vCC
-  DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_2 | DF_RANGE_CHK_3 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4D APUT_OBJECT vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4E APUT_BOOLEAN vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 4F APUT_BYTE vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 50 APUT_CHAR vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 51 APUT_SHORT vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
 
   // 52 IGET vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 53 IGET_WIDE vA, vB, field@CCCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 54 IGET_OBJECT vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 55 IGET_BOOLEAN vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 56 IGET_BYTE vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 57 IGET_CHAR vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 58 IGET_SHORT vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 59 IPUT vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5A IPUT_WIDE vA, vB, field@CCCC
-  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5B IPUT_OBJECT vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5C IPUT_BOOLEAN vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5D IPUT_BYTE vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5E IPUT_CHAR vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 5F IPUT_SHORT vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // 60 SGET vAA, field@BBBB
-  DF_DA | DF_SFIELD | DF_UMS,
+  DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 61 SGET_WIDE vAA, field@BBBB
-  DF_DA | DF_A_WIDE | DF_SFIELD | DF_UMS,
+  DF_DA | DF_A_WIDE | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 62 SGET_OBJECT vAA, field@BBBB
-  DF_DA | DF_REF_A | DF_SFIELD | DF_UMS,
+  DF_DA | DF_REF_A | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 63 SGET_BOOLEAN vAA, field@BBBB
-  DF_DA | DF_SFIELD | DF_UMS,
+  DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 64 SGET_BYTE vAA, field@BBBB
-  DF_DA | DF_SFIELD | DF_UMS,
+  DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 65 SGET_CHAR vAA, field@BBBB
-  DF_DA | DF_SFIELD | DF_UMS,
+  DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 66 SGET_SHORT vAA, field@BBBB
-  DF_DA | DF_SFIELD | DF_UMS,
+  DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 67 SPUT vAA, field@BBBB
-  DF_UA | DF_SFIELD | DF_UMS,
+  DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 68 SPUT_WIDE vAA, field@BBBB
-  DF_UA | DF_A_WIDE | DF_SFIELD | DF_UMS,
+  DF_UA | DF_A_WIDE | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 69 SPUT_OBJECT vAA, field@BBBB
-  DF_UA | DF_REF_A | DF_SFIELD | DF_UMS,
+  DF_UA | DF_REF_A | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 6A SPUT_BOOLEAN vAA, field@BBBB
-  DF_UA | DF_SFIELD | DF_UMS,
+  DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 6B SPUT_BYTE vAA, field@BBBB
-  DF_UA | DF_SFIELD | DF_UMS,
+  DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 6C SPUT_CHAR vAA, field@BBBB
-  DF_UA | DF_SFIELD | DF_UMS,
+  DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 6D SPUT_SHORT vAA, field@BBBB
-  DF_UA | DF_SFIELD | DF_UMS,
+  DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA}
   DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
@@ -370,7 +370,7 @@
   DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
 
   // 71 INVOKE_STATIC {vD, vE, vF, vG, vA}
-  DF_FORMAT_35C | DF_UMS,
+  DF_FORMAT_35C | DF_CLINIT | DF_UMS,
 
   // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA}
   DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
@@ -388,7 +388,7 @@
   DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
 
   // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN}
-  DF_FORMAT_3RC | DF_UMS,
+  DF_FORMAT_3RC | DF_CLINIT | DF_UMS,
 
   // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
   DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
@@ -712,31 +712,31 @@
   DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
 
   // E3 IGET_VOLATILE
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // E4 IPUT_VOLATILE
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // E5 SGET_VOLATILE
-  DF_DA | DF_SFIELD | DF_UMS,
+  DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // E6 SPUT_VOLATILE
-  DF_UA | DF_SFIELD | DF_UMS,
+  DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // E7 IGET_OBJECT_VOLATILE
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // E8 IGET_WIDE_VOLATILE
-  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // E9 IPUT_WIDE_VOLATILE
-  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // EA SGET_WIDE_VOLATILE
-  DF_DA | DF_A_WIDE | DF_SFIELD | DF_UMS,
+  DF_DA | DF_A_WIDE | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // EB SPUT_WIDE_VOLATILE
-  DF_UA | DF_A_WIDE | DF_SFIELD | DF_UMS,
+  DF_UA | DF_A_WIDE | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // EC BREAKPOINT
   DF_NOP,
@@ -751,28 +751,28 @@
   DF_FORMAT_3RC,
 
   // F0 INVOKE_OBJECT_INIT_RANGE
-  DF_NOP | DF_NULL_CHK_0,
+  DF_NOP,
 
   // F1 RETURN_VOID_BARRIER
   DF_NOP,
 
   // F2 IGET_QUICK
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F3 IGET_WIDE_QUICK
-  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN,
+  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F4 IGET_OBJECT_QUICK
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN,
+  DF_DA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F5 IPUT_QUICK
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F6 IPUT_WIDE_QUICK
-  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_IFIELD | DF_LVN,
+  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F7 IPUT_OBJECT_QUICK
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
 
   // F8 INVOKE_VIRTUAL_QUICK
   DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
@@ -787,13 +787,13 @@
   DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
 
   // FC IPUT_OBJECT_VOLATILE
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+  DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
 
   // FD SGET_OBJECT_VOLATILE
-  DF_DA | DF_REF_A | DF_SFIELD | DF_UMS,
+  DF_DA | DF_REF_A | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // FE SPUT_OBJECT_VOLATILE
-  DF_UA | DF_REF_A | DF_SFIELD | DF_UMS,
+  DF_UA | DF_REF_A | DF_SFIELD | DF_CLINIT | DF_UMS,
 
   // FF UNUSED_FF
   DF_NOP,
@@ -824,75 +824,84 @@
   DF_NOP,
 
   // 108 MIR_NULL_CHECK
-  0,
+  DF_UA | DF_REF_A | DF_NULL_CHK_A | 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_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
+
+  // 120 MirOpPackedArrayPut
+  DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | 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,9 +1161,9 @@
 
       // 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) {
+      if ((flags & Instruction::kInvoke) != 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();
 }
 
 /*
@@ -1314,7 +1343,7 @@
  * counts explicitly used s_regs.  A later phase will add implicit
  * counts for things such as Method*, null-checked references, etc.
  */
-void MIRGraph::CountUses(struct BasicBlock* bb) {
+void MIRGraph::CountUses(class BasicBlock* bb) {
   if (bb->block_type != kDalvikByteCode) {
     return;
   }
@@ -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;
@@ -1379,7 +1403,7 @@
       GetBlockName(bb, block_name1);
       GetBlockName(pred_bb, block_name2);
       DumpCFG("/sdcard/cfg/", false);
-      LOG(FATAL) << "Successor " << block_name1 << "not found from "
+      LOG(FATAL) << "Successor " << block_name1 << " not found from "
                  << block_name2;
     }
   }
diff --git a/compiler/dex/mir_field_info.cc b/compiler/dex/mir_field_info.cc
index 68247b7..1db3b5b 100644
--- a/compiler/dex/mir_field_info.cc
+++ b/compiler/dex/mir_field_info.cc
@@ -62,7 +62,7 @@
     compiler_driver->GetResolvedFieldDexFileLocation(resolved_field,
         &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_field_idx_);
     bool is_volatile = compiler_driver->IsFieldVolatile(resolved_field);
-    it->field_offset_ = resolved_field->GetOffset();
+    it->field_offset_ = compiler_driver->GetFieldOffset(resolved_field);
     std::pair<bool, bool> fast_path = compiler_driver->IsFastInstanceField(
         dex_cache.Get(), referrer_class.Get(), resolved_field, field_idx);
     it->flags_ = 0u |  // Without kFlagIsStatic.
@@ -94,7 +94,7 @@
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(compiler_driver->GetDexCache(mUnit)));
   Handle<mirror::ClassLoader> class_loader(
       hs.NewHandle(compiler_driver->GetClassLoader(soa, mUnit)));
-  Handle<mirror::Class> referrer_class(hs.NewHandle(
+  Handle<mirror::Class> referrer_class_handle(hs.NewHandle(
       compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit)));
   // Even if the referrer class is unresolved (i.e. we're compiling a method without class
   // definition) we still want to resolve fields and record all available info.
@@ -110,16 +110,27 @@
         &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_field_idx_);
     bool is_volatile = compiler_driver->IsFieldVolatile(resolved_field) ? 1u : 0u;
 
-    bool is_referrers_class, is_initialized;
+    mirror::Class* referrer_class = referrer_class_handle.Get();
     std::pair<bool, bool> fast_path = compiler_driver->IsFastStaticField(
-        dex_cache.Get(), referrer_class.Get(), resolved_field, field_idx, &it->field_offset_,
-        &it->storage_index_, &is_referrers_class, &is_initialized);
-    it->flags_ = kFlagIsStatic |
+        dex_cache.Get(), referrer_class, resolved_field, field_idx, &it->storage_index_);
+    uint16_t flags = kFlagIsStatic |
         (is_volatile ? kFlagIsVolatile : 0u) |
         (fast_path.first ? kFlagFastGet : 0u) |
-        (fast_path.second ? kFlagFastPut : 0u) |
-        (is_referrers_class ? kFlagIsReferrersClass : 0u) |
-        (is_initialized ? kFlagIsInitialized : 0u);
+        (fast_path.second ? kFlagFastPut : 0u);
+    if (fast_path.first) {
+      it->field_offset_ = compiler_driver->GetFieldOffset(resolved_field);
+      bool is_referrers_class =
+          compiler_driver->IsStaticFieldInReferrerClass(referrer_class, resolved_field);
+      bool is_class_initialized =
+          compiler_driver->IsStaticFieldsClassInitialized(referrer_class, resolved_field);
+      bool is_class_in_dex_cache = !is_referrers_class &&  // If referrer's class, we don't care.
+          compiler_driver->CanAssumeTypeIsPresentInDexCache(*dex_cache->GetDexFile(),
+                                                            it->storage_index_);
+      flags |= (is_referrers_class ? kFlagIsReferrersClass : 0u) |
+          (is_class_initialized ? kFlagClassIsInitialized : 0u) |
+          (is_class_in_dex_cache ? kFlagClassIsInDexCache : 0u);
+    }
+    it->flags_ = flags;
   }
 }
 
diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h
index 9745c41..e97f7a0 100644
--- a/compiler/dex/mir_field_info.h
+++ b/compiler/dex/mir_field_info.h
@@ -130,13 +130,14 @@
     kBitFastPut,
     kIFieldLoweringInfoBitEnd
   };
-  COMPILE_ASSERT(kIFieldLoweringInfoBitEnd <= 16, too_many_flags);
+  static_assert(kIFieldLoweringInfoBitEnd <= 16, "Too many flags");
   static constexpr uint16_t kFlagFastGet = 1u << kBitFastGet;
   static constexpr uint16_t kFlagFastPut = 1u << kBitFastPut;
 
   // The member offset of the field, 0u if unresolved.
   MemberOffset field_offset_;
 
+  friend class NullCheckEliminationTest;
   friend class GlobalValueNumberingTest;
   friend class LocalValueNumberingTest;
 };
@@ -172,8 +173,12 @@
     return (flags_ & kFlagIsReferrersClass) != 0u;
   }
 
-  bool IsInitialized() const {
-    return (flags_ & kFlagIsInitialized) != 0u;
+  bool IsClassInitialized() const {
+    return (flags_ & kFlagClassIsInitialized) != 0u;
+  }
+
+  bool IsClassInDexCache() const {
+    return (flags_ & kFlagClassIsInDexCache) != 0u;
   }
 
   MemberOffset FieldOffset() const {
@@ -189,14 +194,16 @@
     kBitFastGet = kFieldInfoBitEnd,
     kBitFastPut,
     kBitIsReferrersClass,
-    kBitIsInitialized,
+    kBitClassIsInitialized,
+    kBitClassIsInDexCache,
     kSFieldLoweringInfoBitEnd
   };
-  COMPILE_ASSERT(kSFieldLoweringInfoBitEnd <= 16, too_many_flags);
+  static_assert(kSFieldLoweringInfoBitEnd <= 16, "Too many flags");
   static constexpr uint16_t kFlagFastGet = 1u << kBitFastGet;
   static constexpr uint16_t kFlagFastPut = 1u << kBitFastPut;
   static constexpr uint16_t kFlagIsReferrersClass = 1u << kBitIsReferrersClass;
-  static constexpr uint16_t kFlagIsInitialized = 1u << kBitIsInitialized;
+  static constexpr uint16_t kFlagClassIsInitialized = 1u << kBitClassIsInitialized;
+  static constexpr uint16_t kFlagClassIsInDexCache = 1u << kBitClassIsInDexCache;
 
   // The member offset of the field, 0u if unresolved.
   MemberOffset field_offset_;
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 6aee563..b87ab66 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,43 +67,48 @@
   "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_orders_up_to_date_(false),
+      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)),
+      max_nested_loops_(0u),
       i_dom_list_(NULL),
-      def_block_matrix_(NULL),
       temp_scoped_alloc_(),
       temp_insn_data_(nullptr),
       temp_bit_vector_size_(0u),
       temp_bit_vector_(nullptr),
+      temp_bit_matrix_(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 +123,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_);
 }
 
@@ -156,7 +180,7 @@
                                  BasicBlock* orig_block, BasicBlock** immed_pred_block_p) {
   DCHECK_GT(code_offset, orig_block->start_offset);
   MIR* insn = orig_block->first_mir_insn;
-  MIR* prev = NULL;
+  MIR* prev = NULL;  // Will be set to instruction before split.
   while (insn) {
     if (insn->offset == code_offset) break;
     prev = insn;
@@ -165,50 +189,58 @@
   if (insn == NULL) {
     LOG(FATAL) << "Break split failed";
   }
-  BasicBlock* bottom_block = NewMemBB(kDalvikByteCode, num_blocks_++);
-  block_list_.Insert(bottom_block);
+  // Now insn is at the instruction where we want to split, namely
+  // insn will be the first instruction of the "bottom" block.
+  // Similarly, prev will be the last instruction of the "top" 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 +264,12 @@
   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);
+  // Scan the "bottom" instructions, remapping them to the
+  // newly created "bottom" block.
   MIR* p = insn;
-  dex_pc_to_block_map_.Put(p->offset, bottom_block->id);
+  p->bb = 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 +282,12 @@
      * 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);
+      BasicBlockId mapped_id = dex_pc_to_block_map_[p->offset];
+      // At first glance the instructions should all be mapped to orig_block.
+      // However, multiple instructions may correspond to the same dex, hence an earlier
+      // instruction may have already moved the mapping for dex to bottom_block.
+      DCHECK((mapped_id == orig_block->id) || (mapped_id == bottom_block->id));
+      dex_pc_to_block_map_[p->offset] = bottom_block->id;
     }
   }
 
@@ -263,35 +302,34 @@
  * (by the caller)
  * Utilizes a map for fast lookup of the typical cases.
  */
-BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool split, bool create,
+BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool create,
                                 BasicBlock** immed_pred_block_p) {
-  if (code_offset >= cu_->code_item->insns_size_in_code_units_) {
-    return NULL;
+  if (code_offset >= current_code_item_->insns_size_in_code_units_) {
+    return nullptr;
   }
 
-  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)) {
+  if ((bb != nullptr) && (bb->start_offset == code_offset)) {
     // Does this containing block start with the desired instruction?
     return bb;
   }
 
   // No direct hit.
   if (!create) {
-    return NULL;
+    return nullptr;
   }
 
-  if (bb != NULL) {
+  if (bb != nullptr) {
     // The target exists somewhere in an existing block.
-    return SplitBlock(code_offset, bb, bb == *immed_pred_block_p ?  immed_pred_block_p : NULL);
+    return SplitBlock(code_offset, bb, bb == *immed_pred_block_p ?  immed_pred_block_p : nullptr);
   }
 
   // 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;
 }
 
@@ -316,14 +354,13 @@
   }
 
   // Iterate over each of the handlers to enqueue the empty Catch blocks.
-  const byte* handlers_ptr = DexFile::GetCatchHandlerData(*current_code_item_, 0);
+  const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*current_code_item_, 0);
   uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
   for (uint32_t idx = 0; idx < handlers_size; idx++) {
     CatchHandlerIterator iterator(handlers_ptr);
     for (; iterator.HasNext(); iterator.Next()) {
       uint32_t address = iterator.GetHandlerAddress();
-      FindBlock(address, false /* split */, true /*create*/,
-                /* immed_pred_block_p */ NULL);
+      FindBlock(address, true /*create*/, /* immed_pred_block_p */ nullptr);
     }
     handlers_ptr = iterator.EndDataPointer();
   }
@@ -339,10 +376,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.
@@ -355,7 +392,7 @@
     switch (check_insn->Opcode()) {
       case Instruction::MOVE_WIDE:
         wide = true;
-        // Intentional fall-through.
+        FALLTHROUGH_INTENDED;
       case Instruction::MOVE_OBJECT:
       case Instruction::MOVE:
         dest = check_insn->VRegA_12x();
@@ -363,7 +400,7 @@
 
       case Instruction::MOVE_WIDE_FROM16:
         wide = true;
-        // Intentional fall-through.
+        FALLTHROUGH_INTENDED;
       case Instruction::MOVE_OBJECT_FROM16:
       case Instruction::MOVE_FROM16:
         dest = check_insn->VRegA_22x();
@@ -371,7 +408,7 @@
 
       case Instruction::MOVE_WIDE_16:
         wide = true;
-        // Intentional fall-through.
+        FALLTHROUGH_INTENDED;
       case Instruction::MOVE_OBJECT_16:
       case Instruction::MOVE_16:
         dest = check_insn->VRegA_32x();
@@ -381,7 +418,7 @@
       case Instruction::GOTO_16:
       case Instruction::GOTO_32:
         check_insn = check_insn->RelativeAt(check_insn->GetTargetOffset());
-        // Intentional fall-through.
+        FALLTHROUGH_INTENDED;
       default:
         return check_insn->Opcode() == Instruction::MONITOR_EXIT &&
             check_insn->VRegA_11x() == monitor_reg;
@@ -428,36 +465,22 @@
       LOG(FATAL) << "Unexpected opcode(" << insn->dalvikInsn.opcode << ") with kBranch set";
   }
   CountBranch(target);
-  BasicBlock* taken_block = FindBlock(target, /* split */ true, /* create */ true,
+  BasicBlock* taken_block = FindBlock(target, /* 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) {
     BasicBlock* fallthrough_block = FindBlock(cur_offset +  width,
-                                             /*
-                                              * If the method is processed
-                                              * in sequential order from the
-                                              * beginning, we don't need to
-                                              * specify split for continue
-                                              * blocks. However, this
-                                              * routine can be called by
-                                              * compileLoop, which starts
-                                              * parsing the method from an
-                                              * arbitrary address in the
-                                              * method body.
-                                              */
-                                             true,
                                              /* create */
                                              true,
                                              /* 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);
+    FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr);
   }
   return cur_block;
 }
@@ -465,6 +488,7 @@
 /* Process instructions with the kSwitch flag */
 BasicBlock* MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
                                        int width, int flags) {
+  UNUSED(flags);
   const uint16_t* switch_data =
       reinterpret_cast<const uint16_t*>(GetCurrentInsns() + cur_offset + insn->dalvikInsn.vB);
   int size;
@@ -513,12 +537,11 @@
   }
   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,
-                                      /* create */ true, /* immed_pred_block_p */ &cur_block);
+    BasicBlock* case_block = FindBlock(cur_offset + target_table[i],  /* create */ true,
+                                       /* immed_pred_block_p */ &cur_block);
     SuccessorBlockInfo* successor_block_info =
         static_cast<SuccessorBlockInfo*>(arena_->Alloc(sizeof(SuccessorBlockInfo),
                                                        kArenaAllocSuccessor));
@@ -526,15 +549,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);
+  BasicBlock* fallthrough_block = FindBlock(cur_offset +  width, /* create */ true,
+                                            /* immed_pred_block_p */ nullptr);
   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;
 }
 
@@ -542,10 +565,9 @@
 BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
                                       int width, int flags, ArenaBitVector* try_block_addr,
                                       const uint16_t* code_ptr, const uint16_t* code_end) {
+  UNUSED(flags);
   bool in_try_block = try_block_addr->IsBitSet(cur_offset);
   bool is_throw = (insn->dalvikInsn.opcode == Instruction::THROW);
-  bool build_all_edges =
-      (cu_->disable_opt & (1 << kSuppressExceptionEdges)) || is_throw || in_try_block;
 
   /* In try block */
   if (in_try_block) {
@@ -558,8 +580,8 @@
     }
 
     for (; iterator.HasNext(); iterator.Next()) {
-      BasicBlock* catch_block = FindBlock(iterator.GetHandlerAddress(), false /* split*/,
-                                         false /* creat */, NULL  /* immed_pred_block_p */);
+      BasicBlock* catch_block = FindBlock(iterator.GetHandlerAddress(), false /* create */,
+                                          nullptr /* immed_pred_block_p */);
       if (insn->dalvikInsn.opcode == Instruction::MONITOR_EXIT &&
           IsBadMonitorExitCatch(insn->offset, catch_block->start_offset)) {
         // Don't allow monitor-exit to catch its own exception, http://b/15745363 .
@@ -567,8 +589,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,25 +598,25 @@
           (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);
   }
+  bool build_all_edges =
+      (cu_->disable_opt & (1 << kSuppressExceptionEdges)) || is_throw || in_try_block;
   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) {
     cur_block->explicit_throw = true;
     if (code_ptr < code_end) {
       // Force creation of new block following THROW via side-effect.
-      FindBlock(cur_offset + width, /* split */ false, /* create */ true,
-                /* immed_pred_block_p */ NULL);
+      FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr);
     }
     if (!in_try_block) {
        // Don't split a THROW that can't rethrow - we're done.
@@ -631,11 +651,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 +682,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 +695,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 +707,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 +716,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 +739,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 +779,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 +789,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.
@@ -790,8 +799,7 @@
          * Create a fallthrough block for real instructions
          * (incl. NOP).
          */
-         FindBlock(current_offset_ + width, /* split */ false, /* create */ true,
-                   /* immed_pred_block_p */ NULL);
+         FindBlock(current_offset_ + width, /* create */ true, /* immed_pred_block_p */ nullptr);
       }
     } else if (flags & Instruction::kThrow) {
       cur_block = ProcessCanThrow(cur_block, insn, current_offset_, width, flags, try_block_addr_,
@@ -814,8 +822,8 @@
       }
     }
     current_offset_ += width;
-    BasicBlock* next_block = FindBlock(current_offset_, /* split */ false, /* create */
-                                      false, /* immed_pred_block_p */ NULL);
+    BasicBlock* next_block = FindBlock(current_offset_, /* create */ false,
+                                       /* immed_pred_block_p */ nullptr);
     if (next_block) {
       /*
        * The next instruction could be the target of a previously parsed
@@ -829,7 +837,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 +902,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 +919,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 %s\\l}%s\\\n", mir->offset,
                       mir->ssa_rep ? GetDalvikDisassembly(mir) :
                       !MIR::DecodedInstruction::IsPseudoMirOp(opcode) ?
                         Instruction::Name(mir->dalvikInsn.opcode) :
@@ -939,8 +927,11 @@
                       (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_CLASS_IS_INITIALIZED) != 0 ? " cl_inited" : " ",
+                      (mir->optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0 ? " cl_in_cache" : " ",
                       mir->next ? " | " : " ");
-            }
         }
         fprintf(file, "  }\"];\n\n");
     } else if (bb->block_type == kExceptionHandling) {
@@ -968,23 +959,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 +978,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 +1152,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 +1190,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 +1391,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 +1521,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.
@@ -1365,7 +1536,8 @@
     return GetSSAName(ssa_reg);
   }
   if (IsConst(reg_location_[ssa_reg])) {
-    if (!singles_only && reg_location_[ssa_reg].wide) {
+    if (!singles_only && reg_location_[ssa_reg].wide &&
+        !reg_location_[ssa_reg].high_word) {
       return StringPrintf("v%d_%d#0x%" PRIx64, SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg),
                           ConstantValueWide(reg_location_[ssa_reg]));
     } else {
@@ -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() {
@@ -1530,8 +1694,39 @@
 
   temp_bit_vector_size_ = 0u;
   temp_bit_vector_ = nullptr;
+  temp_bit_matrix_ = nullptr;  // Def block matrix.
   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 +1795,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 +1818,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 +1861,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 +1905,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 +1934,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 +1948,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 +1961,9 @@
   }
 
   // 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);
+  max_nested_loops_ = max_nested_loops;
 }
 
 bool BasicBlock::IsExceptionBlock() const {
@@ -1792,8 +1980,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 +1997,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 +2030,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 +2064,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 +2118,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:
@@ -2025,7 +2213,7 @@
   }
 }
 
-void BasicBlock::Hide(CompilationUnit* c_unit) {
+void BasicBlock::Hide(MIRGraph* mir_graph) {
   // First lets make it a dalvik bytecode block so it doesn't have any special meaning.
   block_type = kDalvikByteCode;
 
@@ -2040,14 +2228,9 @@
   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,8 +2240,55 @@
   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;
+}
+
+/*
+ * Kill an unreachable block and all blocks that become unreachable by killing this one.
+ */
+void BasicBlock::KillUnreachable(MIRGraph* mir_graph) {
+  DCHECK(predecessors.empty());  // Unreachable.
+
+  // Mark as dead and hidden.
+  block_type = kDead;
+  hidden = true;
+
+  // Detach it from its MIRs so we don't generate code for them. Also detached MIRs
+  // are updated to know that they no longer have a parent.
+  for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) {
+    mir->bb = NullBasicBlockId;
+  }
+  first_mir_insn = nullptr;
+  last_mir_insn = nullptr;
+
+  data_flow_info = nullptr;
+
+  // Erase this bb from all children's predecessors and kill unreachable children.
+  ChildBlockIterator iter(this, mir_graph);
+  for (BasicBlock* succ_bb = iter.Next(); succ_bb != nullptr; succ_bb = iter.Next()) {
+    succ_bb->ErasePredecessor(id);
+    if (succ_bb->predecessors.empty()) {
+      succ_bb->KillUnreachable(mir_graph);
+    }
+  }
+
+  // Remove links to children.
+  fall_through = NullBasicBlockId;
+  taken = NullBasicBlockId;
+  successor_block_list_type = kNotUsed;
+
+  if (kIsDebugBuild) {
+    if (catch_entry) {
+      DCHECK_EQ(mir_graph->catches_.count(start_offset), 1u);
+      mir_graph->catches_.erase(start_offset);
+    }
   }
 }
 
@@ -2119,12 +2349,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 +2360,37 @@
   return found;
 }
 
-void BasicBlock::UpdatePredecessor(BasicBlockId old_parent, BasicBlockId new_parent) {
-  GrowableArray<BasicBlockId>::Iterator iterator(predecessors);
-  bool found = false;
-
-  while (true) {
-    BasicBlockId pred_bb_id = iterator.Next();
-
-    if (pred_bb_id == NullBasicBlockId) {
+void BasicBlock::ErasePredecessor(BasicBlockId old_pred) {
+  auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred);
+  DCHECK(pos != predecessors.end());
+  // It's faster to move the back() to *pos than erase(pos).
+  *pos = predecessors.back();
+  predecessors.pop_back();
+  size_t idx = std::distance(predecessors.begin(), pos);
+  for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) {
+    if (static_cast<int>(mir->dalvikInsn.opcode) != kMirOpPhi) {
       break;
     }
-
-    if (pred_bb_id == old_parent) {
-      size_t idx = iterator.GetIndex() - 1;
-      predecessors->Put(idx, new_parent);
-      found = true;
-      break;
-    }
+    DCHECK_EQ(mir->ssa_rep->num_uses - 1u, predecessors.size());
+    DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred);
+    mir->meta.phi_incoming[idx] = mir->meta.phi_incoming[predecessors.size()];
+    mir->ssa_rep->uses[idx] = mir->ssa_rep->uses[predecessors.size()];
+    mir->ssa_rep->num_uses = predecessors.size();
   }
+}
 
-  // 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);
+  DCHECK(pos != predecessors.end());
+  *pos = new_pred;
+  size_t idx = std::distance(predecessors.begin(), pos);
+  for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) {
+    if (static_cast<int>(mir->dalvikInsn.opcode) != kMirOpPhi) {
+      break;
+    }
+    DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred);
+    mir->meta.phi_incoming[idx] = new_pred;
   }
 }
 
@@ -2164,7 +2398,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 +2408,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..a1d24e2 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,
@@ -83,17 +49,14 @@
   kFormat35c,
   kFormat3rc,
   kFormatExtended,       // Extended format for extended MIRs.
-  kNullCheckSrc0,        // Null check of uses[0].
-  kNullCheckSrc1,        // Null check of uses[1].
-  kNullCheckSrc2,        // Null check of uses[2].
+  kNullCheckA,           // Null check of A.
+  kNullCheckB,           // Null check of B.
   kNullCheckOut0,        // Null check out outgoing arg0.
   kDstNonNull,           // May assume dst is non-null.
   kRetNonNull,           // May assume retval is non-null.
   kNullTransferSrc0,     // Object copy src[0] -> dst.
   kNullTransferSrcN,     // Phi null check state transfer.
-  kRangeCheckSrc1,       // Range check of uses[1].
-  kRangeCheckSrc2,       // Range check of uses[2].
-  kRangeCheckSrc3,       // Range check of uses[3].
+  kRangeCheckC,          // Range check of C.
   kFPA,
   kFPB,
   kFPC,
@@ -106,6 +69,7 @@
   kUsesMethodStar,       // Implicit use of Method*.
   kUsesIField,           // Accesses an instance field (IGET/IPUT).
   kUsesSField,           // Accesses a static field (SGET/SPUT).
+  kCanInitializeClass,   // Can trigger class initialization (SGET/SPUT/INVOKE_STATIC).
   kDoLVN,                // Worth computing local value numbers.
 };
 
@@ -122,17 +86,14 @@
 #define DF_FORMAT_35C           (UINT64_C(1) << kFormat35c)
 #define DF_FORMAT_3RC           (UINT64_C(1) << kFormat3rc)
 #define DF_FORMAT_EXTENDED      (UINT64_C(1) << kFormatExtended)
-#define DF_NULL_CHK_0           (UINT64_C(1) << kNullCheckSrc0)
-#define DF_NULL_CHK_1           (UINT64_C(1) << kNullCheckSrc1)
-#define DF_NULL_CHK_2           (UINT64_C(1) << kNullCheckSrc2)
+#define DF_NULL_CHK_A           (UINT64_C(1) << kNullCheckA)
+#define DF_NULL_CHK_B           (UINT64_C(1) << kNullCheckB)
 #define DF_NULL_CHK_OUT0        (UINT64_C(1) << kNullCheckOut0)
 #define DF_NON_NULL_DST         (UINT64_C(1) << kDstNonNull)
 #define DF_NON_NULL_RET         (UINT64_C(1) << kRetNonNull)
 #define DF_NULL_TRANSFER_0      (UINT64_C(1) << kNullTransferSrc0)
 #define DF_NULL_TRANSFER_N      (UINT64_C(1) << kNullTransferSrcN)
-#define DF_RANGE_CHK_1          (UINT64_C(1) << kRangeCheckSrc1)
-#define DF_RANGE_CHK_2          (UINT64_C(1) << kRangeCheckSrc2)
-#define DF_RANGE_CHK_3          (UINT64_C(1) << kRangeCheckSrc3)
+#define DF_RANGE_CHK_C          (UINT64_C(1) << kRangeCheckC)
 #define DF_FP_A                 (UINT64_C(1) << kFPA)
 #define DF_FP_B                 (UINT64_C(1) << kFPB)
 #define DF_FP_C                 (UINT64_C(1) << kFPC)
@@ -145,20 +106,18 @@
 #define DF_UMS                  (UINT64_C(1) << kUsesMethodStar)
 #define DF_IFIELD               (UINT64_C(1) << kUsesIField)
 #define DF_SFIELD               (UINT64_C(1) << kUsesSField)
+#define DF_CLINIT               (UINT64_C(1) << kCanInitializeClass)
 #define DF_LVN                  (UINT64_C(1) << kDoLVN)
 
 #define DF_HAS_USES             (DF_UA | DF_UB | DF_UC)
 
 #define DF_HAS_DEFS             (DF_DA)
 
-#define DF_HAS_NULL_CHKS        (DF_NULL_CHK_0 | \
-                                 DF_NULL_CHK_1 | \
-                                 DF_NULL_CHK_2 | \
+#define DF_HAS_NULL_CHKS        (DF_NULL_CHK_A | \
+                                 DF_NULL_CHK_B | \
                                  DF_NULL_CHK_OUT0)
 
-#define DF_HAS_RANGE_CHKS       (DF_RANGE_CHK_1 | \
-                                 DF_RANGE_CHK_2 | \
-                                 DF_RANGE_CHK_3)
+#define DF_HAS_RANGE_CHKS       (DF_RANGE_CHK_C)
 
 #define DF_HAS_NR_CHKS          (DF_HAS_NULL_CHKS | \
                                  DF_HAS_RANGE_CHKS)
@@ -166,9 +125,10 @@
 #define DF_A_IS_REG             (DF_UA | DF_DA)
 #define DF_B_IS_REG             (DF_UB)
 #define DF_C_IS_REG             (DF_UC)
-#define DF_IS_GETTER_OR_SETTER  (DF_IS_GETTER | DF_IS_SETTER)
 #define DF_USES_FP              (DF_FP_A | DF_FP_B | DF_FP_C)
 #define DF_NULL_TRANSFER        (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)
+#define DF_IS_INVOKE            (DF_FORMAT_35C | DF_FORMAT_3RC)
+
 enum OatMethodAttributes {
   kIsLeaf,            // Method is leaf.
   kHasLoop,           // Method contains simple loop.
@@ -188,12 +148,16 @@
 #define MIR_NULL_CHECK_ONLY             (1 << kMIRNullCheckOnly)
 #define MIR_IGNORE_RANGE_CHECK          (1 << kMIRIgnoreRangeCheck)
 #define MIR_RANGE_CHECK_ONLY            (1 << kMIRRangeCheckOnly)
-#define MIR_IGNORE_CLINIT_CHECK         (1 << kMIRIgnoreClInitCheck)
+#define MIR_CLASS_IS_INITIALIZED        (1 << kMIRClassIsInitialized)
+#define MIR_CLASS_IS_IN_DEX_CACHE       (1 << kMIRClassIsInDexCache)
+#define MIR_IGNORE_DIV_ZERO_CHECK       (1 << kMirIgnoreDivZeroCheck)
 #define MIR_INLINED                     (1 << kMIRInlined)
 #define MIR_INLINED_PRED                (1 << kMIRInlinedPred)
 #define MIR_CALLEE                      (1 << kMIRCallee)
 #define MIR_IGNORE_SUSPEND_CHECK        (1 << kMIRIgnoreSuspendCheck)
 #define MIR_DUP                         (1 << kMIRDup)
+#define MIR_MARK                        (1 << kMIRMark)
+#define MIR_STORE_NON_TEMPORAL          (1 << kMIRStoreNonTemporal)
 
 #define BLOCK_NAME_LEN 80
 
@@ -215,6 +179,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.
@@ -230,9 +195,7 @@
   ArenaBitVector* use_v;
   ArenaBitVector* def_v;
   ArenaBitVector* live_in_v;
-  ArenaBitVector* phi_v;
   int32_t* vreg_to_ssa_map_exit;
-  ArenaBitVector* ending_check_v;  // For null check and class init check elimination.
 };
 
 /*
@@ -265,7 +228,8 @@
  * The Midlevel Intermediate Representation node, which may be largely considered a
  * wrapper around a Dalvik byte code.
  */
-struct MIR {
+class MIR : public ArenaObject<kArenaAllocMIR> {
+ public:
   /*
    * TODO: remove embedded DecodedInstruction to save space, keeping only opcode.  Recover
    * additional fields on as-needed basis.  Question: how to support MIR Pseudo-ops; probably
@@ -297,37 +261,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 +301,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.
@@ -368,7 +334,7 @@
     uint32_t method_lowering_info;
   } meta;
 
-  explicit MIR():offset(0), optimization_flags(0), m_unit_index(0), bb(NullBasicBlockId),
+  explicit MIR() : offset(0), optimization_flags(0), m_unit_index(0), bb(NullBasicBlockId),
                  next(nullptr), ssa_rep(nullptr) {
     memset(&meta, 0, sizeof(meta));
   }
@@ -379,16 +345,23 @@
 
   MIR* Copy(CompilationUnit *c_unit);
   MIR* Copy(MIRGraph* mir_Graph);
-
-  static void* operator new(size_t size, ArenaAllocator* arena) {
-    return arena->Alloc(sizeof(MIR), kArenaAllocMIR);
-  }
-  static void operator delete(void* p) {}  // Nop.
 };
 
 struct SuccessorBlockInfo;
 
-struct BasicBlock {
+class BasicBlock : public DeletableArenaObject<kArenaAllocBB> {
+ public:
+  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 +385,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,9 +414,14 @@
    * @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);
+  void Hide(MIRGraph* mir_graph);
+
+  /**
+   *  @brief Kill the unreachable block and all blocks that become unreachable by killing this one.
+   */
+  void KillUnreachable(MIRGraph* mir_graph);
 
   /**
    * @brief Is ssa_reg the last SSA definition of that VR in the block?
@@ -456,7 +434,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);
 
@@ -471,10 +454,8 @@
   MIR* GetNextUnconditionalMir(MIRGraph* mir_graph, MIR* current);
   bool IsExceptionBlock() const;
 
-  static void* operator new(size_t size, ArenaAllocator* arena) {
-    return arena->Alloc(sizeof(BasicBlock), kArenaAllocBB);
-  }
-  static void operator delete(void* p) {}  // Nop.
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BasicBlock);
 };
 
 /*
@@ -507,7 +488,7 @@
   bool visited_fallthrough_;
   bool visited_taken_;
   bool have_successors_;
-  GrowableArray<SuccessorBlockInfo*>::Iterator successor_iter_;
+  ArenaVector<SuccessorBlockInfo*>::const_iterator successor_iter_;
 };
 
 /*
@@ -539,7 +520,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
@@ -562,24 +543,43 @@
 
   /* Find existing block */
   BasicBlock* FindBlock(DexOffset code_offset) {
-    return FindBlock(code_offset, false, false, NULL);
+    return FindBlock(code_offset, false, NULL);
   }
 
   const uint16_t* GetCurrentInsns() const {
     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 + static_cast<int32_t>(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 +594,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_;
   }
 
@@ -621,7 +622,7 @@
     return def_count_;
   }
 
-  ArenaAllocator* GetArena() {
+  ArenaAllocator* GetArena() const {
     return arena_;
   }
 
@@ -660,20 +661,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 +687,28 @@
 
   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_;
+  }
+
+  size_t GetMaxNestedLoops() const {
+    return max_nested_loops_;
   }
 
   bool IsConst(int32_t s_reg) const {
@@ -724,6 +729,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 +750,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 +786,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 +850,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 +887,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 +898,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 +914,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 +971,53 @@
   }
 
   // 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_;
+  }
+
+  bool HasTryCatchBlocks() const {
+    return current_code_item_->tries_size_ != 0;
   }
 
   void DumpCheckStats();
@@ -920,9 +1025,10 @@
   int SRegToVReg(int ssa_reg) const;
   void VerifyDataflow();
   void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb);
-  void EliminateNullChecksAndInferTypesStart();
-  bool EliminateNullChecksAndInferTypes(BasicBlock* bb);
-  void EliminateNullChecksAndInferTypesEnd();
+  bool EliminateNullChecksGate();
+  bool EliminateNullChecks(BasicBlock* bb);
+  void EliminateNullChecksEnd();
+  bool InferTypes(BasicBlock* bb);
   bool EliminateClassInitChecksGate();
   bool EliminateClassInitChecks(BasicBlock* bb);
   void EliminateClassInitChecksEnd();
@@ -965,6 +1071,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);
@@ -1023,7 +1130,7 @@
    * @brief Count the uses in the BasicBlock
    * @param bb the BasicBlock
    */
-  void CountUses(struct BasicBlock* bb);
+  void CountUses(class BasicBlock* bb);
 
   static uint64_t GetDataFlowAttributes(Instruction::Code opcode);
   static uint64_t GetDataFlowAttributes(MIR* mir);
@@ -1044,9 +1151,14 @@
   void ComputeDefBlockMatrix();
   void ComputeDominators();
   void CompilerInitializeSSAConversion();
+  virtual void InitializeBasicBlockDataFlow();
   void InsertPhiNodes();
   void DoDFSPreOrderSSARename(BasicBlock* block);
 
+  bool DfsOrdersUpToDate() const {
+    return dfs_orders_up_to_date_;
+  }
+
   /*
    * IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on
    * we can verify that all catch entries have native PC entries.
@@ -1058,16 +1170,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,13 +1199,11 @@
                       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,
                          BasicBlock** immed_pred_block_p);
-  BasicBlock* FindBlock(DexOffset code_offset, bool split, bool create,
-                        BasicBlock** immed_pred_block_p);
+  BasicBlock* FindBlock(DexOffset code_offset, bool create, BasicBlock** immed_pred_block_p);
   void ProcessTryCatchBlocks();
   bool IsBadMonitorExitCatch(NarrowDexOffset monitor_exit_offset, NarrowDexOffset catch_offset);
   BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
@@ -1116,11 +1225,9 @@
   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);
+  bool BuildExtendedBBList(class BasicBlock* bb);
   bool FillDefBlockMatrix(BasicBlock* bb);
   void InitializeDominationInfo(BasicBlock* bb);
   bool ComputeblockIDom(BasicBlock* bb);
@@ -1135,45 +1242,51 @@
                               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_;
+  bool dfs_orders_up_to_date_;
+  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);
+  static_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_;
+  size_t max_nested_loops_;
   int* i_dom_list_;
-  ArenaBitVector** def_block_matrix_;    // num_dalvik_register 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_;
+  // temp_bit_matrix_ used as one of
+  //   - def_block_matrix: original num registers x num_blocks_,
+  //   - ending_null_check_matrix: num_blocks_ x original num registers,
+  //   - ending_clinit_check_matrix: num_blocks_ x unique class count.
+  ArenaBitVector** temp_bit_matrix_;
   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
@@ -1186,22 +1299,26 @@
   int method_sreg_;
   unsigned int attributes_;
   Checkstats* checkstats_;
-  ArenaAllocator* arena_;
+  ArenaAllocator* const 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 MirOptimizationTest;
   friend class ClassInitCheckEliminationTest;
+  friend class NullCheckEliminationTest;
   friend class GlobalValueNumberingTest;
   friend class LocalValueNumberingTest;
   friend class TopologicalSortOrderTest;
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_method_info.cc b/compiler/dex/mir_method_info.cc
index cc2bd95..b234950 100644
--- a/compiler/dex/mir_method_info.cc
+++ b/compiler/dex/mir_method_info.cc
@@ -76,14 +76,16 @@
     int fast_path_flags = compiler_driver->IsFastInvoke(
         soa, dex_cache, class_loader, mUnit, referrer_class.Get(), resolved_method, &invoke_type,
         &target_method, devirt_target, &it->direct_code_, &it->direct_method_);
-    bool needs_clinit =
-        compiler_driver->NeedsClassInitialization(referrer_class.Get(), resolved_method);
+    bool is_referrers_class = (referrer_class.Get() == resolved_method->GetDeclaringClass());
+    bool is_class_initialized =
+        compiler_driver->IsMethodsClassInitialized(referrer_class.Get(), resolved_method);
     uint16_t other_flags = it->flags_ &
-        ~(kFlagFastPath | kFlagNeedsClassInitialization | (kInvokeTypeMask << kBitSharpTypeBegin));
+        ~(kFlagFastPath | kFlagClassIsInitialized | (kInvokeTypeMask << kBitSharpTypeBegin));
     it->flags_ = other_flags |
         (fast_path_flags != 0 ? kFlagFastPath : 0u) |
         (static_cast<uint16_t>(invoke_type) << kBitSharpTypeBegin) |
-        (needs_clinit ? kFlagNeedsClassInitialization : 0u);
+        (is_referrers_class ? kFlagIsReferrersClass : 0u) |
+        (is_class_initialized ? kFlagClassIsInitialized : 0u);
     it->target_dex_file_ = target_method.dex_file;
     it->target_method_idx_ = target_method.dex_method_index;
     it->stats_flags_ = fast_path_flags;
diff --git a/compiler/dex/mir_method_info.h b/compiler/dex/mir_method_info.h
index efe92f3..08fb103 100644
--- a/compiler/dex/mir_method_info.h
+++ b/compiler/dex/mir_method_info.h
@@ -60,7 +60,7 @@
     kBitIsStatic = 0,
     kMethodInfoBitEnd
   };
-  COMPILE_ASSERT(kMethodInfoBitEnd <= 16, too_many_flags);
+  static_assert(kMethodInfoBitEnd <= 16, "Too many flags");
   static constexpr uint16_t kFlagIsStatic = 1u << kBitIsStatic;
 
   MirMethodInfo(uint16_t method_idx, uint16_t flags)
@@ -123,8 +123,12 @@
     return (flags_ & kFlagFastPath) != 0u;
   }
 
-  bool NeedsClassInitialization() const {
-    return (flags_ & kFlagNeedsClassInitialization) != 0u;
+  bool IsReferrersClass() const {
+    return (flags_ & kFlagIsReferrersClass) != 0;
+  }
+
+  bool IsClassInitialized() const {
+    return (flags_ & kFlagClassIsInitialized) != 0u;
   }
 
   InvokeType GetInvokeType() const {
@@ -162,17 +166,19 @@
     kBitInvokeTypeEnd = kBitInvokeTypeBegin + 3,  // 3 bits for invoke type.
     kBitSharpTypeBegin,
     kBitSharpTypeEnd = kBitSharpTypeBegin + 3,  // 3 bits for sharp type.
-    kBitNeedsClassInitialization = kBitSharpTypeEnd,
-    kMethodLoweringInfoEnd
+    kBitIsReferrersClass = kBitSharpTypeEnd,
+    kBitClassIsInitialized,
+    kMethodLoweringInfoBitEnd
   };
-  COMPILE_ASSERT(kMethodLoweringInfoEnd <= 16, too_many_flags);
+  static_assert(kMethodLoweringInfoBitEnd <= 16, "Too many flags");
   static constexpr uint16_t kFlagFastPath = 1u << kBitFastPath;
-  static constexpr uint16_t kFlagNeedsClassInitialization = 1u << kBitNeedsClassInitialization;
+  static constexpr uint16_t kFlagIsReferrersClass = 1u << kBitIsReferrersClass;
+  static constexpr uint16_t kFlagClassIsInitialized = 1u << kBitClassIsInitialized;
   static constexpr uint16_t kInvokeTypeMask = 7u;
-  COMPILE_ASSERT((1u << (kBitInvokeTypeEnd - kBitInvokeTypeBegin)) - 1u == kInvokeTypeMask,
-                 assert_invoke_type_bits_ok);
-  COMPILE_ASSERT((1u << (kBitSharpTypeEnd - kBitSharpTypeBegin)) - 1u == kInvokeTypeMask,
-                 assert_sharp_type_bits_ok);
+  static_assert((1u << (kBitInvokeTypeEnd - kBitInvokeTypeBegin)) - 1u == kInvokeTypeMask,
+                "assert invoke type bits failed");
+  static_assert((1u << (kBitSharpTypeEnd - kBitSharpTypeBegin)) - 1u == kInvokeTypeMask,
+                "assert sharp type bits failed");
 
   uintptr_t direct_code_;
   uintptr_t direct_method_;
@@ -185,7 +191,7 @@
   uint16_t vtable_idx_;
   int stats_flags_;
 
-  friend class ClassInitCheckEliminationTest;
+  friend class MirOptimizationTest;
 };
 
 }  // namespace art
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 23ceb56..a0ad213 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -14,28 +14,29 @@
  * limitations under the License.
  */
 
+#include "base/bit_vector-inl.h"
 #include "compiler_internals.h"
+#include "dataflow_iterator-inl.h"
 #include "global_value_numbering.h"
 #include "local_value_numbering.h"
-#include "dataflow_iterator-inl.h"
-#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 "quick/dex_file_method_inliner.h"
+#include "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);
@@ -184,6 +185,10 @@
 }
 
 static SelectInstructionKind SelectKind(MIR* mir) {
+  // Work with the case when mir is nullptr.
+  if (mir == nullptr) {
+    return kSelectNone;
+  }
   switch (mir->dalvikInsn.opcode) {
     case Instruction::MOVE:
     case Instruction::MOVE_OBJECT:
@@ -209,8 +214,8 @@
     kCondEq, kCondNe, kCondLt, kCondGe, kCondGt, kCondLe
 };
 
-COMPILE_ASSERT(arraysize(kIfCcZConditionCodes) == Instruction::IF_LEZ - Instruction::IF_EQZ + 1,
-               if_ccz_ccodes_size1);
+static_assert(arraysize(kIfCcZConditionCodes) == Instruction::IF_LEZ - Instruction::IF_EQZ + 1,
+              "if_ccz_ccodes_size1");
 
 static constexpr bool IsInstructionIfCcZ(Instruction::Code opcode) {
   return Instruction::IF_EQZ <= opcode && opcode <= Instruction::IF_LEZ;
@@ -220,38 +225,71 @@
   return kIfCcZConditionCodes[opcode - Instruction::IF_EQZ];
 }
 
-COMPILE_ASSERT(ConditionCodeForIfCcZ(Instruction::IF_EQZ) == kCondEq, check_if_eqz_ccode);
-COMPILE_ASSERT(ConditionCodeForIfCcZ(Instruction::IF_NEZ) == kCondNe, check_if_nez_ccode);
-COMPILE_ASSERT(ConditionCodeForIfCcZ(Instruction::IF_LTZ) == kCondLt, check_if_ltz_ccode);
-COMPILE_ASSERT(ConditionCodeForIfCcZ(Instruction::IF_GEZ) == kCondGe, check_if_gez_ccode);
-COMPILE_ASSERT(ConditionCodeForIfCcZ(Instruction::IF_GTZ) == kCondGt, check_if_gtz_ccode);
-COMPILE_ASSERT(ConditionCodeForIfCcZ(Instruction::IF_LEZ) == kCondLe, check_if_lez_ccode);
+static_assert(ConditionCodeForIfCcZ(Instruction::IF_EQZ) == kCondEq, "if_eqz ccode");
+static_assert(ConditionCodeForIfCcZ(Instruction::IF_NEZ) == kCondNe, "if_nez ccode");
+static_assert(ConditionCodeForIfCcZ(Instruction::IF_LTZ) == kCondLt, "if_ltz ccode");
+static_assert(ConditionCodeForIfCcZ(Instruction::IF_GEZ) == kCondGe, "if_gez ccode");
+static_assert(ConditionCodeForIfCcZ(Instruction::IF_GTZ) == kCondGt, "if_gtz ccode");
+static_assert(ConditionCodeForIfCcZ(Instruction::IF_LEZ) == kCondLe, "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 +298,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 +399,6 @@
     reg_location_[ssa_reg_low].wide = wide;
   }
 
-  compiler_temps_.Insert(compiler_temp);
   return compiler_temp;
 }
 
@@ -321,14 +407,14 @@
   if (bb->block_type == kDead) {
     return true;
   }
-  // Don't do a separate LVN if we did the GVN.
-  bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1u << kGlobalValueNumbering)) != 0u;
+  bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1u << kLocalValueNumbering)) == 0u;
   std::unique_ptr<ScopedArenaAllocator> allocator;
   std::unique_ptr<GlobalValueNumbering> global_valnum;
   std::unique_ptr<LocalValueNumbering> local_valnum;
   if (use_lvn) {
     allocator.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
-    global_valnum.reset(new (allocator.get()) GlobalValueNumbering(cu_, allocator.get()));
+    global_valnum.reset(new (allocator.get()) GlobalValueNumbering(cu_, allocator.get(),
+                                                                   GlobalValueNumbering::kModeLvn));
     local_valnum.reset(new (allocator.get()) LocalValueNumbering(global_valnum.get(), bb->id,
                                                                  allocator.get()));
   }
@@ -573,7 +659,7 @@
 }
 
 /* Collect stats on number of checks removed */
-void MIRGraph::CountChecks(struct BasicBlock* bb) {
+void MIRGraph::CountChecks(class BasicBlock* bb) {
   if (bb->data_flow_info != NULL) {
     for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
       if (mir->ssa_rep == NULL) {
@@ -596,26 +682,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,204 +740,269 @@
       break;
     }
     walker = prev;
+
+    if (walker->visited) {
+      break;
+    }
   }
   return false;
 }
 
 /* Combine any basic blocks terminated by instructions that we now know can't throw */
-void MIRGraph::CombineBlocks(struct BasicBlock* bb) {
+void MIRGraph::CombineBlocks(class BasicBlock* bb) {
   // Loop here to allow combining a sequence of blocks
-  while (true) {
-    // Check termination conditions
-    if ((bb->first_mir_insn == NULL)
-        || (bb->data_flow_info == NULL)
-        || (bb->block_type == kExceptionHandling)
-        || (bb->block_type == kExitBlock)
-        || (bb->block_type == kDead)
-        || (bb->taken == NullBasicBlockId)
-        || (GetBasicBlock(bb->taken)->block_type != kExceptionHandling)
-        || (bb->successor_block_list_type != kNotUsed)
-        || (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) != kMirOpCheck)) {
+  while ((bb->block_type == kDalvikByteCode) &&
+      (bb->last_mir_insn != nullptr) &&
+      (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) == kMirOpCheck)) {
+    MIR* mir = bb->last_mir_insn;
+    DCHECK(bb->first_mir_insn !=  nullptr);
+
+    // Grab the attributes from the paired opcode.
+    MIR* throw_insn = mir->meta.throw_insn;
+    uint64_t df_attributes = GetDataFlowAttributes(throw_insn);
+
+    // Don't combine if the throw_insn can still throw NPE.
+    if ((df_attributes & DF_HAS_NULL_CHKS) != 0 &&
+        (throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) {
+      break;
+    }
+    // Now whitelist specific instructions.
+    bool ok = false;
+    if ((df_attributes & DF_IFIELD) != 0) {
+      // Combine only if fast, otherwise weird things can happen.
+      const MirIFieldLoweringInfo& field_info = GetIFieldLoweringInfo(throw_insn);
+      ok = (df_attributes & DF_DA)  ? field_info.FastGet() : field_info.FastPut();
+    } else if ((df_attributes & DF_SFIELD) != 0) {
+      // Combine only if fast, otherwise weird things can happen.
+      const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(throw_insn);
+      bool fast = ((df_attributes & DF_DA)  ? field_info.FastGet() : field_info.FastPut());
+      // Don't combine if the SGET/SPUT can call <clinit>().
+      bool clinit = !field_info.IsClassInitialized() &&
+          (throw_insn->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0;
+      ok = fast && !clinit;
+    } else if ((df_attributes & DF_HAS_RANGE_CHKS) != 0) {
+      // Only AGET/APUT have range checks. We have processed the AGET/APUT null check above.
+      DCHECK_NE(throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK, 0);
+      ok = ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0);
+    } else if ((throw_insn->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) {
+      // We can encounter a non-throwing insn here thanks to inlining or other optimizations.
+      ok = true;
+    } else if (throw_insn->dalvikInsn.opcode == Instruction::ARRAY_LENGTH ||
+        throw_insn->dalvikInsn.opcode == Instruction::FILL_ARRAY_DATA ||
+        static_cast<int>(throw_insn->dalvikInsn.opcode) == kMirOpNullCheck) {
+      // No more checks for these (null check was processed above).
+      ok = true;
+    }
+    if (!ok) {
       break;
     }
 
-    // Test the kMirOpCheck instruction
-    MIR* mir = bb->last_mir_insn;
-    // Grab the attributes from the paired opcode
-    MIR* throw_insn = mir->meta.throw_insn;
-    uint64_t df_attributes = GetDataFlowAttributes(throw_insn);
-    bool can_combine = true;
-    if (df_attributes & DF_HAS_NULL_CHKS) {
-      can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0);
-    }
-    if (df_attributes & DF_HAS_RANGE_CHKS) {
-      can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0);
-    }
-    if (!can_combine) {
-      break;
-    }
     // OK - got one.  Combine
     BasicBlock* bb_next = GetBasicBlock(bb->fall_through);
     DCHECK(!bb_next->catch_entry);
-    DCHECK_EQ(Predecessors(bb_next), 1U);
-    // Overwrite the kOpCheck insn with the paired opcode
+    DCHECK_EQ(bb_next->predecessors.size(), 1u);
+    // Overwrite the kMirOpCheck insn with the paired opcode.
     DCHECK_EQ(bb_next->first_mir_insn, throw_insn);
     *bb->last_mir_insn = *throw_insn;
+    // And grab the rest of the instructions from bb_next.
+    bb->last_mir_insn = bb_next->last_mir_insn;
+    throw_insn->next = nullptr;
+    bb_next->last_mir_insn = throw_insn;
+    // Mark acquired instructions as belonging to bb.
+    for (MIR* insn = mir; insn != nullptr; insn = insn->next) {
+      insn->bb = bb->id;
+    }
+    // Before we overwrite successors, remove their predecessor links to bb.
+    bb_next->ErasePredecessor(bb->id);
+    if (bb->taken != NullBasicBlockId) {
+      DCHECK_EQ(bb->successor_block_list_type, kNotUsed);
+      BasicBlock* bb_taken = GetBasicBlock(bb->taken);
+      // bb->taken will be overwritten below.
+      DCHECK_EQ(bb_taken->block_type, kExceptionHandling);
+      DCHECK_EQ(bb_taken->predecessors.size(), 1u);
+      DCHECK_EQ(bb_taken->predecessors[0], bb->id);
+      bb_taken->predecessors.clear();
+      bb_taken->block_type = kDead;
+      DCHECK(bb_taken->data_flow_info == nullptr);
+    } else {
+      DCHECK_EQ(bb->successor_block_list_type, kCatch);
+      for (SuccessorBlockInfo* succ_info : bb->successor_blocks) {
+        if (succ_info->block != NullBasicBlockId) {
+          BasicBlock* succ_bb = GetBasicBlock(succ_info->block);
+          DCHECK(succ_bb->catch_entry);
+          succ_bb->ErasePredecessor(bb->id);
+          if (succ_bb->predecessors.empty()) {
+            succ_bb->KillUnreachable(this);
+          }
+        }
+      }
+    }
     // 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.
+    bb_next->successor_block_list_type = kNotUsed;
     // 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
+    bb_next->fall_through = NullBasicBlockId;
     bb->taken = bb_next->taken;
-    // Include the rest of the instructions
-    bb->last_mir_insn = bb_next->last_mir_insn;
+    bb_next->taken = NullBasicBlockId;
     /*
-     * 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;
+    // Merge the use_lvn flag.
+    bb->use_lvn |= bb_next->use_lvn;
+
+    // Kill the unused block.
+    bb_next->data_flow_info = nullptr;
 
     /*
      * NOTE: we aren't updating all dataflow info here.  Should either make sure this pass
      * happens after uses of i_dominated, dom_frontier or update the dataflow info here.
+     * NOTE: GVN uses bb->data_flow_info->live_in_v which is unaffected by the block merge.
      */
 
-    // Kill bb_next and remap now-dead id to parent
+    // Kill bb_next and remap now-dead id to parent.
     bb_next->block_type = kDead;
+    bb_next->data_flow_info = nullptr;  // Must be null for dead blocks. (Relied on by the GVN.)
     block_id_map_.Overwrite(bb_next->id, bb->id);
+    // Update predecessors in children.
+    ChildBlockIterator iter(bb, this);
+    for (BasicBlock* child = iter.Next(); child != nullptr; child = iter.Next()) {
+      child->UpdatePredecessor(bb_next->id, bb->id);
+    }
+
+    // DFS orders are not up to date anymore.
+    dfs_orders_up_to_date_ = false;
 
     // Now, loop back and see if we can keep going
   }
 }
 
-void MIRGraph::EliminateNullChecksAndInferTypesStart() {
-  if ((cu_->disable_opt & (1 << kNullCheckElimination)) == 0) {
-    if (kIsDebugBuild) {
-      AllNodesIterator iter(this);
-      for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
-        CHECK(bb->data_flow_info == nullptr || bb->data_flow_info->ending_check_v == nullptr);
-      }
-    }
-
-    DCHECK(temp_scoped_alloc_.get() == nullptr);
-    temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
-    temp_bit_vector_size_ = GetNumSSARegs();
-    temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
-        temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapTempSSARegisterV);
+bool MIRGraph::EliminateNullChecksGate() {
+  if ((cu_->disable_opt & (1 << kNullCheckElimination)) != 0 ||
+      (merged_df_flags_ & DF_HAS_NULL_CHKS) == 0) {
+    return false;
   }
+
+  DCHECK(temp_scoped_alloc_.get() == nullptr);
+  temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
+  temp_bit_vector_size_ = GetNumOfCodeVRs();
+  temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
+      temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck);
+  temp_bit_matrix_ = static_cast<ArenaBitVector**>(
+      temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * GetNumBlocks(), kArenaAllocMisc));
+  std::fill_n(temp_bit_matrix_, GetNumBlocks(), nullptr);
+
+  // reset MIR_MARK
+  AllNodesIterator iter(this);
+  for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
+    for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+      mir->optimization_flags &= ~MIR_MARK;
+    }
+  }
+
+  return true;
 }
 
 /*
- * Eliminate unnecessary null checks for a basic block.   Also, while we're doing
- * an iterative walk go ahead and perform type and size inference.
+ * Eliminate unnecessary null checks for a basic block.
  */
-bool MIRGraph::EliminateNullChecksAndInferTypes(BasicBlock* bb) {
-  if (bb->data_flow_info == NULL) return false;
-  bool infer_changed = false;
-  bool do_nce = ((cu_->disable_opt & (1 << kNullCheckElimination)) == 0);
+bool MIRGraph::EliminateNullChecks(BasicBlock* bb) {
+  if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) {
+    // Ignore the kExitBlock as well.
+    DCHECK(bb->first_mir_insn == nullptr);
+    return false;
+  }
 
-  ArenaBitVector* ssa_regs_to_check = temp_bit_vector_;
-  if (do_nce) {
-    /*
-     * Set initial state. Catch blocks don't need any special treatment.
-     */
-    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++) {
-        ssa_regs_to_check->SetBit(in_reg);
+  ArenaBitVector* vregs_to_check = temp_bit_vector_;
+  /*
+   * Set initial state. Catch blocks don't need any special treatment.
+   */
+  if (bb->block_type == kEntryBlock) {
+    vregs_to_check->ClearAllBits();
+    // Assume all ins are objects.
+    for (uint16_t in_reg = GetFirstInVR();
+         in_reg < GetNumOfCodeVRs(); in_reg++) {
+      vregs_to_check->SetBit(in_reg);
+    }
+    if ((cu_->access_flags & kAccStatic) == 0) {
+      // If non-static method, mark "this" as non-null.
+      int this_reg = GetFirstInVR();
+      vregs_to_check->ClearBit(this_reg);
+    }
+  } else {
+    DCHECK_EQ(bb->block_type, kDalvikByteCode);
+    // Starting state is union of all incoming arcs.
+    bool copied_first = false;
+    for (BasicBlockId pred_id : bb->predecessors) {
+      if (temp_bit_matrix_[pred_id] == nullptr) {
+        continue;
       }
-      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;
-        ssa_regs_to_check->ClearBit(this_reg);
-      }
-    } else if (bb->predecessors->Size() == 1) {
-      BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(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);
+      BasicBlock* pred_bb = GetBasicBlock(pred_id);
+      DCHECK(pred_bb != nullptr);
+      MIR* null_check_insn = nullptr;
       if (pred_bb->block_type == kDalvikByteCode) {
         // Check to see if predecessor had an explicit null-check.
         MIR* last_insn = pred_bb->last_mir_insn;
         if (last_insn != nullptr) {
           Instruction::Code last_opcode = last_insn->dalvikInsn.opcode;
-          if (last_opcode == Instruction::IF_EQZ) {
-            if (pred_bb->fall_through == bb->id) {
-              // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that
-              // it can't be null.
-              ssa_regs_to_check->ClearBit(last_insn->ssa_rep->uses[0]);
-            }
-          } else if (last_opcode == Instruction::IF_NEZ) {
-            if (pred_bb->taken == bb->id) {
-              // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be
-              // null.
-              ssa_regs_to_check->ClearBit(last_insn->ssa_rep->uses[0]);
+          if ((last_opcode == Instruction::IF_EQZ && pred_bb->fall_through == bb->id) ||
+              (last_opcode == Instruction::IF_NEZ && pred_bb->taken == bb->id)) {
+            // Remember the null check insn if there's no other predecessor requiring null check.
+            if (!copied_first || !vregs_to_check->IsBitSet(last_insn->dalvikInsn.vA)) {
+              null_check_insn = last_insn;
             }
           }
         }
       }
-    } 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.
-        DCHECK(pred_bb != nullptr);
-        DCHECK(pred_bb->data_flow_info != nullptr);
+      if (!copied_first) {
+        copied_first = true;
+        vregs_to_check->Copy(temp_bit_matrix_[pred_id]);
+      } else {
+        vregs_to_check->Union(temp_bit_matrix_[pred_id]);
       }
-      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 (null_check_insn != nullptr) {
+        vregs_to_check->ClearBit(null_check_insn->dalvikInsn.vA);
       }
     }
-    // At this point, ssa_regs_to_check shows which sregs have an object definition with
-    // no intervening uses.
+    DCHECK(copied_first);  // At least one predecessor must have been processed before this bb.
   }
+  // At this point, vregs_to_check shows which sregs have an object definition with
+  // no intervening uses.
 
   // Walk through the instruction in the block, updating as necessary
   for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
-    if (mir->ssa_rep == NULL) {
-        continue;
-    }
-
-    // Propagate type info.
-    infer_changed = InferTypeAndSize(bb, mir, infer_changed);
-    if (!do_nce) {
-      continue;
-    }
-
     uint64_t df_attributes = GetDataFlowAttributes(mir);
 
+    DCHECK_EQ(df_attributes & DF_NULL_TRANSFER_N, 0u);  // No Phis yet.
+
     // Might need a null check?
     if (df_attributes & DF_HAS_NULL_CHKS) {
-      int src_idx;
-      if (df_attributes & DF_NULL_CHK_1) {
-        src_idx = 1;
-      } else if (df_attributes & DF_NULL_CHK_2) {
-        src_idx = 2;
+      int src_vreg;
+      if (df_attributes & DF_NULL_CHK_OUT0) {
+        DCHECK_NE(df_attributes & DF_IS_INVOKE, 0u);
+        src_vreg = mir->dalvikInsn.vC;
+      } else if (df_attributes & DF_NULL_CHK_B) {
+        DCHECK_NE(df_attributes & DF_REF_B, 0u);
+        src_vreg = mir->dalvikInsn.vB;
       } else {
-        src_idx = 0;
+        DCHECK_NE(df_attributes & DF_NULL_CHK_A, 0u);
+        DCHECK_NE(df_attributes & DF_REF_A, 0u);
+        src_vreg = mir->dalvikInsn.vA;
       }
-      int src_sreg = mir->ssa_rep->uses[src_idx];
-      if (!ssa_regs_to_check->IsBitSet(src_sreg)) {
+      if (!vregs_to_check->IsBitSet(src_vreg)) {
         // Eliminate the null check.
-        mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+        mir->optimization_flags |= MIR_MARK;
       } else {
         // Do the null check.
-        mir->optimization_flags &= ~MIR_IGNORE_NULL_CHECK;
-        // Mark s_reg as null-checked
-        ssa_regs_to_check->ClearBit(src_sreg);
+        mir->optimization_flags &= ~MIR_MARK;
+        // Mark src_vreg as null-checked.
+        vregs_to_check->ClearBit(src_vreg);
       }
     }
 
@@ -850,123 +1016,117 @@
      * Note: we can't tell if a CONST definition might be used as an object, so treat
      * them all as object definitions.
      */
-    if (((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A)) ||
+    if ((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A) ||
         (df_attributes & DF_SETS_CONST))  {
-      ssa_regs_to_check->SetBit(mir->ssa_rep->defs[0]);
+      vregs_to_check->SetBit(mir->dalvikInsn.vA);
     }
 
-    // Now, remove mark from all object definitions we know are non-null.
+    // Then, remove mark from all object definitions we know are non-null.
     if (df_attributes & DF_NON_NULL_DST) {
       // Mark target of NEW* as non-null
-      ssa_regs_to_check->ClearBit(mir->ssa_rep->defs[0]);
+      DCHECK_NE(df_attributes & DF_REF_A, 0u);
+      vregs_to_check->ClearBit(mir->dalvikInsn.vA);
     }
 
     // Mark non-null returns from invoke-style NEW*
     if (df_attributes & DF_NON_NULL_RET) {
       MIR* next_mir = mir->next;
       // Next should be an MOVE_RESULT_OBJECT
-      if (next_mir &&
-          next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
-        // Mark as null checked
-        ssa_regs_to_check->ClearBit(next_mir->ssa_rep->defs[0]);
+      if (UNLIKELY(next_mir == nullptr)) {
+        // The MethodVerifier makes sure there's no MOVE_RESULT at the catch entry or branch
+        // target, so the MOVE_RESULT cannot be broken away into another block.
+        LOG(WARNING) << "Unexpected end of block following new";
+      } else if (UNLIKELY(next_mir->dalvikInsn.opcode != Instruction::MOVE_RESULT_OBJECT)) {
+        LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode;
       } else {
-        if (next_mir) {
-          LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode;
-        } else if (bb->fall_through != NullBasicBlockId) {
-          // Look in next basic block
-          struct BasicBlock* next_bb = GetBasicBlock(bb->fall_through);
-          for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL;
-            tmir =tmir->next) {
-            if (MIR::DecodedInstruction::IsPseudoMirOp(tmir->dalvikInsn.opcode)) {
-              continue;
-            }
-            // First non-pseudo should be MOVE_RESULT_OBJECT
-            if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
-              // Mark as null checked
-              ssa_regs_to_check->ClearBit(tmir->ssa_rep->defs[0]);
-            } else {
-              LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode;
-            }
-            break;
-          }
-        }
+        // Mark as null checked.
+        vregs_to_check->ClearBit(next_mir->dalvikInsn.vA);
       }
     }
 
-    /*
-     * Propagate nullcheck state on register copies (including
-     * Phi pseudo copies.  For the latter, nullcheck state is
-     * the "or" of all the Phi's operands.
-     */
-    if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) {
-      int tgt_sreg = mir->ssa_rep->defs[0];
-      int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 :
-          mir->ssa_rep->num_uses;
-      bool needs_null_check = false;
-      for (int i = 0; i < operands; i++) {
-        needs_null_check |= ssa_regs_to_check->IsBitSet(mir->ssa_rep->uses[i]);
-      }
-      if (needs_null_check) {
-        ssa_regs_to_check->SetBit(tgt_sreg);
+    // Propagate null check state on register copies.
+    if (df_attributes & DF_NULL_TRANSFER_0) {
+      DCHECK_EQ(df_attributes | ~(DF_DA | DF_REF_A | DF_UB | DF_REF_B), static_cast<uint64_t>(-1));
+      if (vregs_to_check->IsBitSet(mir->dalvikInsn.vB)) {
+        vregs_to_check->SetBit(mir->dalvikInsn.vA);
       } else {
-        ssa_regs_to_check->ClearBit(tgt_sreg);
+        vregs_to_check->ClearBit(mir->dalvikInsn.vA);
       }
     }
   }
 
   // Did anything change?
   bool nce_changed = false;
-  if (do_nce) {
-    if (bb->data_flow_info->ending_check_v == nullptr) {
-      DCHECK(temp_scoped_alloc_.get() != nullptr);
-      bb->data_flow_info->ending_check_v = new (temp_scoped_alloc_.get()) ArenaBitVector(
-          temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck);
-      nce_changed = ssa_regs_to_check->GetHighestBitSet() != -1;
-      bb->data_flow_info->ending_check_v->Copy(ssa_regs_to_check);
-    } else if (!ssa_regs_to_check->SameBitsSet(bb->data_flow_info->ending_check_v)) {
-      nce_changed = true;
-      bb->data_flow_info->ending_check_v->Copy(ssa_regs_to_check);
-    }
+  ArenaBitVector* old_ending_ssa_regs_to_check = temp_bit_matrix_[bb->id];
+  if (old_ending_ssa_regs_to_check == nullptr) {
+    DCHECK(temp_scoped_alloc_.get() != nullptr);
+    nce_changed = vregs_to_check->GetHighestBitSet() != -1;
+    temp_bit_matrix_[bb->id] = vregs_to_check;
+    // Create a new vregs_to_check for next BB.
+    temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
+        temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck);
+  } else if (!vregs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) {
+    nce_changed = true;
+    temp_bit_matrix_[bb->id] = vregs_to_check;
+    temp_bit_vector_ = old_ending_ssa_regs_to_check;  // Reuse for vregs_to_check for next BB.
   }
-  return infer_changed | nce_changed;
+  return nce_changed;
 }
 
-void MIRGraph::EliminateNullChecksAndInferTypesEnd() {
-  if ((cu_->disable_opt & (1 << kNullCheckElimination)) == 0) {
-    // Clean up temporaries.
-    temp_bit_vector_size_ = 0u;
-    temp_bit_vector_ = nullptr;
-    AllNodesIterator iter(this);
-    for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
-      if (bb->data_flow_info != nullptr) {
-        bb->data_flow_info->ending_check_v = nullptr;
-      }
+void MIRGraph::EliminateNullChecksEnd() {
+  // Clean up temporaries.
+  temp_bit_vector_size_ = 0u;
+  temp_bit_vector_ = nullptr;
+  temp_bit_matrix_ = nullptr;
+  DCHECK(temp_scoped_alloc_.get() != nullptr);
+  temp_scoped_alloc_.reset();
+
+  // converge MIR_MARK with MIR_IGNORE_NULL_CHECK
+  AllNodesIterator iter(this);
+  for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
+    for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+      constexpr int kMarkToIgnoreNullCheckShift = kMIRMark - kMIRIgnoreNullCheck;
+      static_assert(kMarkToIgnoreNullCheckShift > 0, "Not a valid right-shift");
+      uint16_t mirMarkAdjustedToIgnoreNullCheck =
+          (mir->optimization_flags & MIR_MARK) >> kMarkToIgnoreNullCheckShift;
+      mir->optimization_flags |= mirMarkAdjustedToIgnoreNullCheck;
     }
-    DCHECK(temp_scoped_alloc_.get() != nullptr);
-    temp_scoped_alloc_.reset();
   }
 }
 
+/*
+ * Perform type and size inference for a basic block.
+ */
+bool MIRGraph::InferTypes(BasicBlock* bb) {
+  if (bb->data_flow_info == nullptr) return false;
+
+  bool infer_changed = false;
+  for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+    if (mir->ssa_rep == NULL) {
+        continue;
+    }
+
+    // Propagate type info.
+    infer_changed = InferTypeAndSize(bb, mir, infer_changed);
+  }
+
+  return infer_changed;
+}
+
 bool MIRGraph::EliminateClassInitChecksGate() {
   if ((cu_->disable_opt & (1 << kClassInitCheckElimination)) != 0 ||
-      !cu_->mir_graph->HasStaticFieldAccess()) {
+      (merged_df_flags_ & DF_CLINIT) == 0) {
     return false;
   }
 
-  if (kIsDebugBuild) {
-    AllNodesIterator iter(this);
-    for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
-      CHECK(bb->data_flow_info == nullptr || bb->data_flow_info->ending_check_v == nullptr);
-    }
-  }
-
   DCHECK(temp_scoped_alloc_.get() == nullptr);
   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));
+  std::fill_n(temp_insn_data_, end, 0xffffu);
 
   uint32_t unique_class_count = 0u;
   {
@@ -997,26 +1157,40 @@
     // First, find all SGET/SPUTs that may need class initialization checks, record INVOKE_STATICs.
     AllNodesIterator iter(this);
     for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
-      for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
-        DCHECK(bb->data_flow_info != nullptr);
-        if (mir->dalvikInsn.opcode >= Instruction::SGET &&
-            mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
-          const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir);
-          uint16_t index = 0xffffu;
-          if (!field_info.IsInitialized()) {
-            DCHECK_LT(class_to_index_map.size(), 0xffffu);
-            MapEntry entry = {
-                // Treat unresolved fields as if each had its own class.
-                field_info.IsResolved() ? field_info.DeclaringDexFile()
-                                        : nullptr,
-                field_info.IsResolved() ? field_info.DeclaringClassIndex()
-                                        : field_info.FieldIndex(),
-                static_cast<uint16_t>(class_to_index_map.size())
-            };
-            index = class_to_index_map.insert(entry).first->index;
+      if (bb->block_type == kDalvikByteCode) {
+        for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+          if (mir->dalvikInsn.opcode >= Instruction::SGET &&
+              mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
+            const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir);
+            if (!field_info.IsReferrersClass()) {
+              DCHECK_LT(class_to_index_map.size(), 0xffffu);
+              MapEntry entry = {
+                  // Treat unresolved fields as if each had its own class.
+                  field_info.IsResolved() ? field_info.DeclaringDexFile()
+                                          : nullptr,
+                  field_info.IsResolved() ? field_info.DeclaringClassIndex()
+                                          : field_info.FieldIndex(),
+                  static_cast<uint16_t>(class_to_index_map.size())
+              };
+              uint16_t index = class_to_index_map.insert(entry).first->index;
+              // Using offset/2 for index into temp_insn_data_.
+              temp_insn_data_[mir->offset / 2u] = index;
+            }
+          } else if (mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
+              mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE) {
+            const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(mir);
+            DCHECK(method_info.IsStatic());
+            if (method_info.FastPath() && !method_info.IsReferrersClass()) {
+              MapEntry entry = {
+                  method_info.DeclaringDexFile(),
+                  method_info.DeclaringClassIndex(),
+                  static_cast<uint16_t>(class_to_index_map.size())
+              };
+              uint16_t index = class_to_index_map.insert(entry).first->index;
+              // Using offset/2 for index into temp_insn_data_.
+              temp_insn_data_[mir->offset / 2u] = index;
+            }
           }
-          // Using offset/2 for index into temp_insn_data_.
-          temp_insn_data_[mir->offset / 2u] = index;
         }
       }
     }
@@ -1030,9 +1204,13 @@
     return false;
   }
 
-  temp_bit_vector_size_ = unique_class_count;
+  // 2 bits for each class: is class initialized, is class in dex cache.
+  temp_bit_vector_size_ = 2u * unique_class_count;
   temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
       temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapClInitCheck);
+  temp_bit_matrix_ = static_cast<ArenaBitVector**>(
+      temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * GetNumBlocks(), kArenaAllocMisc));
+  std::fill_n(temp_bit_matrix_, GetNumBlocks(), nullptr);
   DCHECK_GT(temp_bit_vector_size_, 0u);
   return true;
 }
@@ -1042,7 +1220,9 @@
  */
 bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) {
   DCHECK_EQ((cu_->disable_opt & (1 << kClassInitCheckElimination)), 0u);
-  if (bb->data_flow_info == NULL) {
+  if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) {
+    // Ignore the kExitBlock as well.
+    DCHECK(bb->first_mir_insn == nullptr);
     return false;
   }
 
@@ -1053,72 +1233,84 @@
   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));
-    // 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.
-      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) {
+    // Starting state is union of all incoming arcs.
+    bool copied_first = false;
+    for (BasicBlockId pred_id : bb->predecessors) {
+      if (temp_bit_matrix_[pred_id] == nullptr) {
         continue;
       }
-      classes_to_check->Union(pred_bb->data_flow_info->ending_check_v);
+      if (!copied_first) {
+        copied_first = true;
+        classes_to_check->Copy(temp_bit_matrix_[pred_id]);
+      } else {
+        classes_to_check->Union(temp_bit_matrix_[pred_id]);
+      }
     }
+    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.
 
   // Walk through the instruction in the block, updating as necessary
   for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
-    if (mir->dalvikInsn.opcode >= Instruction::SGET &&
-        mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
-      uint16_t index = temp_insn_data_[mir->offset / 2u];
-      if (index != 0xffffu) {
-        if (mir->dalvikInsn.opcode >= Instruction::SGET &&
-            mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
-          if (!classes_to_check->IsBitSet(index)) {
-            // Eliminate the class init check.
-            mir->optimization_flags |= MIR_IGNORE_CLINIT_CHECK;
-          } else {
-            // Do the class init check.
-            mir->optimization_flags &= ~MIR_IGNORE_CLINIT_CHECK;
-          }
+    uint16_t index = temp_insn_data_[mir->offset / 2u];
+    if (index != 0xffffu) {
+      bool check_initialization = false;
+      bool check_dex_cache = false;
+
+      // NOTE: index != 0xffff does not guarantee that this is an SGET/SPUT/INVOKE_STATIC.
+      // Dex instructions with width 1 can have the same offset/2.
+
+      if (mir->dalvikInsn.opcode >= Instruction::SGET &&
+          mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
+        check_initialization = true;
+        check_dex_cache = true;
+      } else if (mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
+               mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE) {
+        check_initialization = true;
+        // NOTE: INVOKE_STATIC doesn't guarantee that the type will be in the dex cache.
+      }
+
+      if (check_dex_cache) {
+        uint32_t check_dex_cache_index = 2u * index + 1u;
+        if (!classes_to_check->IsBitSet(check_dex_cache_index)) {
+          // Eliminate the class init check.
+          mir->optimization_flags |= MIR_CLASS_IS_IN_DEX_CACHE;
+        } else {
+          // Do the class init check.
+          mir->optimization_flags &= ~MIR_CLASS_IS_IN_DEX_CACHE;
+        }
+        classes_to_check->ClearBit(check_dex_cache_index);
+      }
+      if (check_initialization) {
+        uint32_t check_clinit_index = 2u * index;
+        if (!classes_to_check->IsBitSet(check_clinit_index)) {
+          // Eliminate the class init check.
+          mir->optimization_flags |= MIR_CLASS_IS_INITIALIZED;
+        } else {
+          // Do the class init check.
+          mir->optimization_flags &= ~MIR_CLASS_IS_INITIALIZED;
         }
         // Mark the class as initialized.
-        classes_to_check->ClearBit(index);
+        classes_to_check->ClearBit(check_clinit_index);
       }
     }
   }
 
   // Did anything change?
   bool changed = false;
-  if (bb->data_flow_info->ending_check_v == nullptr) {
+  ArenaBitVector* old_ending_classes_to_check = temp_bit_matrix_[bb->id];
+  if (old_ending_classes_to_check == nullptr) {
     DCHECK(temp_scoped_alloc_.get() != nullptr);
-    DCHECK(bb->data_flow_info != nullptr);
-    bb->data_flow_info->ending_check_v = new (temp_scoped_alloc_.get()) ArenaBitVector(
-        temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapClInitCheck);
     changed = classes_to_check->GetHighestBitSet() != -1;
-    bb->data_flow_info->ending_check_v->Copy(classes_to_check);
-  } else if (!classes_to_check->Equal(bb->data_flow_info->ending_check_v)) {
+    temp_bit_matrix_[bb->id] = classes_to_check;
+    // Create a new classes_to_check for next BB.
+    temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
+        temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapClInitCheck);
+  } else if (!classes_to_check->Equal(old_ending_classes_to_check)) {
     changed = true;
-    bb->data_flow_info->ending_check_v->Copy(classes_to_check);
+    temp_bit_matrix_[bb->id] = classes_to_check;
+    temp_bit_vector_ = old_ending_classes_to_check;  // Reuse for classes_to_check for next BB.
   }
   return changed;
 }
@@ -1127,13 +1319,7 @@
   // Clean up temporaries.
   temp_bit_vector_size_ = 0u;
   temp_bit_vector_ = nullptr;
-  AllNodesIterator iter(this);
-  for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
-    if (bb->data_flow_info != nullptr) {
-      bb->data_flow_info->ending_check_v = nullptr;
-    }
-  }
-
+  temp_bit_matrix_ = nullptr;
   DCHECK(temp_insn_data_ != nullptr);
   temp_insn_data_ = nullptr;
   DCHECK(temp_scoped_alloc_.get() != nullptr);
@@ -1141,7 +1327,7 @@
 }
 
 bool MIRGraph::ApplyGlobalValueNumberingGate() {
-  if ((cu_->disable_opt & (1u << kGlobalValueNumbering)) != 0u) {
+  if (GlobalValueNumbering::Skip(cu_)) {
     return false;
   }
 
@@ -1149,7 +1335,8 @@
   temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
   DCHECK(temp_gvn_ == nullptr);
   temp_gvn_.reset(
-      new (temp_scoped_alloc_.get()) GlobalValueNumbering(cu_, temp_scoped_alloc_.get()));
+      new (temp_scoped_alloc_.get()) GlobalValueNumbering(cu_, temp_scoped_alloc_.get(),
+                                                          GlobalValueNumbering::kModeGvn));
   return true;
 }
 
@@ -1168,19 +1355,23 @@
 void MIRGraph::ApplyGlobalValueNumberingEnd() {
   // Perform modifications.
   if (temp_gvn_->Good()) {
-    temp_gvn_->AllowModifications();
-    PreOrderDfsIterator iter(this);
-    for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
-      ScopedArenaAllocator allocator(&cu_->arena_stack);  // Reclaim memory after each LVN.
-      LocalValueNumbering* lvn = temp_gvn_->PrepareBasicBlock(bb, &allocator);
-      if (lvn != nullptr) {
-        for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
-          lvn->GetValueNumber(mir);
+    if (max_nested_loops_ != 0u) {
+      temp_gvn_->StartPostProcessing();
+      TopologicalSortIterator iter(this);
+      for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
+        ScopedArenaAllocator allocator(&cu_->arena_stack);  // Reclaim memory after each LVN.
+        LocalValueNumbering* lvn = temp_gvn_->PrepareBasicBlock(bb, &allocator);
+        if (lvn != nullptr) {
+          for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+            lvn->GetValueNumber(mir);
+          }
+          bool change = temp_gvn_->FinishBasicBlock(bb);
+          DCHECK(!change) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
         }
-        bool change = temp_gvn_->FinishBasicBlock(bb);
-        DCHECK(!change) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
       }
     }
+    // GVN was successful, running the LVN would be useless.
+    cu_->disable_opt |= (1u << kLocalValueNumbering);
   } else {
     LOG(WARNING) << "GVN failed for " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
   }
@@ -1209,8 +1400,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 +1409,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 +1425,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 +1441,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.IsClassInitialized() &&
+          ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 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)
@@ -1309,7 +1509,7 @@
   }
 }
 
-bool MIRGraph::BuildExtendedBBList(struct BasicBlock* bb) {
+bool MIRGraph::BuildExtendedBBList(class BasicBlock* bb) {
   if (bb->visited) return false;
   if (!((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode)
       || (bb->block_type == kExitBlock))) {
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index c510b52..8874faf 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -23,15 +23,8 @@
 
 namespace art {
 
-class ClassInitCheckEliminationTest : public testing::Test {
+class MirOptimizationTest : public testing::Test {
  protected:
-  struct SFieldDef {
-    uint16_t field_idx;
-    uintptr_t declaring_dex_file;
-    uint16_t declaring_class_idx;
-    uint16_t declaring_field_idx;
-  };
-
   struct BBDef {
     static constexpr size_t kMaxSuccessors = 4;
     static constexpr size_t kMaxPredecessors = 4;
@@ -43,10 +36,24 @@
     BasicBlockId predecessors[kMaxPredecessors];
   };
 
+  struct MethodDef {
+    uint16_t method_idx;
+    uintptr_t declaring_dex_file;
+    uint16_t declaring_class_idx;
+    uint16_t declaring_method_idx;
+    InvokeType invoke_type;
+    InvokeType sharp_type;
+    bool is_referrers_class;
+    bool is_initialized;
+  };
+
   struct MIRDef {
-    Instruction::Code opcode;
     BasicBlockId bbid;
+    Instruction::Code opcode;
     uint32_t field_or_method_info;
+    uint32_t vA;
+    uint32_t vB;
+    uint32_t vC;
   };
 
 #define DEF_SUCC0() \
@@ -72,79 +79,58 @@
 #define DEF_BB(type, succ, pred) \
     { type, succ, pred }
 
-#define DEF_MIR(opcode, bb, field_info) \
-    { 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);
-    for (size_t i = 0u; i != count; ++i) {
-      const SFieldDef* def = &defs[i];
-      MirSFieldLoweringInfo field_info(def->field_idx);
-      if (def->declaring_dex_file != 0u) {
-        field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
-        field_info.declaring_class_idx_ = def->declaring_class_idx;
-        field_info.declaring_field_idx_ = def->declaring_field_idx;
-        field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic;
-      }
-      ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
-      ASSERT_FALSE(field_info.IsInitialized());
-      cu_.mir_graph->sfield_lowering_infos_.Insert(field_info);
-    }
-  }
-
-  template <size_t count>
-  void PrepareSFields(const SFieldDef (&defs)[count]) {
-    DoPrepareSFields(defs, count);
-  }
+#define DEF_SGET_SPUT(bb, opcode, vA, field_info) \
+    { bb, opcode, field_info, vA, 0u, 0u }
+#define DEF_IGET_IPUT(bb, opcode, vA, vB, field_info) \
+    { bb, opcode, field_info, vA, vB, 0u }
+#define DEF_AGET_APUT(bb, opcode, vA, vB, vC) \
+    { bb, opcode, 0u, vA, vB, vC }
+#define DEF_INVOKE(bb, opcode, vC, method_info) \
+    { bb, opcode, method_info, 0u, 0u, vC }
+#define DEF_OTHER1(bb, opcode, vA) \
+    { bb, opcode, 0u, vA, 0u, 0u }
+#define DEF_OTHER2(bb, opcode, vA, vB) \
+    { bb, opcode, 0u, vA, vB, 0u }
 
   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);
   }
 
@@ -153,6 +139,92 @@
     DoPrepareBasicBlocks(defs, count);
   }
 
+  void PrepareSingleBlock() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)),
+    };
+    PrepareBasicBlocks(bbs);
+  }
+
+  void PrepareDiamond() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),
+    };
+    PrepareBasicBlocks(bbs);
+  }
+
+  void PrepareLoop() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)),  // "taken" loops to self.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+    };
+    PrepareBasicBlocks(bbs);
+  }
+
+  void PrepareCatch() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),     // The top.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // The throwing insn.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // Catch handler.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),  // The merged block.
+    };
+    PrepareBasicBlocks(bbs);
+    BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
+    catch_handler->catch_entry = true;
+    // Add successor block info to the check block.
+    BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
+    check_bb->successor_block_list_type = kCatch;
+    SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
+        (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+    successor_block_info->block = catch_handler->id;
+    check_bb->successor_blocks.push_back(successor_block_info);
+  }
+
+  void DoPrepareMethods(const MethodDef* defs, size_t count) {
+    cu_.mir_graph->method_lowering_infos_.clear();
+    cu_.mir_graph->method_lowering_infos_.reserve(count);
+    for (size_t i = 0u; i != count; ++i) {
+      const MethodDef* def = &defs[i];
+      MirMethodLoweringInfo method_info(def->method_idx, def->invoke_type);
+      if (def->declaring_dex_file != 0u) {
+        method_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
+        method_info.declaring_class_idx_ = def->declaring_class_idx;
+        method_info.declaring_method_idx_ = def->declaring_method_idx;
+      }
+      ASSERT_EQ(def->invoke_type != kStatic, def->sharp_type != kStatic);
+      method_info.flags_ =
+          ((def->invoke_type == kStatic) ? MirMethodLoweringInfo::kFlagIsStatic : 0u) |
+          MirMethodLoweringInfo::kFlagFastPath |
+          (static_cast<uint16_t>(def->invoke_type) << MirMethodLoweringInfo::kBitInvokeTypeBegin) |
+          (static_cast<uint16_t>(def->sharp_type) << MirMethodLoweringInfo::kBitSharpTypeBegin) |
+          ((def->is_referrers_class) ? MirMethodLoweringInfo::kFlagIsReferrersClass : 0u) |
+          ((def->is_initialized == kStatic) ? MirMethodLoweringInfo::kFlagClassIsInitialized : 0u);
+      ASSERT_EQ(def->declaring_dex_file != 0u, method_info.IsResolved());
+      cu_.mir_graph->method_lowering_infos_.push_back(method_info);
+    }
+  }
+
+  template <size_t count>
+  void PrepareMethods(const MethodDef (&defs)[count]) {
+    DoPrepareMethods(defs, count);
+  }
+
   void DoPrepareMIRs(const MIRDef* defs, size_t count) {
     mir_count_ = count;
     mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR));
@@ -161,13 +233,24 @@
       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;
+      } else if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) {
+        ASSERT_LT(def->field_or_method_info, cu_.mir_graph->ifield_lowering_infos_.size());
+        mir->meta.ifield_lowering_info = def->field_or_method_info;
+      } else if (def->opcode >= Instruction::INVOKE_VIRTUAL &&
+          def->opcode < Instruction::INVOKE_INTERFACE_RANGE &&
+          def->opcode != Instruction::RETURN_VOID_BARRIER) {
+        ASSERT_LT(def->field_or_method_info, cu_.mir_graph->method_lowering_infos_.size());
+        mir->meta.method_lowering_info = def->field_or_method_info;
       }
+      mir->dalvikInsn.vA = def->vA;
+      mir->dalvikInsn.vB = def->vB;
+      mir->dalvikInsn.vC = def->vC;
       mir->ssa_rep = nullptr;
       mir->offset = 2 * i;  // All insns need to be at least 2 code units long.
       mir->optimization_flags = 0u;
@@ -179,7 +262,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>
@@ -187,15 +270,60 @@
     DoPrepareMIRs(defs, count);
   }
 
+  MirOptimizationTest()
+      : pool_(),
+        cu_(&pool_),
+        mir_count_(0u),
+        mirs_(nullptr),
+        code_item_(nullptr) {
+    cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
+    cu_.access_flags = kAccStatic;  // Don't let "this" interfere with this test.
+  }
+
+  ArenaPool pool_;
+  CompilationUnit cu_;
+  size_t mir_count_;
+  MIR* mirs_;
+  DexFile::CodeItem* code_item_;
+};
+
+class ClassInitCheckEliminationTest : public MirOptimizationTest {
+ protected:
+  struct SFieldDef {
+    uint16_t field_idx;
+    uintptr_t declaring_dex_file;
+    uint16_t declaring_class_idx;
+    uint16_t declaring_field_idx;
+  };
+
+  void DoPrepareSFields(const SFieldDef* defs, size_t 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);
+      if (def->declaring_dex_file != 0u) {
+        field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
+        field_info.declaring_class_idx_ = def->declaring_class_idx;
+        field_info.declaring_field_idx_ = def->declaring_field_idx;
+        field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic;
+      }
+      ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
+      ASSERT_FALSE(field_info.IsClassInitialized());
+      cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
+    }
+  }
+
+  template <size_t count>
+  void PrepareSFields(const SFieldDef (&defs)[count]) {
+    DoPrepareSFields(defs, count);
+  }
+
   void PerformClassInitCheckElimination() {
-    cu_.mir_graph->SSATransformationStart();
     cu_.mir_graph->ComputeDFSOrders();
-    cu_.mir_graph->ComputeDominators();
-    cu_.mir_graph->ComputeTopologicalSortOrder();
-    cu_.mir_graph->SSATransformationEnd();
     bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate();
     ASSERT_TRUE(gate_result);
-    LoopRepeatingTopologicalSortIterator iterator(cu_.mir_graph.get());
+    RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
     bool change = false;
     for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
       change = cu_.mir_graph->EliminateClassInitChecks(bb);
@@ -204,19 +332,63 @@
   }
 
   ClassInitCheckEliminationTest()
-      : pool_(),
-        cu_(&pool_),
-        mir_count_(0u),
-        mirs_(nullptr),
-        code_item_(nullptr) {
-    cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
+      : MirOptimizationTest() {
+  }
+};
+
+class NullCheckEliminationTest : public MirOptimizationTest {
+ protected:
+  struct IFieldDef {
+    uint16_t field_idx;
+    uintptr_t declaring_dex_file;
+    uint16_t declaring_class_idx;
+    uint16_t declaring_field_idx;
+  };
+
+  void DoPrepareIFields(const IFieldDef* defs, size_t 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);
+      if (def->declaring_dex_file != 0u) {
+        field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
+        field_info.declaring_class_idx_ = def->declaring_class_idx;
+        field_info.declaring_field_idx_ = def->declaring_field_idx;
+      }
+      ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
+      cu_.mir_graph->ifield_lowering_infos_.push_back(field_info);
+    }
   }
 
-  ArenaPool pool_;
-  CompilationUnit cu_;
-  size_t mir_count_;
-  MIR* mirs_;
-  DexFile::CodeItem* code_item_;
+  template <size_t count>
+  void PrepareIFields(const IFieldDef (&defs)[count]) {
+    DoPrepareIFields(defs, count);
+  }
+
+  void PerformNullCheckElimination() {
+    // Make vregs in range [100, 1000) input registers, i.e. requiring a null check.
+    code_item_->registers_size_ = 1000;
+    code_item_->ins_size_ = 900;
+
+    cu_.mir_graph->ComputeDFSOrders();
+    bool gate_result = cu_.mir_graph->EliminateNullChecksGate();
+    ASSERT_TRUE(gate_result);
+    RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
+    bool change = false;
+    for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
+      change = cu_.mir_graph->EliminateNullChecks(bb);
+    }
+    cu_.mir_graph->EliminateNullChecksEnd();
+  }
+
+  NullCheckEliminationTest()
+      : MirOptimizationTest() {
+    static const MethodDef methods[] = {
+        { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false },  // Dummy.
+    };
+    PrepareMethods(methods);
+  }
 };
 
 TEST_F(ClassInitCheckEliminationTest, SingleBlock) {
@@ -228,37 +400,74 @@
       { 4u, 1u, 3u, 4u },  // Same declaring class as sfield[3].
       { 5u, 0u, 0u, 0u },  // Unresolved.
   };
-  static const BBDef bbs[] = {
-      DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
-      DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
-      DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)),
-  };
   static const MIRDef mirs[] = {
-      DEF_MIR(Instruction::SPUT, 3u, 5u),  // Unresolved.
-      DEF_MIR(Instruction::SPUT, 3u, 0u),
-      DEF_MIR(Instruction::SGET, 3u, 1u),
-      DEF_MIR(Instruction::SGET, 3u, 2u),
-      DEF_MIR(Instruction::SGET, 3u, 5u),  // Unresolved.
-      DEF_MIR(Instruction::SGET, 3u, 0u),
-      DEF_MIR(Instruction::SGET, 3u, 1u),
-      DEF_MIR(Instruction::SGET, 3u, 2u),
-      DEF_MIR(Instruction::SGET, 3u, 5u),  // Unresolved.
-      DEF_MIR(Instruction::SGET, 3u, 3u),
-      DEF_MIR(Instruction::SGET, 3u, 4u),
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 5u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u),
   };
   static const bool expected_ignore_clinit_check[] = {
       false, false, false, false, true, true, true, true, true, false, true
   };
 
   PrepareSFields(sfields);
-  PrepareBasicBlocks(bbs);
+  PrepareSingleBlock();
   PrepareMIRs(mirs);
   PerformClassInitCheckElimination();
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
   for (size_t i = 0u; i != arraysize(mirs); ++i) {
     EXPECT_EQ(expected_ignore_clinit_check[i],
-              (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_ignore_clinit_check[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
+  }
+}
+
+TEST_F(ClassInitCheckEliminationTest, SingleBlockWithInvokes) {
+  static const SFieldDef sfields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 1u, 1u },
+      { 2u, 1u, 2u, 2u },
+  };
+  static const MethodDef methods[] = {
+      { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false },
+      { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false },
+      { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false },
+  };
+  static const MIRDef mirs[] = {
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+  };
+  static const bool expected_class_initialized[] = {
+      false, true, false, true, false, true
+  };
+  static const bool expected_class_in_dex_cache[] = {
+      false, false, false, false, false, false
+  };
+
+  PrepareSFields(sfields);
+  PrepareMethods(methods);
+  PrepareSingleBlock();
+  PrepareMIRs(mirs);
+  PerformClassInitCheckElimination();
+  ASSERT_EQ(arraysize(expected_class_initialized), mir_count_);
+  ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_class_initialized[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_class_in_dex_cache[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
   }
 }
 
@@ -276,43 +485,34 @@
       { 9u, 1u, 8u, 9u },  // Same declaring class as sfield[8].
       { 10u, 0u, 0u, 0u },  // Unresolved.
   };
-  static const BBDef bbs[] = {
-      DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
-      DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
-      DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),
-  };
   static const MIRDef mirs[] = {
       // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
-      DEF_MIR(Instruction::SGET, 3u, 10u),  // Unresolved.
-      DEF_MIR(Instruction::SPUT, 3u, 10u),  // Unresolved.
-      DEF_MIR(Instruction::SPUT, 3u, 0u),
-      DEF_MIR(Instruction::SGET, 6u, 0u),  // Eliminated (block #3 dominates #6).
-      DEF_MIR(Instruction::SPUT, 4u, 1u),
-      DEF_MIR(Instruction::SGET, 6u, 1u),  // Not eliminated (block #4 doesn't dominate #6).
-      DEF_MIR(Instruction::SGET, 3u, 2u),
-      DEF_MIR(Instruction::SGET, 4u, 2u),  // Eliminated (block #3 dominates #4).
-      DEF_MIR(Instruction::SGET, 3u, 3u),
-      DEF_MIR(Instruction::SGET, 5u, 3u),  // Eliminated (block #3 dominates #5).
-      DEF_MIR(Instruction::SGET, 3u, 4u),
-      DEF_MIR(Instruction::SGET, 6u, 4u),  // Eliminated (block #3 dominates #6).
-      DEF_MIR(Instruction::SGET, 4u, 5u),
-      DEF_MIR(Instruction::SGET, 6u, 5u),  // Not eliminated (block #4 doesn't dominate #6).
-      DEF_MIR(Instruction::SGET, 5u, 6u),
-      DEF_MIR(Instruction::SGET, 6u, 6u),  // Not eliminated (block #5 doesn't dominate #6).
-      DEF_MIR(Instruction::SGET, 4u, 7u),
-      DEF_MIR(Instruction::SGET, 5u, 7u),
-      DEF_MIR(Instruction::SGET, 6u, 7u),  // Eliminated (initialized in both blocks #3 and #4).
-      DEF_MIR(Instruction::SGET, 4u, 8u),
-      DEF_MIR(Instruction::SGET, 5u, 9u),
-      DEF_MIR(Instruction::SGET, 6u, 8u),  // Eliminated (with sfield[9] in block #5).
-      DEF_MIR(Instruction::SPUT, 6u, 9u),  // Eliminated (with sfield[8] in block #4).
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 10u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 10u),  // Unresolved.
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u),  // Eliminated (BB #3 dominates #6).
+      DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 1u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u),  // Not eliminated (BB #4 doesn't dominate #6).
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u),
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),  // Eliminated (BB #3 dominates #4).
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 3u),  // Eliminated (BB #3 dominates #5).
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 4u),  // Eliminated (BB #3 dominates #6).
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 5u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 5u),  // Not eliminated (BB #4 doesn't dominate #6).
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 6u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 6u),  // Not eliminated (BB #5 doesn't dominate #6).
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 7u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 7u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 7u),  // Eliminated (initialized in both #3 and #4).
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 8u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 9u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 8u),  // Eliminated (with sfield[9] in BB #5).
+      DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 9u),  // Eliminated (with sfield[8] in BB #4).
   };
   static const bool expected_ignore_clinit_check[] = {
-      false, true,          // Unresolved: sfield[10], method[2]
+      false, true,          // Unresolved: sfield[10]
       false, true,          // sfield[0]
       false, false,         // sfield[1]
       false, true,          // sfield[2]
@@ -325,13 +525,76 @@
   };
 
   PrepareSFields(sfields);
-  PrepareBasicBlocks(bbs);
+  PrepareDiamond();
   PrepareMIRs(mirs);
   PerformClassInitCheckElimination();
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
   for (size_t i = 0u; i != arraysize(mirs); ++i) {
     EXPECT_EQ(expected_ignore_clinit_check[i],
-              (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_ignore_clinit_check[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
+  }
+}
+
+TEST_F(ClassInitCheckEliminationTest, DiamondWithInvokes) {
+  static const SFieldDef sfields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 1u, 1u },
+      { 2u, 1u, 2u, 2u },
+      { 3u, 1u, 3u, 3u },
+      { 4u, 1u, 4u, 4u },
+  };
+  static const MethodDef methods[] = {
+      { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false },
+      { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false },
+      { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false },
+      { 3u, 1u, 3u, 3u, kStatic, kStatic, false, false },
+      { 4u, 1u, 4u, 4u, kStatic, kStatic, false, false },
+  };
+  static const MIRDef mirs[] = {
+      // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
+      DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u),
+      DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
+      DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 1u),
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),
+      DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+      DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 2u),
+      DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 3u),
+      DEF_SGET_SPUT(5u, Instruction::SPUT, 0u, 3u),
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u),
+      DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 4u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 4u),
+      DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 4u),
+  };
+  static const bool expected_class_initialized[] = {
+      false, true,    // BB #3 SPUT, BB#6 INVOKE_STATIC
+      false, true,    // BB #3 INVOKE_STATIC, BB#6 SPUT
+      false, false, true,   // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT
+      false, false, true,   // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET
+      false, false, true,   // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC
+  };
+  static const bool expected_class_in_dex_cache[] = {
+      false, false,   // BB #3 SPUT, BB#6 INVOKE_STATIC
+      false, false,   // BB #3 INVOKE_STATIC, BB#6 SPUT
+      false, false, false,  // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT
+      false, false, false,  // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET
+      false, false, false,  // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC
+  };
+
+  PrepareSFields(sfields);
+  PrepareMethods(methods);
+  PrepareDiamond();
+  PrepareMIRs(mirs);
+  PerformClassInitCheckElimination();
+  ASSERT_EQ(arraysize(expected_class_initialized), mir_count_);
+  ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_class_initialized[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_class_in_dex_cache[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
   }
 }
 
@@ -339,33 +602,70 @@
   static const SFieldDef sfields[] = {
       { 0u, 1u, 0u, 0u },
       { 1u, 1u, 1u, 1u },
-  };
-  static const BBDef bbs[] = {
-      DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
-      DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
-      DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)),  // "taken" loops to self.
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+      { 2u, 1u, 2u, 2u },
   };
   static const MIRDef mirs[] = {
-      DEF_MIR(Instruction::SGET, 3u, 0u),
-      DEF_MIR(Instruction::SGET, 4u, 1u),
-      DEF_MIR(Instruction::SGET, 5u, 0u),  // Eliminated.
-      DEF_MIR(Instruction::SGET, 5u, 1u),  // Eliminated.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 0u),  // Eliminated.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 1u),  // Eliminated.
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u),  // Eliminated.
   };
   static const bool expected_ignore_clinit_check[] = {
-      false, false, true, true
+      false, true, false, true, false, true,
   };
 
   PrepareSFields(sfields);
-  PrepareBasicBlocks(bbs);
+  PrepareLoop();
   PrepareMIRs(mirs);
   PerformClassInitCheckElimination();
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
   for (size_t i = 0u; i != arraysize(mirs); ++i) {
     EXPECT_EQ(expected_ignore_clinit_check[i],
-              (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_ignore_clinit_check[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
+  }
+}
+
+TEST_F(ClassInitCheckEliminationTest, LoopWithInvokes) {
+  static const SFieldDef sfields[] = {
+      { 0u, 1u, 0u, 0u },
+  };
+  static const MethodDef methods[] = {
+      { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false },
+      { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false },
+      { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false },
+  };
+  static const MIRDef mirs[] = {
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
+      DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
+      DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u),
+      DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+      DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u),
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u),
+  };
+  static const bool expected_class_initialized[] = {
+      false, true, false, true, false, true, true,
+  };
+  static const bool expected_class_in_dex_cache[] = {
+      false, false, false, false, false, false, false,
+  };
+
+  PrepareSFields(sfields);
+  PrepareMethods(methods);
+  PrepareLoop();
+  PrepareMIRs(mirs);
+  PerformClassInitCheckElimination();
+  ASSERT_EQ(arraysize(expected_class_initialized), mir_count_);
+  ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_class_initialized[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_class_in_dex_cache[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
   }
 }
 
@@ -376,51 +676,207 @@
       { 2u, 1u, 2u, 2u },
       { 3u, 1u, 3u, 3u },
   };
-  static const BBDef bbs[] = {
-      DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
-      DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
-      DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),     // The top.
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // The throwing insn.
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),     // Catch handler.
-      DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),  // The merged block.
-  };
   static const MIRDef mirs[] = {
-      DEF_MIR(Instruction::SGET, 3u, 0u),  // Before the exception edge.
-      DEF_MIR(Instruction::SGET, 3u, 1u),  // Before the exception edge.
-      DEF_MIR(Instruction::SGET, 4u, 2u),  // After the exception edge.
-      DEF_MIR(Instruction::SGET, 4u, 3u),  // After the exception edge.
-      DEF_MIR(Instruction::SGET, 5u, 0u),  // In catch handler; class init check eliminated.
-      DEF_MIR(Instruction::SGET, 5u, 2u),  // In catch handler; class init check not eliminated.
-      DEF_MIR(Instruction::SGET, 6u, 0u),  // Class init check eliminated.
-      DEF_MIR(Instruction::SGET, 6u, 1u),  // Class init check eliminated.
-      DEF_MIR(Instruction::SGET, 6u, 2u),  // Class init check eliminated.
-      DEF_MIR(Instruction::SGET, 6u, 3u),  // Class init check not eliminated.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u),  // Before the exception edge.
+      DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u),  // Before the exception edge.
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u),  // After the exception edge.
+      DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 3u),  // After the exception edge.
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u),  // In catch handler; eliminated.
+      DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u),  // In catch handler; not eliminated.
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u),  // Class init check eliminated.
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u),  // Class init check eliminated.
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 2u),  // Class init check eliminated.
+      DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u),  // Class init check not eliminated.
   };
   static const bool expected_ignore_clinit_check[] = {
       false, false, false, false, true, false, true, true, true, false
   };
 
   PrepareSFields(sfields);
-  PrepareBasicBlocks(bbs);
-  BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
-  catch_handler->catch_entry = true;
-  // 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);
+  PrepareCatch();
   PrepareMIRs(mirs);
   PerformClassInitCheckElimination();
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
   for (size_t i = 0u; i != arraysize(mirs); ++i) {
     EXPECT_EQ(expected_ignore_clinit_check[i],
-              (mirs_[i].optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0) << i;
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i;
+    EXPECT_EQ(expected_ignore_clinit_check[i],
+              (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i;
   }
 }
 
+TEST_F(NullCheckEliminationTest, SingleBlock) {
+  static const IFieldDef ifields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 0u, 1u },
+      { 2u, 1u, 0u, 2u },  // Object.
+  };
+  static const MIRDef mirs[] = {
+      DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 0u, 100u, 2u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 0u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 2u, 100u, 2u),  // Differs from 0u (no LVN here).
+      DEF_IGET_IPUT(3u, Instruction::IGET, 3u, 2u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 101u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 5u, 102u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 6u, 103u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 7u, 103u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 8u, 104u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 9u, 104u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 10u, 105u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 11u, 105u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 12u, 106u, 0u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 13u, 106u, 1u),
+      DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 107, 0u /* dummy */),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 15u, 107u, 1u),
+      DEF_IGET_IPUT(3u, Instruction::IGET, 16u, 108u, 0u),
+      DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 108, 0u /* dummy */),
+      DEF_AGET_APUT(3u, Instruction::AGET, 18u, 109u, 110u),
+      DEF_AGET_APUT(3u, Instruction::APUT, 19u, 109u, 111u),
+      DEF_OTHER2(3u, Instruction::ARRAY_LENGTH, 20u, 112u),
+      DEF_AGET_APUT(3u, Instruction::AGET, 21u, 112u, 113u),
+      DEF_OTHER1(3u, Instruction::MONITOR_ENTER, 114u),
+      DEF_OTHER1(3u, Instruction::MONITOR_EXIT, 114u),
+  };
+  static const bool expected_ignore_null_check[] = {
+      false, false, true, false /* Not doing LVN. */,
+      false, true /* Set before running NCE. */,
+      false, true,  // IGET, IGET
+      false, true,  // IPUT, IPUT
+      false, true,  // IGET, IPUT
+      false, true,  // IPUT, IGET
+      false, true,  // INVOKE, IGET
+      false, true,  // IGET, INVOKE
+      false, true,  // AGET, APUT
+      false, true,  // ARRAY_LENGTH, AGET
+      false, true,  // MONITOR_ENTER, MONITOR_EXIT
+  };
+
+  PrepareIFields(ifields);
+  PrepareSingleBlock();
+  PrepareMIRs(mirs);
+
+  // Mark IGET 5u as null-checked to test that NCE doesn't clear this flag.
+  mirs_[5u].optimization_flags |= MIR_IGNORE_NULL_CHECK;
+
+  PerformNullCheckElimination();
+  ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_ignore_null_check[i],
+              (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+  }
+}
+
+TEST_F(NullCheckEliminationTest, Diamond) {
+  static const IFieldDef ifields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 0u, 1u },
+      { 2u, 1u, 0u, 2u },  // int[].
+  };
+  static const MIRDef mirs[] = {
+      // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
+      DEF_IGET_IPUT(3u, Instruction::IPUT, 0u, 100u, 0u),
+      DEF_IGET_IPUT(6u, Instruction::IGET, 1u, 100u, 1u),  // Eliminated (BB #3 dominates #6).
+      DEF_IGET_IPUT(3u, Instruction::IGET, 2u, 101u, 0u),
+      DEF_IGET_IPUT(4u, Instruction::IPUT, 3u, 101u, 0u),  // Eliminated (BB #3 dominates #4).
+      DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
+      DEF_IGET_IPUT(5u, Instruction::IPUT, 5u, 102u, 1u),  // Eliminated (BB #3 dominates #5).
+      DEF_IGET_IPUT(4u, Instruction::IPUT, 6u, 103u, 0u),
+      DEF_IGET_IPUT(6u, Instruction::IPUT, 7u, 103u, 1u),  // Not eliminated (going through BB #5).
+      DEF_IGET_IPUT(5u, Instruction::IGET, 8u, 104u, 1u),
+      DEF_IGET_IPUT(6u, Instruction::IGET, 9u, 104u, 0u),  // Not eliminated (going through BB #4).
+      DEF_INVOKE(4u, Instruction::INVOKE_DIRECT, 105u, 0u /* dummy */),
+      DEF_IGET_IPUT(5u, Instruction::IGET, 11u, 105u, 1u),
+      DEF_IGET_IPUT(6u, Instruction::IPUT, 12u, 105u, 0u),  // Eliminated.
+      DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 13u, 106u, 2u),
+      DEF_OTHER1(3u, Instruction::IF_EQZ, 13u),            // Last insn in the BB #3.
+      DEF_OTHER2(5u, Instruction::NEW_ARRAY, 13u, 107u),
+      DEF_AGET_APUT(6u, Instruction::AGET, 16u, 13u, 108u),  // Eliminated.
+  };
+  static const bool expected_ignore_null_check[] = {
+      false, true,   // BB #3 IPUT, BB #6 IGET
+      false, true,   // BB #3 IGET, BB #4 IPUT
+      false, true,   // BB #3 IGET, BB #5 IPUT
+      false, false,  // BB #4 IPUT, BB #6 IPUT
+      false, false,  // BB #5 IGET, BB #6 IGET
+      false, false, true,  // BB #4 INVOKE, BB #5 IGET, BB #6 IPUT
+      false, false,  // BB #3 IGET_OBJECT & IF_EQZ
+      false, true,   // BB #5 NEW_ARRAY, BB #6 AGET
+  };
+
+  PrepareIFields(ifields);
+  PrepareDiamond();
+  PrepareMIRs(mirs);
+  PerformNullCheckElimination();
+  ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_ignore_null_check[i],
+              (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+  }
+}
+
+TEST_F(NullCheckEliminationTest, Loop) {
+  static const IFieldDef ifields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 1u, 1u },
+  };
+  static const MIRDef mirs[] = {
+      DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u),
+      DEF_IGET_IPUT(4u, Instruction::IGET, 1u, 101u, 0u),
+      DEF_IGET_IPUT(5u, Instruction::IGET, 2u, 100u, 1u),  // Eliminated.
+      DEF_IGET_IPUT(5u, Instruction::IGET, 3u, 101u, 1u),  // Eliminated.
+      DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
+      DEF_IGET_IPUT(4u, Instruction::IGET, 5u, 102u, 1u),  // Not eliminated (MOVE_OBJECT_16).
+      DEF_OTHER2(4u, Instruction::MOVE_OBJECT_16, 102u, 103u),
+  };
+  static const bool expected_ignore_null_check[] = {
+      false, false, true, true,
+      false, false, false,
+  };
+
+  PrepareIFields(ifields);
+  PrepareLoop();
+  PrepareMIRs(mirs);
+  PerformNullCheckElimination();
+  ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_ignore_null_check[i],
+              (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+  }
+}
+
+TEST_F(NullCheckEliminationTest, Catch) {
+  static const IFieldDef ifields[] = {
+      { 0u, 1u, 0u, 0u },
+      { 1u, 1u, 1u, 1u },
+  };
+  static const MIRDef mirs[] = {
+      DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u),  // Before the exception edge.
+      DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 101u, 0u),  // Before the exception edge.
+      DEF_IGET_IPUT(4u, Instruction::IGET, 2u, 102u, 0u),  // After the exception edge.
+      DEF_IGET_IPUT(4u, Instruction::IGET, 3u, 103u, 0u),  // After the exception edge.
+      DEF_IGET_IPUT(5u, Instruction::IGET, 4u, 100u, 1u),  // In catch handler; eliminated.
+      DEF_IGET_IPUT(5u, Instruction::IGET, 5u, 102u, 1u),  // In catch handler; not eliminated.
+      DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 100u, 0u),  // Null check eliminated.
+      DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 101u, 1u),  // Null check eliminated.
+      DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 102u, 0u),  // Null check eliminated.
+      DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 103u, 1u),  // Null check not eliminated.
+  };
+  static const bool expected_ignore_null_check[] = {
+      false, false, false, false, true, false, true, true, true, false
+  };
+
+  PrepareIFields(ifields);
+  PrepareCatch();
+  PrepareMIRs(mirs);
+  PerformNullCheckElimination();
+  ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+  for (size_t i = 0u; i != arraysize(mirs); ++i) {
+    EXPECT_EQ(expected_ignore_null_check[i],
+              (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+  }
+}
+
+// Undefine MIR_DEF for null check elimination.
+#undef MIR_DEF
+
 }  // namespace art
diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h
index dbb5366..d3e54a0 100644
--- a/compiler/dex/pass.h
+++ b/compiler/dex/pass.h
@@ -19,11 +19,13 @@
 
 #include <string>
 
-#include "base/macros.h"
+#include "compiler_ir.h"
+#include "base/logging.h"
+
 namespace art {
 
 // Forward declarations.
-struct BasicBlock;
+class BasicBlock;
 struct CompilationUnit;
 class Pass;
 
@@ -81,12 +83,10 @@
    * @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 {
-    // Unused parameter.
-    UNUSED(data);
-
-    // BasicBlock did not change.
-    return false;
+  virtual bool Worker(PassDataHolder* data ATTRIBUTE_UNUSED) const {
+    // Passes that do all their work in Start() or End() should not allow useless node iteration.
+    LOG(FATAL) << "Unsupported default Worker() used for " << GetName();
+    UNREACHABLE();
   }
 
   static void BasePrintMessage(CompilationUnit* c_unit, const char* pass_name, const char* message, ...) {
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..7bfaf82 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"
@@ -65,9 +67,6 @@
       case kTopologicalSortTraversal:
         DoWalkBasicBlocks<TopologicalSortIterator>(&pass_me_data_holder_, me_pass);
         break;
-      case kRepeatingTopologicalSortTraversal:
-        DoWalkBasicBlocks<RepeatingTopologicalSortIterator>(&pass_me_data_holder_, me_pass);
-        break;
       case kLoopRepeatingTopologicalSortTraversal:
         DoWalkBasicBlocks<LoopRepeatingTopologicalSortIterator>(&pass_me_data_holder_, me_pass);
         break;
@@ -94,19 +93,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 +144,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 +157,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 +195,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..a2bf8b4 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -20,6 +20,7 @@
 #include "dataflow_iterator.h"
 #include "dataflow_iterator-inl.h"
 #include "pass_driver_me_opts.h"
+#include "post_opt_passes.h"
 
 namespace art {
 
@@ -35,12 +36,15 @@
 const Pass* const PassDriver<PassDriverMEOpts>::g_passes[] = {
   GetPassInstance<CacheFieldLoweringInfo>(),
   GetPassInstance<CacheMethodLoweringInfo>(),
-  GetPassInstance<SpecialMethodInliner>(),
-  GetPassInstance<CodeLayout>(),
-  GetPassInstance<NullCheckEliminationAndTypeInference>(),
+  GetPassInstance<CalculatePredecessors>(),
+  GetPassInstance<DFSOrders>(),
   GetPassInstance<ClassInitCheckElimination>(),
-  GetPassInstance<GlobalValueNumberingPass>(),
+  GetPassInstance<SpecialMethodInliner>(),
+  GetPassInstance<NullCheckElimination>(),
   GetPassInstance<BBCombine>(),
+  GetPassInstance<CodeLayout>(),
+  GetPassInstance<TypeInference>(),
+  GetPassInstance<GlobalValueNumberingPass>(),
   GetPassInstance<BBOptimizations>(),
 };
 
@@ -69,20 +73,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..e6238e9 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -33,7 +33,6 @@
 const Pass* const PassDriver<PassDriverMEPostOpt>::g_passes[] = {
   GetPassInstance<InitializeData>(),
   GetPassInstance<ClearPhiInstructions>(),
-  GetPassInstance<CalculatePredecessors>(),
   GetPassInstance<DFSOrders>(),
   GetPassInstance<BuildDomination>(),
   GetPassInstance<TopologicalSortOrders>(),
@@ -73,4 +72,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..d0b450a 100644
--- a/compiler/dex/pass_me.h
+++ b/compiler/dex/pass_me.h
@@ -23,7 +23,7 @@
 namespace art {
 
 // Forward declarations.
-struct BasicBlock;
+class BasicBlock;
 struct CompilationUnit;
 class Pass;
 
@@ -32,39 +32,41 @@
  * @details Each enum should be a power of 2 to be correctly used.
  */
 enum OptimizationFlag {
-  kOptimizationBasicBlockChange = 1,  /**< @brief Has there been a change to a BasicBlock? */
-  kOptimizationDefUsesChange = 2,     /**< @brief Has there been a change to a def-use? */
-  kLoopStructureChange = 4,           /**< @brief Has there been a loop structural change? */
+  kOptimizationBasicBlockChange = 1,  /// @brief Has there been a change to a BasicBlock?
+  kOptimizationDefUsesChange = 2,     /// @brief Has there been a change to a def-use?
+  kLoopStructureChange = 4,           /// @brief Has there been a loop structural change?
 };
+std::ostream& operator<<(std::ostream& os, const OptimizationFlag& rhs);
 
 // Data holder class.
 class PassMEDataHolder: public PassDataHolder {
   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 {
-  kAllNodes = 0,                           /**< @brief All nodes. */
-  kPreOrderDFSTraversal,                   /**< @brief Depth-First-Search / Pre-Order. */
-  kRepeatingPreOrderDFSTraversal,          /**< @brief Depth-First-Search / Repeating Pre-Order. */
-  kReversePostOrderDFSTraversal,           /**< @brief Depth-First-Search / Reverse Post-Order. */
-  kRepeatingPostOrderDFSTraversal,         /**< @brief Depth-First-Search / Repeating Post-Order. */
-  kRepeatingReversePostOrderDFSTraversal,  /**< @brief Depth-First-Search / Repeating Reverse Post-Order. */
-  kPostOrderDOMTraversal,                  /**< @brief Dominator tree / Post-Order. */
-  kTopologicalSortTraversal,               /**< @brief Topological Order traversal. */
-  kRepeatingTopologicalSortTraversal,      /**< @brief Repeating Topological Order traversal. */
-  kLoopRepeatingTopologicalSortTraversal,  /**< @brief Loop-repeating Topological Order traversal. */
-  kNoNodes,                                /**< @brief Skip BasicBlock traversal. */
+  kAllNodes = 0,                           /// @brief All nodes.
+  kPreOrderDFSTraversal,                   /// @brief Depth-First-Search / Pre-Order.
+  kRepeatingPreOrderDFSTraversal,          /// @brief Depth-First-Search / Repeating Pre-Order.
+  kReversePostOrderDFSTraversal,           /// @brief Depth-First-Search / Reverse Post-Order.
+  kRepeatingPostOrderDFSTraversal,         /// @brief Depth-First-Search / Repeating Post-Order.
+  kRepeatingReversePostOrderDFSTraversal,  /// @brief Depth-First-Search / Repeating Reverse Post-Order.
+  kPostOrderDOMTraversal,                  /// @brief Dominator tree / Post-Order.
+  kTopologicalSortTraversal,               /// @brief Topological Order traversal.
+  kLoopRepeatingTopologicalSortTraversal,  /// @brief Loop-repeating Topological Order traversal.
+  kNoNodes,                                /// @brief Skip BasicBlock traversal.
 };
+std::ostream& operator<<(std::ostream& os, const DataFlowAnalysisMode& rhs);
 
 /**
  * @class Pass
  * @brief Pass is the Pass structure for the optimizations.
  * @details The following structure has the different optimization passes that we are going to do.
  */
-class PassME: public Pass {
+class PassME : public Pass {
  public:
   explicit PassME(const char* name, DataFlowAnalysisMode type = kAllNodes,
           unsigned int flags = 0u, const char* dump = "")
@@ -80,12 +82,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 +138,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 +165,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..bc4f5c4 100644
--- a/compiler/dex/portable/mir_to_gbc.h
+++ b/compiler/dex/portable/mir_to_gbc.h
@@ -31,10 +31,49 @@
 #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 {
 
-struct BasicBlock;
+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_;
+};
+
+class BasicBlock;
 struct CallInfo;
 struct CompilationUnit;
 struct MIR;
@@ -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..7b84ba8 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"
 
@@ -29,7 +30,7 @@
  */
 class InitializeData : public PassME {
  public:
-  InitializeData() : PassME("InitializeData") {
+  InitializeData() : PassME("InitializeData", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -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;
 };
 
 /**
@@ -75,7 +76,7 @@
  */
 class CalculatePredecessors : public PassME {
  public:
-  CalculatePredecessors() : PassME("CalculatePredecessors") {
+  CalculatePredecessors() : PassME("CalculatePredecessors", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const;
@@ -87,7 +88,14 @@
  */
 class DFSOrders : public PassME {
  public:
-  DFSOrders() : PassME("DFSOrders") {
+  DFSOrders() : PassME("DFSOrders", kNoNodes) {
+  }
+
+  bool Gate(const PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return !c_unit->mir_graph->DfsOrdersUpToDate();
   }
 
   void Start(PassDataHolder* data) const {
@@ -104,7 +112,7 @@
  */
 class BuildDomination : public PassME {
  public:
-  BuildDomination() : PassME("BuildDomination") {
+  BuildDomination() : PassME("BuildDomination", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -132,7 +140,7 @@
  */
 class TopologicalSortOrders : public PassME {
  public:
-  TopologicalSortOrders() : PassME("TopologicalSortOrders") {
+  TopologicalSortOrders() : PassME("TopologicalSortOrders", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -149,7 +157,7 @@
  */
 class DefBlockMatrix : public PassME {
  public:
-  DefBlockMatrix() : PassME("DefBlockMatrix") {
+  DefBlockMatrix() : PassME("DefBlockMatrix", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -166,7 +174,7 @@
  */
 class CreatePhiNodes : public PassME {
  public:
-  CreatePhiNodes() : PassME("CreatePhiNodes") {
+  CreatePhiNodes() : PassME("CreatePhiNodes", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -184,7 +192,7 @@
 
 class ClearVisitedFlag : public PassME {
  public:
-  ClearVisitedFlag() : PassME("ClearVisitedFlag") {
+  ClearVisitedFlag() : PassME("ClearVisitedFlag", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -201,7 +209,7 @@
  */
 class SSAConversion : public PassME {
  public:
-  SSAConversion() : PassME("SSAConversion") {
+  SSAConversion() : PassME("SSAConversion", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -222,11 +230,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.
@@ -240,7 +248,7 @@
  */
 class PerformInitRegLocations : public PassME {
  public:
-  PerformInitRegLocations() : PassME("PerformInitRegLocation") {
+  PerformInitRegLocations() : PassME("PerformInitRegLocation", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -260,11 +268,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.
@@ -285,7 +293,7 @@
  */
 class FreeData : public PassME {
  public:
-  FreeData() : PassME("FreeData") {
+  FreeData() : PassME("FreeData", kNoNodes) {
   }
 
   void End(PassDataHolder* data) const {
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index 6272555..4c7f874 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -97,7 +97,7 @@
 // First FP callee save.
 #define ARM_FP_CALLEE_SAVE_BASE 16
 // Flag for using R4 to do suspend check
-#define ARM_R4_SUSPEND_FLAG
+// #define ARM_R4_SUSPEND_FLAG
 
 enum ArmResourceEncodingPos {
   kArmGPReg0   = 0,
@@ -109,7 +109,7 @@
   kArmRegEnd   = 48,
 };
 
-enum ArmNativeRegisterPool {
+enum ArmNativeRegisterPool {  // private marker to avoid generate-operator-out.py from processing.
   r0           = RegStorage::k32BitSolo | RegStorage::kCoreRegister |  0,
   r1           = RegStorage::k32BitSolo | RegStorage::kCoreRegister |  1,
   r2           = RegStorage::k32BitSolo | RegStorage::kCoreRegister |  2,
@@ -297,19 +297,20 @@
 constexpr RegStorage rs_dr31(RegStorage::kValid | dr31);
 #endif
 
-// RegisterLocation templates return values (r0, or r0/r1).
-const RegLocation arm_loc_c_return
-    {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1,
-     RegStorage(RegStorage::k32BitSolo, r0), INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_wide
+// RegisterLocation templates return values (r0, r0/r1, s0, or d0).
+// Note: The return locations are shared between quick code and quick helper. This follows quick
+// ABI. Quick helper assembly routine needs to handle the ABI differences.
+const RegLocation arm_loc_c_return =
+    {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_r0, INVALID_SREG, INVALID_SREG};
+const RegLocation arm_loc_c_return_wide =
     {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1,
-     RegStorage(RegStorage::k64BitPair, r0, r1), INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_float
-    {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1,
-     RegStorage(RegStorage::k32BitSolo, r0), INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_double
-    {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1,
-     RegStorage(RegStorage::k64BitPair, r0, r1), INVALID_SREG, INVALID_SREG};
+     RegStorage::MakeRegPair(rs_r0, rs_r1), INVALID_SREG, INVALID_SREG};
+const RegLocation arm_loc_c_return_float = kArm32QuickCodeUseSoftFloat
+    ? arm_loc_c_return
+    : RegLocation({kLocPhysReg, 0, 0, 0, 1, 0, 0, 0, 1, rs_fr0, INVALID_SREG, INVALID_SREG});
+const RegLocation arm_loc_c_return_double = kArm32QuickCodeUseSoftFloat
+    ? arm_loc_c_return_wide
+    : RegLocation({kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, rs_dr0, INVALID_SREG, INVALID_SREG});
 
 enum ArmShiftEncodings {
   kArmLsl = 0x0,
@@ -528,6 +529,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.
@@ -544,6 +546,7 @@
   kThumb2StrdI8,     // strd rt, rt2, [rn +-/1024].
   kArmLast,
 };
+std::ostream& operator<<(std::ostream& os, const ArmOpcode& rhs);
 
 enum ArmOpDmbOptions {
   kSY = 0xf,
@@ -556,23 +559,26 @@
 
 // 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.
 };
+std::ostream& operator<<(std::ostream& os, const ArmEncodingKind& rhs);
 
 // Struct used to define the snippet positions for each Thumb opcode.
 struct ArmEncodingMap {
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index 35c3597..4e20d76 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:
@@ -1248,7 +1265,7 @@
           if (lir->operands[1] != rs_r15pc.GetReg()) {
             break;
           }
-          // NOTE: intentional fallthrough.
+          FALLTHROUGH_INTENDED;
         case kFixupLoad: {
           /*
            * PC-relative loads are mostly used to load immediates
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..b4eebb3 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);
@@ -138,41 +140,6 @@
 }
 
 /*
- * Array data table format:
- *  ushort ident = 0x0300   magic value
- *  ushort width            width of each element in the table
- *  uint   size             number of elements in the table
- *  ubyte  data[size*width] table of data values (may contain a single-byte
- *                          padding at the end)
- *
- * 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;
-  // Add the table to the list - we'll process it later
-  FillArrayData *tab_rec =
-      static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData));
-  tab_rec->table = table;
-  tab_rec->vaddr = current_dalvik_offset_;
-  uint16_t width = tab_rec->table[1];
-  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);
-
-  // Making a call - use explicit registers
-  FlushAllRegs();   /* Everything to home location */
-  LoadValueDirectFixed(rl_src, rs_r0);
-  LoadWordDisp(rs_rARM_SELF, QUICK_ENTRYPOINT_OFFSET(4, pHandleFillArrayData).Int32Value(),
-               rs_rARM_LR);
-  // Materialize a pointer to the fill data image
-  NewLIR3(kThumb2Adr, rs_r1.GetReg(), 0, WrapPointer(tab_rec));
-  ClobberCallerSave();
-  LIR* call_inst = OpReg(kOpBlx, rs_rARM_LR);
-  MarkSafepointPC(call_inst);
-}
-
-/*
  * Handle unlocked -> thin locked transition inline or else call out to quick entrypoint. For more
  * details see monitor.cc.
  */
@@ -499,4 +466,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 ATTRIBUTE_UNUSED,
+                             int state, const MethodReference& target_method,
+                             uint32_t unused_idx ATTRIBUTE_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..d235199 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -19,10 +19,70 @@
 
 #include "arm_lir.h"
 #include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir.h"
+#include "utils/arena_containers.h"
 
 namespace art {
 
 class ArmMir2Lir FINAL : public Mir2Lir {
+ protected:
+  // TODO: Consolidate hard float target support.
+  // InToRegStorageMapper and InToRegStorageMapping can be shared with all backends.
+  // Base class used to get RegStorage for next argument.
+  class InToRegStorageMapper {
+   public:
+    virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide) = 0;
+    virtual ~InToRegStorageMapper() {
+    }
+  };
+
+  // Inherited class for ARM backend.
+  class InToRegStorageArmMapper FINAL : public InToRegStorageMapper {
+   public:
+    InToRegStorageArmMapper()
+        : cur_core_reg_(0), cur_fp_reg_(0), cur_fp_double_reg_(0) {
+    }
+
+    virtual ~InToRegStorageArmMapper() {
+    }
+
+    RegStorage GetNextReg(bool is_double_or_float, bool is_wide) OVERRIDE;
+
+   private:
+    uint32_t cur_core_reg_;
+    uint32_t cur_fp_reg_;
+    uint32_t cur_fp_double_reg_;
+  };
+
+  // Class to map argument to RegStorage. The mapping object is initialized by a mapper.
+  class InToRegStorageMapping FINAL {
+   public:
+    InToRegStorageMapping()
+        : max_mapped_in_(0), is_there_stack_mapped_(false), initialized_(false) {
+    }
+
+    int GetMaxMappedIn() const {
+      return max_mapped_in_;
+    }
+
+    bool IsThereStackMapped() const {
+      return is_there_stack_mapped_;
+    }
+
+    bool IsInitialized() const {
+      return initialized_;
+    }
+
+    void Initialize(RegLocation* arg_locs, int count, InToRegStorageMapper* mapper);
+    RegStorage Get(int in_position) const;
+
+   private:
+    std::map<int, RegStorage> mapping_;
+    int max_mapped_in_;
+    bool is_there_stack_mapped_;
+    bool initialized_;
+  };
+
   public:
     ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
 
@@ -30,6 +90,10 @@
     bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src,
                             RegLocation rl_dest, int lit);
     bool EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE;
+    void GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
+                                    int32_t constant) OVERRIDE;
+    void GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
+                                     int64_t constant) OVERRIDE;
     LIR* CheckSuspendUsingLoad() OVERRIDE;
     RegStorage LoadHelper(QuickEntrypointEnum trampoline) OVERRIDE;
     LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest,
@@ -45,15 +109,30 @@
     void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg);
 
     // Required for target - register utilities.
-    RegStorage TargetReg(SpecialTargetRegister reg);
-    RegStorage GetArgMappingToPhysicalReg(int arg_num);
-    RegLocation GetReturnAlt();
-    RegLocation GetReturnWideAlt();
-    RegLocation LocCReturn();
-    RegLocation LocCReturnRef();
-    RegLocation LocCReturnDouble();
-    RegLocation LocCReturnFloat();
-    RegLocation LocCReturnWide();
+    RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE;
+    RegStorage TargetReg(SpecialTargetRegister reg, WideKind wide_kind) OVERRIDE {
+      if (wide_kind == kWide) {
+        DCHECK((kArg0 <= reg && reg < kArg3) || (kFArg0 <= reg && reg < kFArg15) || (kRet0 == reg));
+        RegStorage ret_reg = RegStorage::MakeRegPair(TargetReg(reg),
+            TargetReg(static_cast<SpecialTargetRegister>(reg + 1)));
+        if (ret_reg.IsFloat()) {
+          // Regard double as double, be consistent with register allocation.
+          ret_reg = As64BitFloatReg(ret_reg);
+        }
+        return ret_reg;
+      } else {
+        return TargetReg(reg);
+      }
+    }
+
+    RegStorage GetArgMappingToPhysicalReg(int arg_num) OVERRIDE;
+    RegLocation GetReturnAlt() OVERRIDE;
+    RegLocation GetReturnWideAlt() OVERRIDE;
+    RegLocation LocCReturn() OVERRIDE;
+    RegLocation LocCReturnRef() OVERRIDE;
+    RegLocation LocCReturnDouble() OVERRIDE;
+    RegLocation LocCReturnFloat() OVERRIDE;
+    RegLocation LocCReturnWide() OVERRIDE;
     ResourceMask GetRegMaskCommon(const RegStorage& reg) const OVERRIDE;
     void AdjustSpillMask();
     void ClobberCallerSave();
@@ -85,15 +164,15 @@
 
     // Required for target - Dalvik-level generators.
     void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                        RegLocation rl_src2) OVERRIDE;
+                        RegLocation rl_src2, int flags) OVERRIDE;
     void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                           RegLocation rl_src1, RegLocation rl_src2);
+                           RegLocation rl_src1, RegLocation rl_src2, int flags);
     void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
                      RegLocation rl_index, RegLocation rl_dest, int scale);
     void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index,
                      RegLocation rl_src, int scale, bool card_mark);
     void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                           RegLocation rl_src1, RegLocation rl_shift);
+                           RegLocation rl_src1, RegLocation rl_shift, int flags);
     void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
                           RegLocation rl_src2);
     void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
@@ -116,13 +195,12 @@
     void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
     void GenExitSequence();
     void GenSpecialExitSequence();
-    void GenFillArrayData(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);
     void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
                           int32_t true_val, int32_t false_val, RegStorage rs_dest,
-                          int dest_reg_class) OVERRIDE;
+                          RegisterClass dest_reg_class) OVERRIDE;
     bool GenMemBarrier(MemBarrierKind barrier_kind);
     void GenMonitorEnter(int opt_flags, RegLocation rl_src);
     void GenMonitorExit(int opt_flags, RegLocation rl_src);
@@ -177,16 +255,51 @@
     RegStorage AllocPreservedDouble(int s_reg);
     RegStorage AllocPreservedSingle(int s_reg);
 
-    bool WideGPRsAreAliases() OVERRIDE {
+    bool WideGPRsAreAliases() const OVERRIDE {
       return false;  // Wide GPRs are formed by pairing.
     }
-    bool WideFPRsAreAliases() OVERRIDE {
+    bool WideFPRsAreAliases() const OVERRIDE {
       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);
 
+    int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
+                             NextCallInsn next_call_insn,
+                             const MethodReference& target_method,
+                             uint32_t vtable_idx,
+                             uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
+                             bool skip_this) OVERRIDE;
+    int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
+                           NextCallInsn next_call_insn,
+                           const MethodReference& target_method,
+                           uint32_t vtable_idx,
+                           uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
+                           bool skip_this) OVERRIDE;
+
   private:
     void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
     void GenMulLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
@@ -201,12 +314,12 @@
     void InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir);
     void AssignDataOffsets();
     RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
-                          bool is_div, bool check_zero);
-    RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div);
-    typedef struct {
+                          bool is_div, int flags) OVERRIDE;
+    RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) OVERRIDE;
+    struct EasyMultiplyOp {
       OpKind op;
       uint32_t shift;
-    } EasyMultiplyOp;
+    };
     bool GetEasyMultiplyOp(int lit, EasyMultiplyOp* op);
     bool GetEasyMultiplyTwoOps(int lit, EasyMultiplyOp* ops);
     void GenEasyMultiplyTwoOps(RegStorage r_dest, RegStorage r_src, EasyMultiplyOp* ops);
@@ -214,6 +327,38 @@
     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_;
+
+    /**
+     * @brief Given float register pair, returns Solo64 float register.
+     * @param reg #RegStorage containing a float register pair (e.g. @c s2 and @c s3).
+     * @return A Solo64 float mapping to the register pair (e.g. @c d1).
+     */
+    static RegStorage As64BitFloatReg(RegStorage reg) {
+      DCHECK(reg.IsFloat());
+
+      RegStorage low = reg.GetLow();
+      RegStorage high = reg.GetHigh();
+      DCHECK((low.GetRegNum() % 2 == 0) && (low.GetRegNum() + 1 == high.GetRegNum()));
+
+      return RegStorage::FloatSolo64(low.GetRegNum() / 2);
+    }
+
+    /**
+     * @brief Given Solo64 float register, returns float register pair.
+     * @param reg #RegStorage containing a Solo64 float register (e.g. @c d1).
+     * @return A float register pair mapping to the Solo64 float pair (e.g. @c s2 and s3).
+     */
+    static RegStorage As64BitFloatRegPair(RegStorage reg) {
+      DCHECK(reg.IsDouble() && reg.Is64BitSolo());
+
+      int reg_num = reg.GetRegNum();
+      return RegStorage::MakeRegPair(RegStorage::FloatSolo32(reg_num * 2),
+                                     RegStorage::FloatSolo32(reg_num * 2 + 1));
+    }
+
+    InToRegStorageMapping in_to_reg_storage_mapping_;
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
index 3eb7c83..2b2592d 100644
--- a/compiler/dex/quick/arm/fp_arm.cc
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -113,6 +113,32 @@
   StoreValueWide(rl_dest, rl_result);
 }
 
+void ArmMir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
+                                            int32_t constant) {
+  RegLocation rl_result;
+  RegStorage r_tmp = AllocTempSingle();
+  LoadConstantNoClobber(r_tmp, constant);
+  rl_src1 = LoadValue(rl_src1, kFPReg);
+  rl_result = EvalLoc(rl_dest, kFPReg, true);
+  NewLIR3(kThumb2Vmuls, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), r_tmp.GetReg());
+  StoreValue(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
+                                             int64_t constant) {
+  RegLocation rl_result;
+  RegStorage r_tmp = AllocTempDouble();
+  DCHECK(r_tmp.IsDouble());
+  LoadConstantWide(r_tmp, constant);
+  rl_src1 = LoadValueWide(rl_src1, kFPReg);
+  DCHECK(rl_src1.wide);
+  rl_result = EvalLocWide(rl_dest, kFPReg, true);
+  DCHECK(rl_dest.wide);
+  DCHECK(rl_result.wide);
+  NewLIR3(kThumb2Vmuld, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), r_tmp.GetReg());
+  StoreValueWide(rl_dest, rl_result);
+}
+
 void ArmMir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src) {
   int op = kThumbBkpt;
   int src_reg;
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 0de2a44..57544b5 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -21,7 +21,7 @@
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "mirror/array.h"
+#include "mirror/array-inl.h"
 
 namespace art {
 
@@ -49,12 +49,13 @@
   int cond_bit = code & 1;
   int alt_bit = cond_bit ^ 1;
 
-  // Note: case fallthroughs intentional
   switch (strlen(guide)) {
     case 3:
       mask1 = (guide[2] == 'T') ? cond_bit : alt_bit;
+      FALLTHROUGH_INTENDED;
     case 2:
       mask2 = (guide[1] == 'T') ? cond_bit : alt_bit;
+      FALLTHROUGH_INTENDED;
     case 1:
       mask3 = (guide[0] == 'T') ? cond_bit : alt_bit;
       break;
@@ -62,6 +63,7 @@
       break;
     default:
       LOG(FATAL) << "OAT: bad case in OpIT";
+      UNREACHABLE();
   }
   mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
        (1 << (3 - strlen(guide)));
@@ -77,12 +79,13 @@
   int cond_bit = code & 1;
   int alt_bit = cond_bit ^ 1;
 
-  // Note: case fallthroughs intentional
   switch (strlen(new_guide)) {
     case 3:
       mask1 = (new_guide[2] == 'T') ? cond_bit : alt_bit;
+      FALLTHROUGH_INTENDED;
     case 2:
       mask2 = (new_guide[1] == 'T') ? cond_bit : alt_bit;
+      FALLTHROUGH_INTENDED;
     case 1:
       mask3 = (new_guide[0] == 'T') ? cond_bit : alt_bit;
       break;
@@ -90,6 +93,7 @@
       break;
     default:
       LOG(FATAL) << "OAT: bad case in UpdateIT";
+      UNREACHABLE();
   }
   mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
       (1 << (3 - strlen(new_guide)));
@@ -205,7 +209,8 @@
 
 void ArmMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
                                   int32_t true_val, int32_t false_val, RegStorage rs_dest,
-                                  int dest_reg_class) {
+                                  RegisterClass dest_reg_class) {
+  UNUSED(dest_reg_class);
   // TODO: Generalize the IT below to accept more than one-instruction loads.
   DCHECK(InexpensiveConstantInt(true_val));
   DCHECK(InexpensiveConstantInt(false_val));
@@ -228,6 +233,7 @@
 }
 
 void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
+  UNUSED(bb);
   RegLocation rl_result;
   RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
   RegLocation rl_dest = mir_graph_->GetDest(mir);
@@ -377,7 +383,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,
@@ -438,6 +444,15 @@
     bool src_fp = r_src.IsFloat();
     DCHECK(r_dest.Is64Bit());
     DCHECK(r_src.Is64Bit());
+    // Note: If the register is get by register allocator, it should never be a pair.
+    // But some functions in mir_2_lir assume 64-bit registers are 32-bit register pairs.
+    // TODO: Rework Mir2Lir::LoadArg() and Mir2Lir::LoadArgDirect().
+    if (dest_fp && r_dest.IsPair()) {
+      r_dest = As64BitFloatReg(r_dest);
+    }
+    if (src_fp && r_src.IsPair()) {
+      r_src = As64BitFloatReg(r_src);
+    }
     if (dest_fp) {
       if (src_fp) {
         OpRegCopy(r_dest, r_src);
@@ -491,6 +506,7 @@
 // Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
 bool ArmMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                     RegLocation rl_src, RegLocation rl_dest, int lit) {
+  UNUSED(dalvik_opcode);
   if ((lit < 0) || (lit >= static_cast<int>(sizeof(magic_table)/sizeof(magic_table[0])))) {
     return false;
   }
@@ -674,14 +690,17 @@
 }
 
 RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
-                      RegLocation rl_src2, bool is_div, bool check_zero) {
+                                  RegLocation rl_src2, bool is_div, int flags) {
+  UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags);
   LOG(FATAL) << "Unexpected use of GenDivRem for Arm";
-  return rl_dest;
+  UNREACHABLE();
 }
 
-RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) {
+RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit,
+                                     bool is_div) {
+  UNUSED(rl_dest, rl_src1, lit, is_div);
   LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm";
-  return rl_dest;
+  UNREACHABLE();
 }
 
 RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) {
@@ -845,7 +864,7 @@
   RegLocation rl_object = LoadValue(rl_src_obj, kRefReg);
   RegLocation rl_new_value;
   if (!is_long) {
-    rl_new_value = LoadValue(rl_src_new_value);
+    rl_new_value = LoadValue(rl_src_new_value, LocToRegClass(rl_src_new_value));
   } else if (load_early) {
     rl_new_value = LoadValueWide(rl_src_new_value, kCoreReg);
   }
@@ -868,7 +887,7 @@
 
   RegLocation rl_expected;
   if (!is_long) {
-    rl_expected = LoadValue(rl_src_expected);
+    rl_expected = LoadValue(rl_src_expected, LocToRegClass(rl_src_new_value));
   } else if (load_early) {
     rl_expected = LoadValueWide(rl_src_expected, kCoreReg);
   } else {
@@ -1059,6 +1078,7 @@
 void ArmMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
                                                RegLocation rl_result, int lit,
                                                int first_bit, int second_bit) {
+  UNUSED(lit);
   OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg,
                    EncodeShift(kArmLsl, second_bit - first_bit));
   if (first_bit != 0) {
@@ -1082,7 +1102,7 @@
 #else
   RegStorage t_reg = AllocTemp();
   LoadBaseDisp(rs_rARM_SELF, Thread::ThreadFlagsOffset<4>().Int32Value(),
-    t_reg, kUnsignedHalf);
+    t_reg, kUnsignedHalf, kNotVolatile);
   LIR* cmp_branch = OpCmpImmBranch((target == NULL) ? kCondNe : kCondEq, t_reg,
     0, target);
   FreeTemp(t_reg);
@@ -1155,112 +1175,113 @@
 
 void ArmMir2Lir::GenMulLong(Instruction::Code opcode, RegLocation rl_dest,
                             RegLocation rl_src1, RegLocation rl_src2) {
-    /*
-     * tmp1     = src1.hi * src2.lo;  // src1.hi is no longer needed
-     * dest     = src1.lo * src2.lo;
-     * tmp1    += src1.lo * src2.hi;
-     * dest.hi += tmp1;
-     *
-     * To pull off inline multiply, we have a worst-case requirement of 7 temporary
-     * registers.  Normally for Arm, we get 5.  We can get to 6 by including
-     * lr in the temp set.  The only problematic case is all operands and result are
-     * distinct, and none have been promoted.  In that case, we can succeed by aggressively
-     * freeing operand temp registers after they are no longer needed.  All other cases
-     * can proceed normally.  We'll just punt on the case of the result having a misaligned
-     * overlap with either operand and send that case to a runtime handler.
-     */
-    RegLocation rl_result;
-    if (BadOverlap(rl_src1, rl_dest) || (BadOverlap(rl_src2, rl_dest))) {
-      FlushAllRegs();
-      CallRuntimeHelperRegLocationRegLocation(kQuickLmul, rl_src1, rl_src2, false);
-      rl_result = GetReturnWide(kCoreReg);
-      StoreValueWide(rl_dest, rl_result);
-      return;
-    }
-
-    rl_src1 = LoadValueWide(rl_src1, kCoreReg);
-    rl_src2 = LoadValueWide(rl_src2, kCoreReg);
-
-    int reg_status = 0;
-    RegStorage res_lo;
-    RegStorage res_hi;
-    bool dest_promoted = rl_dest.location == kLocPhysReg && rl_dest.reg.Valid() &&
-        !IsTemp(rl_dest.reg.GetLow()) && !IsTemp(rl_dest.reg.GetHigh());
-    bool src1_promoted = !IsTemp(rl_src1.reg.GetLow()) && !IsTemp(rl_src1.reg.GetHigh());
-    bool src2_promoted = !IsTemp(rl_src2.reg.GetLow()) && !IsTemp(rl_src2.reg.GetHigh());
-    // Check if rl_dest is *not* either operand and we have enough temp registers.
-    if ((rl_dest.s_reg_low != rl_src1.s_reg_low && rl_dest.s_reg_low != rl_src2.s_reg_low) &&
-        (dest_promoted || src1_promoted || src2_promoted)) {
-      // In this case, we do not need to manually allocate temp registers for result.
-      rl_result = EvalLoc(rl_dest, kCoreReg, true);
-      res_lo = rl_result.reg.GetLow();
-      res_hi = rl_result.reg.GetHigh();
-    } else {
-      res_lo = AllocTemp();
-      if ((rl_src1.s_reg_low == rl_src2.s_reg_low) || src1_promoted || src2_promoted) {
-        // In this case, we have enough temp registers to be allocated for result.
-        res_hi = AllocTemp();
-        reg_status = 1;
-      } else {
-        // In this case, all temps are now allocated.
-        // res_hi will be allocated after we can free src1_hi.
-        reg_status = 2;
-      }
-    }
-
-    // Temporarily add LR to the temp pool, and assign it to tmp1
-    MarkTemp(rs_rARM_LR);
-    FreeTemp(rs_rARM_LR);
-    RegStorage tmp1 = rs_rARM_LR;
-    LockTemp(rs_rARM_LR);
-
-    if (rl_src1.reg == rl_src2.reg) {
-      DCHECK(res_hi.Valid());
-      DCHECK(res_lo.Valid());
-      NewLIR3(kThumb2MulRRR, tmp1.GetReg(), rl_src1.reg.GetLowReg(), rl_src1.reg.GetHighReg());
-      NewLIR4(kThumb2Umull, res_lo.GetReg(), res_hi.GetReg(), rl_src1.reg.GetLowReg(),
-              rl_src1.reg.GetLowReg());
-      OpRegRegRegShift(kOpAdd, res_hi, res_hi, tmp1, EncodeShift(kArmLsl, 1));
-    } else {
-      NewLIR3(kThumb2MulRRR, tmp1.GetReg(), rl_src2.reg.GetLowReg(), rl_src1.reg.GetHighReg());
-      if (reg_status == 2) {
-        DCHECK(!res_hi.Valid());
-        DCHECK_NE(rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg());
-        DCHECK_NE(rl_src1.reg.GetHighReg(), rl_src2.reg.GetHighReg());
-        // Will force free src1_hi, so must clobber.
-        Clobber(rl_src1.reg);
-        FreeTemp(rl_src1.reg.GetHigh());
-        res_hi = AllocTemp();
-      }
-      DCHECK(res_hi.Valid());
-      DCHECK(res_lo.Valid());
-      NewLIR4(kThumb2Umull, res_lo.GetReg(), res_hi.GetReg(), rl_src2.reg.GetLowReg(),
-              rl_src1.reg.GetLowReg());
-      NewLIR4(kThumb2Mla, tmp1.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetHighReg(),
-              tmp1.GetReg());
-      NewLIR4(kThumb2AddRRR, res_hi.GetReg(), tmp1.GetReg(), res_hi.GetReg(), 0);
-      if (reg_status == 2) {
-        FreeTemp(rl_src1.reg.GetLow());
-      }
-    }
-
-    // Now, restore lr to its non-temp status.
-    FreeTemp(tmp1);
-    Clobber(rs_rARM_LR);
-    UnmarkTemp(rs_rARM_LR);
-
-    if (reg_status != 0) {
-      // We had manually allocated registers for rl_result.
-      // Now construct a RegLocation.
-      rl_result = GetReturnWide(kCoreReg);  // Just using as a template.
-      rl_result.reg = RegStorage::MakeRegPair(res_lo, res_hi);
-    }
-
+  UNUSED(opcode);
+  /*
+   * tmp1     = src1.hi * src2.lo;  // src1.hi is no longer needed
+   * dest     = src1.lo * src2.lo;
+   * tmp1    += src1.lo * src2.hi;
+   * dest.hi += tmp1;
+   *
+   * To pull off inline multiply, we have a worst-case requirement of 7 temporary
+   * registers.  Normally for Arm, we get 5.  We can get to 6 by including
+   * lr in the temp set.  The only problematic case is all operands and result are
+   * distinct, and none have been promoted.  In that case, we can succeed by aggressively
+   * freeing operand temp registers after they are no longer needed.  All other cases
+   * can proceed normally.  We'll just punt on the case of the result having a misaligned
+   * overlap with either operand and send that case to a runtime handler.
+   */
+  RegLocation rl_result;
+  if (PartiallyIntersects(rl_src1, rl_dest) || (PartiallyIntersects(rl_src2, rl_dest))) {
+    FlushAllRegs();
+    CallRuntimeHelperRegLocationRegLocation(kQuickLmul, rl_src1, rl_src2, false);
+    rl_result = GetReturnWide(kCoreReg);
     StoreValueWide(rl_dest, rl_result);
+    return;
+  }
+
+  rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+  rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+
+  int reg_status = 0;
+  RegStorage res_lo;
+  RegStorage res_hi;
+  bool dest_promoted = rl_dest.location == kLocPhysReg && rl_dest.reg.Valid() &&
+      !IsTemp(rl_dest.reg.GetLow()) && !IsTemp(rl_dest.reg.GetHigh());
+  bool src1_promoted = !IsTemp(rl_src1.reg.GetLow()) && !IsTemp(rl_src1.reg.GetHigh());
+  bool src2_promoted = !IsTemp(rl_src2.reg.GetLow()) && !IsTemp(rl_src2.reg.GetHigh());
+  // Check if rl_dest is *not* either operand and we have enough temp registers.
+  if ((rl_dest.s_reg_low != rl_src1.s_reg_low && rl_dest.s_reg_low != rl_src2.s_reg_low) &&
+      (dest_promoted || src1_promoted || src2_promoted)) {
+    // In this case, we do not need to manually allocate temp registers for result.
+    rl_result = EvalLoc(rl_dest, kCoreReg, true);
+    res_lo = rl_result.reg.GetLow();
+    res_hi = rl_result.reg.GetHigh();
+  } else {
+    res_lo = AllocTemp();
+    if ((rl_src1.s_reg_low == rl_src2.s_reg_low) || src1_promoted || src2_promoted) {
+      // In this case, we have enough temp registers to be allocated for result.
+      res_hi = AllocTemp();
+      reg_status = 1;
+    } else {
+      // In this case, all temps are now allocated.
+      // res_hi will be allocated after we can free src1_hi.
+      reg_status = 2;
+    }
+  }
+
+  // Temporarily add LR to the temp pool, and assign it to tmp1
+  MarkTemp(rs_rARM_LR);
+  FreeTemp(rs_rARM_LR);
+  RegStorage tmp1 = rs_rARM_LR;
+  LockTemp(rs_rARM_LR);
+
+  if (rl_src1.reg == rl_src2.reg) {
+    DCHECK(res_hi.Valid());
+    DCHECK(res_lo.Valid());
+    NewLIR3(kThumb2MulRRR, tmp1.GetReg(), rl_src1.reg.GetLowReg(), rl_src1.reg.GetHighReg());
+    NewLIR4(kThumb2Umull, res_lo.GetReg(), res_hi.GetReg(), rl_src1.reg.GetLowReg(),
+            rl_src1.reg.GetLowReg());
+    OpRegRegRegShift(kOpAdd, res_hi, res_hi, tmp1, EncodeShift(kArmLsl, 1));
+  } else {
+    NewLIR3(kThumb2MulRRR, tmp1.GetReg(), rl_src2.reg.GetLowReg(), rl_src1.reg.GetHighReg());
+    if (reg_status == 2) {
+      DCHECK(!res_hi.Valid());
+      DCHECK_NE(rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg());
+      DCHECK_NE(rl_src1.reg.GetHighReg(), rl_src2.reg.GetHighReg());
+      // Will force free src1_hi, so must clobber.
+      Clobber(rl_src1.reg);
+      FreeTemp(rl_src1.reg.GetHigh());
+      res_hi = AllocTemp();
+    }
+    DCHECK(res_hi.Valid());
+    DCHECK(res_lo.Valid());
+    NewLIR4(kThumb2Umull, res_lo.GetReg(), res_hi.GetReg(), rl_src2.reg.GetLowReg(),
+            rl_src1.reg.GetLowReg());
+    NewLIR4(kThumb2Mla, tmp1.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetHighReg(),
+            tmp1.GetReg());
+    NewLIR4(kThumb2AddRRR, res_hi.GetReg(), tmp1.GetReg(), res_hi.GetReg(), 0);
+    if (reg_status == 2) {
+      FreeTemp(rl_src1.reg.GetLow());
+    }
+  }
+
+  // Now, restore lr to its non-temp status.
+  FreeTemp(tmp1);
+  Clobber(rs_rARM_LR);
+  UnmarkTemp(rs_rARM_LR);
+
+  if (reg_status != 0) {
+    // We had manually allocated registers for rl_result.
+    // Now construct a RegLocation.
+    rl_result = GetReturnWide(kCoreReg);  // Just using as a template.
+    rl_result.reg = RegStorage::MakeRegPair(res_lo, res_hi);
+  }
+
+  StoreValueWide(rl_dest, rl_result);
 }
 
 void ArmMir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                                RegLocation rl_src2) {
+                                RegLocation rl_src2, int flags) {
   switch (opcode) {
     case Instruction::MUL_LONG:
     case Instruction::MUL_LONG_2ADDR:
@@ -1275,7 +1296,7 @@
   }
 
   // Fallback for all other ops.
-  Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+  Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
 }
 
 /*
@@ -1338,7 +1359,6 @@
       FreeTemp(reg_len);
     }
     LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size, kNotVolatile);
-    MarkPossibleNullPointerException(opt_flags);
     if (!constant_index) {
       FreeTemp(reg_ptr);
     }
@@ -1359,7 +1379,6 @@
       FreeTemp(reg_len);
     }
     LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
-    MarkPossibleNullPointerException(opt_flags);
     FreeTemp(reg_ptr);
     StoreValue(rl_dest, rl_result);
   }
@@ -1438,7 +1457,6 @@
     }
 
     StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size, kNotVolatile);
-    MarkPossibleNullPointerException(opt_flags);
   } else {
     /* reg_ptr -> array data */
     OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
@@ -1448,7 +1466,6 @@
       FreeTemp(reg_len);
     }
     StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
-    MarkPossibleNullPointerException(opt_flags);
   }
   if (allocated_reg_ptr_temp) {
     FreeTemp(reg_ptr);
@@ -1460,7 +1477,9 @@
 
 
 void ArmMir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
-                                   RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) {
+                                   RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift,
+                                   int flags) {
+  UNUSED(flags);
   rl_src = LoadValueWide(rl_src, kCoreReg);
   // Per spec, we only care about low 6 bits of shift amount.
   int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
@@ -1468,7 +1487,7 @@
     StoreValueWide(rl_dest, rl_src);
     return;
   }
-  if (BadOverlap(rl_src, rl_dest)) {
+  if (PartiallyIntersects(rl_src, rl_dest)) {
     GenShiftOpLong(opcode, rl_dest, rl_src, rl_shift);
     return;
   }
@@ -1533,11 +1552,12 @@
 }
 
 void ArmMir2Lir::GenArithImmOpLong(Instruction::Code opcode,
-                                   RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
+                                   RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                                   int flags) {
   if ((opcode == Instruction::SUB_LONG_2ADDR) || (opcode == Instruction::SUB_LONG)) {
     if (!rl_src2.is_const) {
       // Don't bother with special handling for subtract from immediate.
-      GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+      GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
       return;
     }
   } else {
@@ -1547,8 +1567,8 @@
       std::swap(rl_src1, rl_src2);
     }
   }
-  if (BadOverlap(rl_src1, rl_dest)) {
-    GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+  if (PartiallyIntersects(rl_src1, rl_dest)) {
+    GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
     return;
   }
   DCHECK(rl_src2.is_const);
@@ -1565,7 +1585,7 @@
     case Instruction::SUB_LONG:
     case Instruction::SUB_LONG_2ADDR:
       if ((mod_imm_lo < 0) || (mod_imm_hi < 0)) {
-        GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+        GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
         return;
       }
       break;
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 0509ad3..0e8f645 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"
 
@@ -88,7 +89,7 @@
 
 // Return a target-dependent special register.
 RegStorage ArmMir2Lir::TargetReg(SpecialTargetRegister reg) {
-  RegStorage res_reg = RegStorage::InvalidReg();
+  RegStorage res_reg;
   switch (reg) {
     case kSelf: res_reg = rs_rARM_SELF; break;
 #ifdef ARM_R4_SUSPEND_FLAG
@@ -103,10 +104,22 @@
     case kArg1: res_reg = rs_r1; break;
     case kArg2: res_reg = rs_r2; break;
     case kArg3: res_reg = rs_r3; break;
-    case kFArg0: res_reg = rs_r0; break;
-    case kFArg1: res_reg = rs_r1; break;
-    case kFArg2: res_reg = rs_r2; break;
-    case kFArg3: res_reg = rs_r3; break;
+    case kFArg0: res_reg = kArm32QuickCodeUseSoftFloat ? rs_r0 : rs_fr0; break;
+    case kFArg1: res_reg = kArm32QuickCodeUseSoftFloat ? rs_r1 : rs_fr1; break;
+    case kFArg2: res_reg = kArm32QuickCodeUseSoftFloat ? rs_r2 : rs_fr2; break;
+    case kFArg3: res_reg = kArm32QuickCodeUseSoftFloat ? rs_r3 : rs_fr3; break;
+    case kFArg4: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr4; break;
+    case kFArg5: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr5; break;
+    case kFArg6: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr6; break;
+    case kFArg7: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr7; break;
+    case kFArg8: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr8; break;
+    case kFArg9: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr9; break;
+    case kFArg10: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr10; break;
+    case kFArg11: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr11; break;
+    case kFArg12: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr12; break;
+    case kFArg13: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr13; break;
+    case kFArg14: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr14; break;
+    case kFArg15: res_reg = kArm32QuickCodeUseSoftFloat ? RegStorage::InvalidReg() : rs_fr15; break;
     case kRet0: res_reg = rs_r0; break;
     case kRet1: res_reg = rs_r1; break;
     case kInvokeTgt: res_reg = rs_rARM_LR; break;
@@ -118,20 +131,6 @@
   return res_reg;
 }
 
-RegStorage ArmMir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
-  // For the 32-bit internal ABI, the first 3 arguments are passed in registers.
-  switch (arg_num) {
-    case 0:
-      return rs_r1;
-    case 1:
-      return rs_r2;
-    case 2:
-      return rs_r3;
-    default:
-      return RegStorage::InvalidReg();
-  }
-}
-
 /*
  * Decode the register id.
  */
@@ -451,6 +450,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,14 +554,15 @@
 }
 
 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) {
-      LOG(FATAL) << "Encoding order for " << ArmMir2Lir::EncodingMap[i].name
-                 << " is wrong: expecting " << i << ", seeing "
-                 << static_cast<int>(ArmMir2Lir::EncodingMap[i].opcode);
-    }
+    DCHECK_EQ(ArmMir2Lir::EncodingMap[i].opcode, i)
+        << "Encoding order for " << ArmMir2Lir::EncodingMap[i].name
+        << " is wrong: expecting " << i << ", seeing "
+        << static_cast<int>(ArmMir2Lir::EncodingMap[i].opcode);
   }
 }
 
@@ -567,16 +572,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);
@@ -710,6 +715,32 @@
   LockTemp(rs_r1);
   LockTemp(rs_r2);
   LockTemp(rs_r3);
+  if (!kArm32QuickCodeUseSoftFloat) {
+    LockTemp(rs_fr0);
+    LockTemp(rs_fr1);
+    LockTemp(rs_fr2);
+    LockTemp(rs_fr3);
+    LockTemp(rs_fr4);
+    LockTemp(rs_fr5);
+    LockTemp(rs_fr6);
+    LockTemp(rs_fr7);
+    LockTemp(rs_fr8);
+    LockTemp(rs_fr9);
+    LockTemp(rs_fr10);
+    LockTemp(rs_fr11);
+    LockTemp(rs_fr12);
+    LockTemp(rs_fr13);
+    LockTemp(rs_fr14);
+    LockTemp(rs_fr15);
+    LockTemp(rs_dr0);
+    LockTemp(rs_dr1);
+    LockTemp(rs_dr2);
+    LockTemp(rs_dr3);
+    LockTemp(rs_dr4);
+    LockTemp(rs_dr5);
+    LockTemp(rs_dr6);
+    LockTemp(rs_dr7);
+  }
 }
 
 /* To be used when explicitly managing register use */
@@ -718,6 +749,32 @@
   FreeTemp(rs_r1);
   FreeTemp(rs_r2);
   FreeTemp(rs_r3);
+  if (!kArm32QuickCodeUseSoftFloat) {
+    FreeTemp(rs_fr0);
+    FreeTemp(rs_fr1);
+    FreeTemp(rs_fr2);
+    FreeTemp(rs_fr3);
+    FreeTemp(rs_fr4);
+    FreeTemp(rs_fr5);
+    FreeTemp(rs_fr6);
+    FreeTemp(rs_fr7);
+    FreeTemp(rs_fr8);
+    FreeTemp(rs_fr9);
+    FreeTemp(rs_fr10);
+    FreeTemp(rs_fr11);
+    FreeTemp(rs_fr12);
+    FreeTemp(rs_fr13);
+    FreeTemp(rs_fr14);
+    FreeTemp(rs_fr15);
+    FreeTemp(rs_dr0);
+    FreeTemp(rs_dr1);
+    FreeTemp(rs_dr2);
+    FreeTemp(rs_dr3);
+    FreeTemp(rs_dr4);
+    FreeTemp(rs_dr5);
+    FreeTemp(rs_dr6);
+    FreeTemp(rs_dr7);
+  }
 }
 
 RegStorage ArmMir2Lir::LoadHelper(QuickEntrypointEnum trampoline) {
@@ -783,8 +840,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 +864,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 +879,330 @@
   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();
+}
+
+RegStorage ArmMir2Lir::InToRegStorageArmMapper::GetNextReg(bool is_double_or_float, bool is_wide) {
+  const RegStorage coreArgMappingToPhysicalReg[] =
+      {rs_r1, rs_r2, rs_r3};
+  const int coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
+  const RegStorage fpArgMappingToPhysicalReg[] =
+      {rs_fr0, rs_fr1, rs_fr2, rs_fr3, rs_fr4, rs_fr5, rs_fr6, rs_fr7,
+       rs_fr8, rs_fr9, rs_fr10, rs_fr11, rs_fr12, rs_fr13, rs_fr14, rs_fr15};
+  constexpr uint32_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg);
+  static_assert(fpArgMappingToPhysicalRegSize % 2 == 0, "Number of FP Arg regs is not even");
+
+  if (kArm32QuickCodeUseSoftFloat) {
+    is_double_or_float = false;  // Regard double as long, float as int.
+    is_wide = false;  // Map long separately.
+  }
+
+  RegStorage result = RegStorage::InvalidReg();
+  if (is_double_or_float) {
+    // TODO: Remove "cur_fp_double_reg_ % 2 != 0" when we return double as double.
+    if (is_wide || cur_fp_double_reg_ % 2 != 0) {
+      cur_fp_double_reg_ = std::max(cur_fp_double_reg_, RoundUp(cur_fp_reg_, 2));
+      if (cur_fp_double_reg_ < fpArgMappingToPhysicalRegSize) {
+        // TODO: Replace by following code in the branch when FlushIns() support 64-bit registers.
+        // result = RegStorage::MakeRegPair(fpArgMappingToPhysicalReg[cur_fp_double_reg_],
+        //                                  fpArgMappingToPhysicalReg[cur_fp_double_reg_ + 1]);
+        // result = As64BitFloatReg(result);
+        // cur_fp_double_reg_ += 2;
+        result = fpArgMappingToPhysicalReg[cur_fp_double_reg_];
+        cur_fp_double_reg_++;
+      }
+    } else {
+      // TODO: Remove the check when we return double as double.
+      DCHECK_EQ(cur_fp_double_reg_ % 2, 0U);
+      if (cur_fp_reg_ % 2 == 0) {
+        cur_fp_reg_ = std::max(cur_fp_double_reg_, cur_fp_reg_);
+      }
+      if (cur_fp_reg_ < fpArgMappingToPhysicalRegSize) {
+        result = fpArgMappingToPhysicalReg[cur_fp_reg_];
+        cur_fp_reg_++;
+      }
+    }
+  } else {
+    if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+      result = coreArgMappingToPhysicalReg[cur_core_reg_++];
+      // TODO: Enable following code when FlushIns() support 64-bit registers.
+      // if (is_wide && cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+      //   result = RegStorage::MakeRegPair(result, coreArgMappingToPhysicalReg[cur_core_reg_++]);
+      // }
+    }
+  }
+  return result;
+}
+
+RegStorage ArmMir2Lir::InToRegStorageMapping::Get(int in_position) const {
+  DCHECK(IsInitialized());
+  auto res = mapping_.find(in_position);
+  return res != mapping_.end() ? res->second : RegStorage::InvalidReg();
+}
+
+void ArmMir2Lir::InToRegStorageMapping::Initialize(RegLocation* arg_locs, int count,
+                                                   InToRegStorageMapper* mapper) {
+  DCHECK(mapper != nullptr);
+  max_mapped_in_ = -1;
+  is_there_stack_mapped_ = false;
+  for (int in_position = 0; in_position < count; in_position++) {
+     RegStorage reg = mapper->GetNextReg(arg_locs[in_position].fp,
+                                         arg_locs[in_position].wide);
+     if (reg.Valid()) {
+       mapping_[in_position] = reg;
+       // TODO: Enable the following code when FlushIns() support 64-bit argument registers.
+       // if (arg_locs[in_position].wide) {
+       //  if (reg.Is32Bit()) {
+       //    // As it is a split long, the hi-part is on stack.
+       //    is_there_stack_mapped_ = true;
+       //  }
+       //  // We covered 2 v-registers, so skip the next one
+       //  in_position++;
+       // }
+       max_mapped_in_ = std::max(max_mapped_in_, in_position);
+     } else {
+       is_there_stack_mapped_ = true;
+     }
+  }
+  initialized_ = true;
+}
+
+// TODO: Should be able to return long, double registers.
+// Need check some common code as it will break some assumption.
+RegStorage ArmMir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
+  if (!in_to_reg_storage_mapping_.IsInitialized()) {
+    int start_vreg = mir_graph_->GetFirstInVR();
+    RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
+
+    InToRegStorageArmMapper mapper;
+    in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
+  }
+  return in_to_reg_storage_mapping_.Get(arg_num);
+}
+
+int ArmMir2Lir::GenDalvikArgsNoRange(CallInfo* info,
+                                     int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
+                                     const MethodReference& target_method,
+                                     uint32_t vtable_idx, uintptr_t direct_code,
+                                     uintptr_t direct_method, InvokeType type, bool skip_this) {
+  if (kArm32QuickCodeUseSoftFloat) {
+    return Mir2Lir::GenDalvikArgsNoRange(info, call_state, pcrLabel, next_call_insn, target_method,
+                                         vtable_idx, direct_code, direct_method, type, skip_this);
+  } else {
+    return GenDalvikArgsRange(info, call_state, pcrLabel, next_call_insn, target_method, vtable_idx,
+                              direct_code, direct_method, type, skip_this);
+  }
+}
+
+int ArmMir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
+                                   LIR** pcrLabel, NextCallInsn next_call_insn,
+                                   const MethodReference& target_method,
+                                   uint32_t vtable_idx, uintptr_t direct_code,
+                                   uintptr_t direct_method, InvokeType type, bool skip_this) {
+  if (kArm32QuickCodeUseSoftFloat) {
+    return Mir2Lir::GenDalvikArgsRange(info, call_state, pcrLabel, next_call_insn, target_method,
+                                       vtable_idx, direct_code, direct_method, type, skip_this);
+  }
+
+  // TODO: Rework the implementation when argument register can be long or double.
+
+  /* If no arguments, just return */
+  if (info->num_arg_words == 0) {
+    return call_state;
+  }
+
+  const int start_index = skip_this ? 1 : 0;
+
+  InToRegStorageArmMapper mapper;
+  InToRegStorageMapping in_to_reg_storage_mapping;
+  in_to_reg_storage_mapping.Initialize(info->args, info->num_arg_words, &mapper);
+  const int last_mapped_in = in_to_reg_storage_mapping.GetMaxMappedIn();
+  int regs_left_to_pass_via_stack = info->num_arg_words - (last_mapped_in + 1);
+
+  // First of all, check whether it makes sense to use bulk copying.
+  // Bulk copying is done only for the range case.
+  // TODO: make a constant instead of 2
+  if (info->is_range && regs_left_to_pass_via_stack >= 2) {
+    // Scan the rest of the args - if in phys_reg flush to memory
+    for (int next_arg = last_mapped_in + 1; next_arg < info->num_arg_words;) {
+      RegLocation loc = info->args[next_arg];
+      if (loc.wide) {
+        // TODO: Only flush hi-part.
+        if (loc.high_word) {
+          loc = info->args[--next_arg];
+        }
+        loc = UpdateLocWide(loc);
+        if (loc.location == kLocPhysReg) {
+          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+          StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
+        }
+        next_arg += 2;
+      } else {
+        loc = UpdateLoc(loc);
+        if (loc.location == kLocPhysReg) {
+          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+          if (loc.ref) {
+            StoreRefDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kNotVolatile);
+          } else {
+            StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32,
+                          kNotVolatile);
+          }
+        }
+        next_arg++;
+      }
+    }
+
+    // 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,
+                                                   cu_->instruction_set);
+
+    int current_src_offset = start_offset;
+    int current_dest_offset = outs_offset;
+
+    // Only davik regs are accessed in this loop; no next_call_insn() calls.
+    ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+    while (regs_left_to_pass_via_stack > 0) {
+      /*
+       * TODO: Improve by adding block copy for large number of arguments.  This
+       * should be done, if possible, as a target-depending helper.  For now, just
+       * copy a Dalvik vreg at a time.
+       */
+      // Moving 32-bits via general purpose register.
+      size_t bytes_to_move = sizeof(uint32_t);
+
+      // Instead of allocating a new temp, simply reuse one of the registers being used
+      // for argument passing.
+      RegStorage temp = TargetReg(kArg3, kNotWide);
+
+      // Now load the argument VR and store to the outs.
+      Load32Disp(TargetPtrReg(kSp), current_src_offset, temp);
+      Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp);
+
+      current_src_offset += bytes_to_move;
+      current_dest_offset += bytes_to_move;
+      regs_left_to_pass_via_stack -= (bytes_to_move >> 2);
+    }
+    DCHECK_EQ(regs_left_to_pass_via_stack, 0);
+  }
+
+  // Now handle rest not registers if they are
+  if (in_to_reg_storage_mapping.IsThereStackMapped()) {
+    RegStorage regWide = TargetReg(kArg2, kWide);
+    for (int i = start_index; i <= last_mapped_in + regs_left_to_pass_via_stack; i++) {
+      RegLocation rl_arg = info->args[i];
+      rl_arg = UpdateRawLoc(rl_arg);
+      RegStorage reg = in_to_reg_storage_mapping.Get(i);
+      // TODO: Only pass split wide hi-part via stack.
+      if (!reg.Valid() || rl_arg.wide) {
+        int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
+
+        {
+          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+          if (rl_arg.wide) {
+            if (rl_arg.location == kLocPhysReg) {
+              StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile);
+            } else {
+              LoadValueDirectWideFixed(rl_arg, regWide);
+              StoreBaseDisp(TargetPtrReg(kSp), out_offset, regWide, k64, kNotVolatile);
+            }
+          } else {
+            if (rl_arg.location == kLocPhysReg) {
+              if (rl_arg.ref) {
+                StoreRefDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, kNotVolatile);
+              } else {
+                StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile);
+              }
+            } else {
+              if (rl_arg.ref) {
+                RegStorage regSingle = TargetReg(kArg2, kRef);
+                LoadValueDirectFixed(rl_arg, regSingle);
+                StoreRefDisp(TargetPtrReg(kSp), out_offset, regSingle, kNotVolatile);
+              } else {
+                RegStorage regSingle = TargetReg(kArg2, kNotWide);
+                LoadValueDirectFixed(rl_arg, regSingle);
+                StoreBaseDisp(TargetPtrReg(kSp), out_offset, regSingle, k32, kNotVolatile);
+              }
+            }
+          }
+        }
+
+        call_state = next_call_insn(cu_, info, call_state, target_method,
+                                    vtable_idx, direct_code, direct_method, type);
+      }
+      if (rl_arg.wide) {
+        i++;
+      }
+    }
+  }
+
+  // Finish with mapped registers
+  for (int i = start_index; i <= last_mapped_in; i++) {
+    RegLocation rl_arg = info->args[i];
+    rl_arg = UpdateRawLoc(rl_arg);
+    RegStorage reg = in_to_reg_storage_mapping.Get(i);
+    if (reg.Valid()) {
+      if (reg.Is64Bit()) {
+        LoadValueDirectWideFixed(rl_arg, reg);
+      } else {
+        // TODO: Only split long should be the case we need to care about.
+        if (rl_arg.wide) {
+          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+          int high_word = rl_arg.high_word ? 1 : 0;
+          rl_arg = high_word ? info->args[i - 1] : rl_arg;
+          if (rl_arg.location == kLocPhysReg) {
+            RegStorage rs_arg = rl_arg.reg;
+            if (rs_arg.IsDouble() && rs_arg.Is64BitSolo()) {
+              rs_arg = As64BitFloatRegPair(rs_arg);
+            }
+            RegStorage rs_arg_low = rs_arg.GetLow();
+            RegStorage rs_arg_high = rs_arg.GetHigh();
+            OpRegCopy(reg, high_word ? rs_arg_high : rs_arg_low);
+          } else {
+            Load32Disp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low + high_word), reg);
+          }
+        } else {
+          LoadValueDirectFixed(rl_arg, reg);
+        }
+      }
+      call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+                                  direct_code, direct_method, type);
+    }
+    if (reg.Is64Bit()) {
+      i++;
+    }
+  }
+
+  call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+                           direct_code, direct_method, type);
+  if (pcrLabel) {
+    if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
+      *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
+    } else {
+      *pcrLabel = nullptr;
+      // In lieu of generating a check for kArg1 being null, we need to
+      // perform a load when doing implicit checks.
+      RegStorage tmp = AllocTemp();
+      Load32Disp(TargetReg(kArg1, kRef), 0, tmp);
+      MarkPossibleNullPointerException(info->opt_flags);
+      FreeTemp(tmp);
+    }
+  }
+  return call_state;
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index bba1a8c..0d5aa90 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -373,18 +373,21 @@
 }
 
 LIR* ArmMir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, MoveType move_type) {
+  UNUSED(r_dest, r_base, offset, move_type);
   UNIMPLEMENTED(FATAL);
-  return nullptr;
+  UNREACHABLE();
 }
 
 LIR* ArmMir2Lir::OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, MoveType move_type) {
+  UNUSED(r_base, offset, r_src, move_type);
   UNIMPLEMENTED(FATAL);
-  return nullptr;
+  UNREACHABLE();
 }
 
 LIR* ArmMir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src) {
+  UNUSED(op, cc, r_dest, r_src);
   LOG(FATAL) << "Unexpected use of OpCondRegReg for Arm";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* ArmMir2Lir::OpRegRegRegShift(OpKind op, RegStorage r_dest, RegStorage r_src1,
@@ -461,7 +464,6 @@
 }
 
 LIR* ArmMir2Lir::OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, int value) {
-  LIR* res;
   bool neg = (value < 0);
   int32_t abs_value = (neg) ? -value : value;
   ArmOpcode opcode = kThumbBkpt;
@@ -494,7 +496,7 @@
           (value <= 1020) && ((value & 0x3) == 0)) {
         return NewLIR3(kThumbAddPcRel, r_dest.GetReg(), r_src1.GetReg(), value >> 2);
       }
-      // Note: intentional fallthrough
+      FALLTHROUGH_INTENDED;
     case kOpSub:
       if (all_low_regs && ((abs_value & 0x7) == abs_value)) {
         if (op == kOpAdd)
@@ -587,6 +589,7 @@
   } else {
     RegStorage r_scratch = AllocTemp();
     LoadConstant(r_scratch, value);
+    LIR* res;
     if (EncodingMap[alt_opcode].flags & IS_QUAD_OP)
       res = NewLIR4(alt_opcode, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg(), 0);
     else
@@ -956,7 +959,7 @@
 
   // TODO: in future may need to differentiate Dalvik accesses w/ spills
   if (mem_ref_type_ == ResourceMask::kDalvikReg) {
-    DCHECK(r_base == rs_rARM_SP);
+    DCHECK_EQ(r_base, rs_rARM_SP);
     AnnotateDalvikRegAccess(load, displacement >> 2, true /* is_load */, r_dest.Is64Bit());
   }
   return load;
@@ -969,9 +972,9 @@
     size = k32;
   }
   LIR* load;
-  if (UNLIKELY(is_volatile == kVolatile &&
-               (size == k64 || size == kDouble) &&
-               !cu_->compiler_driver->GetInstructionSetFeatures().HasLpae())) {
+  if (is_volatile == kVolatile && (size == k64 || size == kDouble) &&
+      !cu_->compiler_driver->GetInstructionSetFeatures()->
+          AsArmInstructionSetFeatures()->HasLpae()) {
     // Only 64-bit load needs special handling.
     // If the cpu supports LPAE, aligned LDRD is atomic - fall through to LoadBaseDisp().
     DCHECK(!r_dest.IsFloat());  // See RegClassForFieldLoadSave().
@@ -1007,6 +1010,12 @@
     // Intentional fall-though.
     case k64:
       if (r_src.IsFloat()) {
+        // Note: If the register is retrieved by register allocator, it should never be a pair.
+        // But some functions in mir2lir assume 64-bit registers are 32-bit register pairs.
+        // TODO: Rework Mir2Lir::LoadArg() and Mir2Lir::LoadArgDirect().
+        if (r_src.IsPair()) {
+          r_src = As64BitFloatReg(r_src);
+        }
         DCHECK(!r_src.IsPair());
         store = LoadStoreUsingInsnWithOffsetImm8Shl2(kThumb2Vstrd, r_base, displacement, r_src);
       } else {
@@ -1079,7 +1088,7 @@
 
   // TODO: In future, may need to differentiate Dalvik & spill accesses
   if (mem_ref_type_ == ResourceMask::kDalvikReg) {
-    DCHECK(r_base == rs_rARM_SP);
+    DCHECK_EQ(r_base, rs_rARM_SP);
     AnnotateDalvikRegAccess(store, displacement >> 2, false /* is_load */, r_src.Is64Bit());
   }
   return store;
@@ -1093,9 +1102,9 @@
   }
 
   LIR* store;
-  if (UNLIKELY(is_volatile == kVolatile &&
-               (size == k64 || size == kDouble) &&
-               !cu_->compiler_driver->GetInstructionSetFeatures().HasLpae())) {
+  if (is_volatile == kVolatile && (size == k64 || size == kDouble) &&
+      !cu_->compiler_driver->GetInstructionSetFeatures()->
+          AsArmInstructionSetFeatures()->HasLpae()) {
     // Only 64-bit store needs special handling.
     // If the cpu supports LPAE, aligned STRD is atomic - fall through to StoreBaseDisp().
     // Use STREXD for the atomic store. (Expect displacement > 0, don't optimize for == 0.)
@@ -1161,11 +1170,13 @@
 }
 
 LIR* ArmMir2Lir::OpMem(OpKind op, RegStorage r_base, int disp) {
+  UNUSED(op, r_base, disp);
   LOG(FATAL) << "Unexpected use of OpMem for Arm";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* ArmMir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) {
+  UNUSED(trampoline);  // The address of the trampoline is already loaded into r_tgt.
   return OpReg(op, r_tgt);
 }
 
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index a449cbd..973279e 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,8 +114,10 @@
 #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)
+#define IS_SIGNED_IMM26(value) IS_SIGNED_IMM(26, value)
 
 // Quick macro used to define the registers.
 #define A64_REGISTER_CODE_LIST(R) \
@@ -127,7 +127,7 @@
   R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31)
 
 // Registers (integer) values.
-enum A64NativeRegisterPool {
+enum A64NativeRegisterPool {  // private marker to avoid generate-operator-out.py from processing.
 #  define A64_DEFINE_REGISTERS(nr) \
     rw##nr = RegStorage::k32BitSolo | RegStorage::kCoreRegister | nr, \
     rx##nr = RegStorage::k64BitSolo | RegStorage::kCoreRegister | nr, \
@@ -185,15 +185,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 +227,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].
@@ -241,6 +241,7 @@
   kA64B2ct,          // b.cond [01010100] imm_19[23-5] [0] cond[3-0].
   kA64Blr1x,         // blr [1101011000111111000000] rn[9-5] [00000].
   kA64Br1x,          // br  [1101011000011111000000] rn[9-5] [00000].
+  kA64Bl1t,          // bl  [100101] imm26[25-0].
   kA64Brk1d,         // brk [11010100001] imm_16[20-5] [00000].
   kA64B1t,           // b   [00010100] offset_26[25-0].
   kA64Cbnz2rt,       // cbnz[00110101] imm_19[23-5] rt[4-0].
@@ -355,12 +356,16 @@
   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.
-  kA64Wide = 0x1000  // Flag used to select the second instruction variant.
+  kA64NotWide = kA64First,  // 0 - Flag used to select the first instruction variant.
+  kA64Wide = 0x1000         // Flag used to select the second instruction variant.
 };
+std::ostream& operator<<(std::ostream& os, const A64Opcode& rhs);
 
 /*
  * The A64 instruction set provides two variants for many instructions. For example, "mov wN, wM"
@@ -371,22 +376,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 +393,40 @@
 };
 
 // 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.
 };
+std::ostream& operator<<(std::ostream& os, const A64EncodingKind & rhs);
 
 // 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 +434,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..da7ac87 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,
@@ -154,6 +155,10 @@
                  kFmtRegX, 9, 5, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE0 | IS_BRANCH,
                  "br", "!0x", kFixupNone),
+    ENCODING_MAP(kA64Bl1t, NO_VARIANTS(0x94000000),
+                 kFmtBitBlt, 25, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP,
+                 "bl", "!0T", kFixupLabel),
     ENCODING_MAP(kA64Brk1d, NO_VARIANTS(0xd4200000),
                  kFmtBitBlt, 20, 5, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
@@ -228,27 +233,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 +273,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 +309,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 +373,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 +383,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 +391,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 +415,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 +511,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 +551,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 +587,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 +619,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 +671,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;
 
@@ -686,16 +705,16 @@
               switch (kind) {
                 case kFmtRegX:
                   want_64_bit = true;
-                  // Intentional fall-through.
+                  FALLTHROUGH_INTENDED;
                 case kFmtRegW:
                   want_var_size = false;
-                  // Intentional fall-through.
+                  FALLTHROUGH_INTENDED;
                 case kFmtRegR:
                   want_zero = true;
                   break;
                 case kFmtRegXOrSp:
                   want_64_bit = true;
-                  // Intentional fall-through.
+                  FALLTHROUGH_INTENDED;
                 case kFmtRegWOrSp:
                   want_var_size = false;
                   break;
@@ -703,10 +722,10 @@
                   break;
                 case kFmtRegD:
                   want_64_bit = true;
-                  // Intentional fall-through.
+                  FALLTHROUGH_INTENDED;
                 case kFmtRegS:
                   want_var_size = false;
-                  // Intentional fall-through.
+                  FALLTHROUGH_INTENDED;
                 case kFmtRegF:
                   want_float = true;
                   break;
@@ -760,8 +779,8 @@
             // and zr. This means that these two registers do not need any special treatment, as
             // their bottom 5 bits are correctly set to 31 == 0b11111, which is the right
             // value for encoding both sp and zr.
-            COMPILE_ASSERT((rxzr & 0x1f) == 0x1f, rzr_register_number_must_be_31);
-            COMPILE_ASSERT((rsp & 0x1f) == 0x1f, rsp_register_number_must_be_31);
+            static_assert((rxzr & 0x1f) == 0x1f, "rzr register number must be 31");
+            static_assert((rsp & 0x1f) == 0x1f, "rsp register number must be 31");
           }
 
           value = (operand << encoder->field_loc[i].start) &
@@ -787,6 +806,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 +851,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 +858,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 +876,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_IMM26(delta >> 2)) {
             LOG(FATAL) << "Invalid jump range in kFixupT1Branch";
           }
           lir->operands[0] = delta >> 2;
@@ -876,12 +892,74 @@
           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;
+            LIR* targetLIR = lir->target;
+            // "tbz/tbnz Rt, #imm, label" -> "tst Rt, #(1<<imm)".
+            offset_adjustment -= lir->flags.size;
+            int32_t encodedImm = EncodeLogicalImmediate(IS_WIDE(opcode), 1 << lir->operands[1]);
+            DCHECK_NE(encodedImm, -1);
+            lir->opcode = IS_WIDE(opcode) ? WIDE(kA64Tst2rl) : kA64Tst2rl;
+            lir->operands[1] = encodedImm;
+            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, targetLIR);
+            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 +988,7 @@
     }
 
     if (res == kSuccess) {
+      DCHECK_EQ(offset_adjustment, 0);
       break;
     } else {
       assembler_retries++;
@@ -951,7 +1030,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 +1041,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..a9a58a3 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -21,6 +21,8 @@
 #include "dex/quick/mir_to_lir-inl.h"
 #include "gc/accounting/card_table.h"
 #include "entrypoints/quick/quick_entrypoints.h"
+#include "mirror/art_method.h"
+#include "mirror/object_array-inl.h"
 
 namespace art {
 
@@ -44,7 +46,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 +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);
@@ -96,7 +98,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 +110,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,41 +149,6 @@
 }
 
 /*
- * Array data table format:
- *  ushort ident = 0x0300   magic value
- *  ushort width            width of each element in the table
- *  uint   size             number of elements in the table
- *  ubyte  data[size*width] table of data values (may contain a single-byte
- *                          padding at the end)
- *
- * 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;
-  // Add the table to the list - we'll process it later
-  FillArrayData *tab_rec =
-      static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData));
-  tab_rec->table = table;
-  tab_rec->vaddr = current_dalvik_offset_;
-  uint16_t width = tab_rec->table[1];
-  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);
-
-  // Making a call - use explicit registers
-  FlushAllRegs();   /* Everything to home location */
-  LoadValueDirectFixed(rl_src, rs_x0);
-  LoadWordDisp(rs_xSELF, QUICK_ENTRYPOINT_OFFSET(8, pHandleFillArrayData).Int32Value(),
-               rs_xLR);
-  // Materialize a pointer to the fill data image
-  NewLIR3(kA64Adr2xd, rx1, 0, WrapPointer(tab_rec));
-  ClobberCallerSave();
-  LIR* call_inst = OpReg(kOpBlx, rs_xLR);
-  MarkSafepointPC(call_inst);
-}
-
-/*
  * Handle unlocked -> thin locked transition inline or else call out to quick entrypoint. For more
  * details see monitor.cc.
  */
@@ -433,4 +400,119 @@
   NewLIR0(kA64Ret);
 }
 
+static bool Arm64UseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
+  UNUSED(cu, target_method);
+  // Always emit relative calls.
+  return true;
+}
+
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in static & direct invoke sequences.
+ */
+static int Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+                               int state, const MethodReference& target_method,
+                               uint32_t unused_idx,
+                               uintptr_t direct_code, uintptr_t direct_method,
+                               InvokeType type) {
+  UNUSED(info, unused_idx);
+  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 (Arm64UseRelativeCall(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 (Arm64UseRelativeCall(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 Arm64Mir2Lir::GetNextSDCallInsn() {
+  return Arm64NextSDCallInsn;
+}
+
+LIR* Arm64Mir2Lir::CallWithLinkerFixup(const MethodReference& target_method, InvokeType type) {
+  // For ARM64, 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_, kA64Bl1t, 0,
+                     target_method_idx, WrapPointer(const_cast<DexFile*>(target_dex_file)), type);
+  AppendLIR(call);
+  call_method_insns_.push_back(call);
+  return call;
+}
+
+LIR* Arm64Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
+  LIR* call_insn;
+  if (method_info.FastPath() && Arm64UseRelativeCall(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/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 3e1c18b..5182a89 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>
 
@@ -70,6 +71,10 @@
   bool HandleEasyDivRem64(Instruction::Code dalvik_opcode, bool is_div,
                           RegLocation rl_src, RegLocation rl_dest, int64_t lit);
   bool EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE;
+  void GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
+                                  int32_t constant) OVERRIDE;
+  void GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
+                                   int64_t constant) OVERRIDE;
   LIR* CheckSuspendUsingLoad() OVERRIDE;
   RegStorage LoadHelper(QuickEntrypointEnum trampoline) OVERRIDE;
   LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest,
@@ -140,13 +145,13 @@
   void GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
                       RegLocation lr_shift) OVERRIDE;
   void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                         RegLocation rl_src2) OVERRIDE;
+                         RegLocation rl_src2, int flags) OVERRIDE;
   void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index,
                    RegLocation rl_dest, int scale) OVERRIDE;
   void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index,
                    RegLocation rl_src, int scale, bool card_mark) OVERRIDE;
   void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                         RegLocation rl_shift) OVERRIDE;
+                         RegLocation rl_shift, int flags) OVERRIDE;
   void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
                         RegLocation rl_src2) OVERRIDE;
   void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
@@ -167,11 +172,12 @@
   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;
   void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                      RegLocation rl_src2) OVERRIDE;
+                      RegLocation rl_src2, int flags) OVERRIDE;
   RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div)
       OVERRIDE;
   RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div)
@@ -181,13 +187,12 @@
   void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
   void GenExitSequence() OVERRIDE;
   void GenSpecialExitSequence() OVERRIDE;
-  void GenFillArrayData(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;
   void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
                         int32_t true_val, int32_t false_val, RegStorage rs_dest,
-                        int dest_reg_class) OVERRIDE;
+                        RegisterClass dest_reg_class) OVERRIDE;
 
   bool GenMemBarrier(MemBarrierKind barrier_kind) OVERRIDE;
   void GenMonitorEnter(int opt_flags, RegLocation rl_src) OVERRIDE;
@@ -248,15 +253,37 @@
                          uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
                          bool skip_this) OVERRIDE;
 
-  bool WideGPRsAreAliases() OVERRIDE {
+  bool WideGPRsAreAliases() const OVERRIDE {
     return true;  // 64b architecture.
   }
-  bool WideFPRsAreAliases() OVERRIDE {
+  bool WideFPRsAreAliases() const OVERRIDE {
     return true;  // 64b architecture.
   }
 
   size_t GetInstructionOffset(LIR* lir) OVERRIDE;
 
+  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
+   */
+  virtual 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;
 
  private:
@@ -340,8 +367,8 @@
   void InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir);
   void AssignDataOffsets();
   RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
-                        bool is_div, bool check_zero);
-  RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div);
+                        bool is_div, int flags) OVERRIDE;
+  RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) OVERRIDE;
   size_t GetLoadStoreSize(LIR* lir);
 
   bool SmallLiteralDivRem64(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src,
@@ -390,10 +417,12 @@
   void GenNotLong(RegLocation rl_dest, RegLocation rl_src);
   void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
   void GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                     RegLocation rl_src2, bool is_div);
+                     RegLocation rl_src2, bool is_div, int flags);
 
   InToRegStorageMapping in_to_reg_storage_mapping_;
-  static const ArmEncodingMap EncodingMap[kA64Last];
+  static const A64EncodingMap EncodingMap[kA64Last];
+
+  ArenaVector<LIR*> call_method_insns_;
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/arm64/fp_arm64.cc b/compiler/dex/quick/arm64/fp_arm64.cc
index 7268d70..ff692b7 100644
--- a/compiler/dex/quick/arm64/fp_arm64.cc
+++ b/compiler/dex/quick/arm64/fp_arm64.cc
@@ -112,7 +112,33 @@
   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);
+}
+
+void Arm64Mir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
+                                              int32_t constant) {
+  RegLocation rl_result;
+  RegStorage r_tmp = AllocTempSingle();
+  LoadConstantNoClobber(r_tmp, constant);
+  rl_src1 = LoadValue(rl_src1, kFPReg);
+  rl_result = EvalLoc(rl_dest, kFPReg, true);
+  NewLIR3(kA64Fmul3fff, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), r_tmp.GetReg());
+  StoreValue(rl_dest, rl_result);
+}
+
+void Arm64Mir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
+                                               int64_t constant) {
+  RegLocation rl_result;
+  RegStorage r_tmp = AllocTempDouble();
+  DCHECK(r_tmp.IsDouble());
+  LoadConstantWide(r_tmp, constant);
+  rl_src1 = LoadValueWide(rl_src1, kFPReg);
+  DCHECK(rl_src1.wide);
+  rl_result = EvalLocWide(rl_dest, kFPReg, true);
+  DCHECK(rl_dest.wide);
+  DCHECK(rl_result.wide);
+  NewLIR3(WIDE(kA64Fmul3fff), rl_result.reg.GetReg(), rl_src1.reg.GetReg(), r_tmp.GetReg());
   StoreValueWide(rl_dest, rl_result);
 }
 
@@ -145,17 +171,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 +196,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 +234,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 +307,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 +344,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 +379,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 +396,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 +410,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 +420,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 +430,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 +440,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 +465,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..e57f99c 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -21,7 +21,7 @@
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "mirror/array.h"
+#include "mirror/array-inl.h"
 #include "utils.h"
 
 namespace art {
@@ -32,11 +32,13 @@
 }
 
 LIR* Arm64Mir2Lir::OpIT(ConditionCode ccode, const char* guide) {
+  UNUSED(ccode, guide);
   LOG(FATAL) << "Unexpected use of OpIT for Arm64";
-  return NULL;
+  UNREACHABLE();
 }
 
 void Arm64Mir2Lir::OpEndIT(LIR* it) {
+  UNUSED(it);
   LOG(FATAL) << "Unexpected use of OpEndIT for Arm64";
 }
 
@@ -174,13 +176,14 @@
 
 void Arm64Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
                                     int32_t true_val, int32_t false_val, RegStorage rs_dest,
-                                    int dest_reg_class) {
+                                    RegisterClass dest_reg_class) {
   DCHECK(rs_dest.Valid());
   OpRegReg(kOpCmp, left_op, right_op);
   GenSelect(true_val, false_val, code, rs_dest, dest_reg_class);
 }
 
 void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
+  UNUSED(bb);
   RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
   rl_src = LoadValue(rl_src, rl_src.ref ? kRefReg : kCoreReg);
   // rl_src may be aliased with rl_result/rl_dest, so do compare early.
@@ -262,17 +265,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 +308,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 +336,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.
@@ -402,6 +409,7 @@
 // Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
 bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                       RegLocation rl_src, RegLocation rl_dest, int lit) {
+  UNUSED(dalvik_opcode);
   if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) {
     return false;
   }
@@ -446,6 +454,7 @@
 
 bool Arm64Mir2Lir::SmallLiteralDivRem64(Instruction::Code dalvik_opcode, bool is_div,
                                         RegLocation rl_src, RegLocation rl_dest, int64_t lit) {
+  UNUSED(dalvik_opcode);
   if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) {
     return false;
   }
@@ -585,13 +594,16 @@
 }
 
 bool Arm64Mir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) {
+  UNUSED(rl_src, rl_dest, lit);
   LOG(FATAL) << "Unexpected use of EasyMultiply for Arm64";
-  return false;
+  UNREACHABLE();
 }
 
-RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) {
+RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit,
+                                       bool is_div) {
+  UNUSED(rl_dest, rl_src1, lit, is_div);
   LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm64";
-  return rl_dest;
+  UNREACHABLE();
 }
 
 RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) {
@@ -609,9 +621,10 @@
 }
 
 RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
-                                    RegLocation rl_src2, bool is_div, bool check_zero) {
+                                    RegLocation rl_src2, bool is_div, int flags) {
+  UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags);
   LOG(FATAL) << "Unexpected use of GenDivRem for Arm64";
-  return rl_dest;
+  UNREACHABLE();
 }
 
 RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegStorage r_src2,
@@ -625,7 +638,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 +654,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 +778,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 +884,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);
@@ -906,29 +933,31 @@
 
 LIR* Arm64Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
   ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
-  return RawLIR(current_dalvik_offset_, WIDE(kA64Ldr2rp), reg.GetReg(), 0, 0, 0, 0, target);
+  return RawLIR(current_dalvik_offset_, kA64Ldr2rp, As32BitReg(reg).GetReg(), 0, 0, 0, 0, target);
 }
 
 LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base, int count) {
+  UNUSED(r_base, count);
   LOG(FATAL) << "Unexpected use of OpVldm for Arm64";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* Arm64Mir2Lir::OpVstm(RegStorage r_base, int count) {
+  UNUSED(r_base, count);
   LOG(FATAL) << "Unexpected use of OpVstm for Arm64";
-  return NULL;
+  UNREACHABLE();
 }
 
 void Arm64Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
-                                               RegLocation rl_result, int lit,
-                                               int first_bit, int second_bit) {
+                                                 RegLocation rl_result, int lit ATTRIBUTE_UNUSED,
+                                                 int first_bit, int second_bit) {
   OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsl, second_bit - first_bit));
   if (first_bit != 0) {
     OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
   }
 }
 
-void Arm64Mir2Lir::GenDivZeroCheckWide(RegStorage reg) {
+void Arm64Mir2Lir::GenDivZeroCheckWide(RegStorage reg ATTRIBUTE_UNUSED) {
   LOG(FATAL) << "Unexpected use of GenDivZero for Arm64";
 }
 
@@ -943,7 +972,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);
@@ -1001,7 +1030,7 @@
 }
 
 void Arm64Mir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest,
-                                 RegLocation rl_src1, RegLocation rl_src2, bool is_div) {
+                                 RegLocation rl_src1, RegLocation rl_src2, bool is_div, int flags) {
   if (rl_src2.is_const) {
     DCHECK(rl_src2.wide);
     int64_t lit = mir_graph_->ConstantValueWide(rl_src2);
@@ -1013,7 +1042,9 @@
   RegLocation rl_result;
   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
-  GenDivZeroCheck(rl_src2.reg);
+  if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
+    GenDivZeroCheck(rl_src2.reg);
+  }
   rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, is_div);
   StoreValueWide(rl_dest, rl_result);
 }
@@ -1048,7 +1079,7 @@
 }
 
 void Arm64Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                                  RegLocation rl_src1, RegLocation rl_src2) {
+                                  RegLocation rl_src1, RegLocation rl_src2, int flags) {
   switch (opcode) {
     case Instruction::NOT_LONG:
       GenNotLong(rl_dest, rl_src2);
@@ -1067,11 +1098,11 @@
       return;
     case Instruction::DIV_LONG:
     case Instruction::DIV_LONG_2ADDR:
-      GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true);
+      GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags);
       return;
     case Instruction::REM_LONG:
     case Instruction::REM_LONG_2ADDR:
-      GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false);
+      GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags);
       return;
     case Instruction::AND_LONG_2ADDR:
     case Instruction::AND_LONG:
@@ -1160,7 +1191,6 @@
     } else {
       LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size, kNotVolatile);
     }
-    MarkPossibleNullPointerException(opt_flags);
     if (!constant_index) {
       FreeTemp(reg_ptr);
     }
@@ -1185,7 +1215,6 @@
     } else {
       LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size);
     }
-    MarkPossibleNullPointerException(opt_flags);
     FreeTemp(reg_ptr);
     StoreValue(rl_dest, rl_result);
   }
@@ -1268,7 +1297,6 @@
     } else {
       StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size, kNotVolatile);
     }
-    MarkPossibleNullPointerException(opt_flags);
   } else {
     /* reg_ptr -> array data */
     OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
@@ -1282,7 +1310,6 @@
     } else {
       StoreBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale, size);
     }
-    MarkPossibleNullPointerException(opt_flags);
   }
   if (allocated_reg_ptr_temp) {
     FreeTemp(reg_ptr);
@@ -1293,7 +1320,8 @@
 }
 
 void Arm64Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
-                                     RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) {
+                                     RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift,
+                                     int flags ATTRIBUTE_UNUSED) {
   OpKind op = kOpBkpt;
   // Per spec, we only care about low 6 bits of shift amount.
   int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
@@ -1325,7 +1353,7 @@
 }
 
 void Arm64Mir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                                     RegLocation rl_src1, RegLocation rl_src2) {
+                                     RegLocation rl_src1, RegLocation rl_src2, int flags) {
   OpKind op = kOpBkpt;
   switch (opcode) {
     case Instruction::ADD_LONG:
@@ -1354,7 +1382,7 @@
 
   if (op == kOpSub) {
     if (!rl_src2.is_const) {
-      return GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+      return GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
     }
   } else {
     // Associativity.
@@ -1440,7 +1468,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(),
@@ -1449,8 +1477,8 @@
   }
 }
 
-static int SpillRegsPreSub(Arm64Mir2Lir* m2l, RegStorage base, uint32_t core_reg_mask,
-                           uint32_t fp_reg_mask, int frame_size) {
+static int SpillRegsPreSub(Arm64Mir2Lir* m2l, uint32_t core_reg_mask, uint32_t fp_reg_mask,
+                           int frame_size) {
   m2l->OpRegRegImm(kOpSub, rs_sp, rs_sp, frame_size);
 
   int core_count = POPCOUNT(core_reg_mask);
@@ -1472,7 +1500,7 @@
 }
 
 static int SpillRegsPreIndexed(Arm64Mir2Lir* m2l, RegStorage base, uint32_t core_reg_mask,
-                               uint32_t fp_reg_mask, int frame_size) {
+                               uint32_t fp_reg_mask) {
   // Otherwise, spill both core and fp regs at the same time.
   // The very first instruction will be an stp with pre-indexed address, moving the stack pointer
   // down. From then on, we fill upwards. This will generate overall the same number of instructions
@@ -1551,7 +1579,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 {
@@ -1595,9 +1623,9 @@
   // This case is also optimal when we have an odd number of core spills, and an even (non-zero)
   // number of fp spills.
   if ((RoundUp(frame_size, 8) / 8 <= 63)) {
-    return SpillRegsPreSub(this, base, core_reg_mask, fp_reg_mask, frame_size);
+    return SpillRegsPreSub(this, core_reg_mask, fp_reg_mask, frame_size);
   } else {
-    return SpillRegsPreIndexed(this, base, core_reg_mask, fp_reg_mask, frame_size);
+    return SpillRegsPreIndexed(this, base, core_reg_mask, fp_reg_mask);
   }
 }
 
@@ -1624,7 +1652,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(),
@@ -1635,6 +1663,7 @@
 
 void Arm64Mir2Lir::UnspillRegs(RegStorage base, uint32_t core_reg_mask, uint32_t fp_reg_mask,
                                int frame_size) {
+  DCHECK_EQ(base, rs_sp);
   // Restore saves and drop stack frame.
   // 2 versions:
   //
@@ -1686,13 +1715,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..094ff51 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);
@@ -578,14 +585,14 @@
 }
 
 Arm64Mir2Lir::Arm64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
-    : Mir2Lir(cu, mir_graph, arena) {
+    : Mir2Lir(cu, mir_graph, arena),
+      call_method_insns_(arena->Adapter()) {
   // Sanity check - make sure encoding map lines up.
   for (int i = 0; i < kA64Last; i++) {
-    if (UNWIDE(Arm64Mir2Lir::EncodingMap[i].opcode) != i) {
-      LOG(FATAL) << "Encoding order for " << Arm64Mir2Lir::EncodingMap[i].name
-                 << " is wrong: expecting " << i << ", seeing "
-                 << static_cast<int>(Arm64Mir2Lir::EncodingMap[i].opcode);
-    }
+    DCHECK_EQ(UNWIDE(Arm64Mir2Lir::EncodingMap[i].opcode), i)
+        << "Encoding order for " << Arm64Mir2Lir::EncodingMap[i].name
+        << " is wrong: expecting " << i << ", seeing "
+        << static_cast<int>(Arm64Mir2Lir::EncodingMap[i].opcode);
   }
 }
 
@@ -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,
@@ -1199,4 +1201,21 @@
   return call_state;
 }
 
+void Arm64Mir2Lir::InstallLiteralPools() {
+  // PC-relative calls to methods.
+  patches_.reserve(call_method_insns_.size());
+  for (LIR* p : call_method_insns_) {
+      DCHECK_EQ(p->opcode, kA64Bl1t);
+      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/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc
index f58f830..78a6df8 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;
@@ -306,7 +306,7 @@
 // algorithm will give it a low priority for promotion, even when it is referenced many times in
 // the code.
 
-bool Arm64Mir2Lir::InexpensiveConstantInt(int32_t value) {
+bool Arm64Mir2Lir::InexpensiveConstantInt(int32_t value ATTRIBUTE_UNUSED) {
   // A 32-bit int can always be loaded with 2 instructions (and without using the literal pool).
   // We therefore return true and give it a low priority for promotion.
   return true;
@@ -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));
@@ -677,24 +673,29 @@
   }
 }
 
-LIR* Arm64Mir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, MoveType move_type) {
+LIR* Arm64Mir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset,
+                               MoveType move_type) {
+  UNUSED(r_dest, r_base, offset, move_type);
   UNIMPLEMENTED(FATAL);
-  return nullptr;
+  UNREACHABLE();
 }
 
-LIR* Arm64Mir2Lir::OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, MoveType move_type) {
+LIR* Arm64Mir2Lir::OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src,
+                               MoveType move_type) {
+  UNUSED(r_base, offset, r_src, move_type);
   UNIMPLEMENTED(FATAL);
   return nullptr;
 }
 
 LIR* Arm64Mir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src) {
+  UNUSED(op, cc, r_dest, r_src);
   LOG(FATAL) << "Unexpected use of OpCondRegReg for Arm64";
-  return NULL;
+  UNREACHABLE();
 }
 
 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 +748,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 +763,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:
@@ -772,10 +773,10 @@
       opcode = kA64Sub4RRre;
       break;
     default:
-      LOG(FATAL) << "Unimplemented opcode: " << op;
-      break;
+      UNIMPLEMENTED(FATAL) << "Unimplemented opcode: " << op;
+      UNREACHABLE();
   }
-  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 +811,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) {
@@ -837,7 +838,7 @@
                      value);
     case kOpAdd:
       neg = !neg;
-      // Note: intentional fallthrough
+      FALLTHROUGH_INTENDED;
     case kOpSub:
       // Add and sub below read/write sp rather than xzr.
       if (abs_value < 0x1000) {
@@ -937,9 +938,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 +1026,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 +1041,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 +1114,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 +1129,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 +1198,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 +1210,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);
@@ -1265,7 +1266,7 @@
 
   // TODO: in future may need to differentiate Dalvik accesses w/ spills
   if (mem_ref_type_ == ResourceMask::kDalvikReg) {
-    DCHECK(r_base == rs_sp);
+    DCHECK_EQ(r_base, rs_sp);
     AnnotateDalvikRegAccess(load, displacement >> 2, true /* is_load */, r_dest.Is64Bit());
   }
   return load;
@@ -1294,8 +1295,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 +1307,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.
@@ -1356,7 +1357,7 @@
 
   // TODO: In future, may need to differentiate Dalvik & spill accesses.
   if (mem_ref_type_ == ResourceMask::kDalvikReg) {
-    DCHECK(r_base == rs_sp);
+    DCHECK_EQ(r_base, rs_sp);
     AnnotateDalvikRegAccess(store, displacement >> 2, false /* is_load */, r_src.Is64Bit());
   }
   return store;
@@ -1390,16 +1391,20 @@
 }
 
 LIR* Arm64Mir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
+  UNUSED(r_dest, r_src);
   LOG(FATAL) << "Unexpected use of OpFpRegCopy for Arm64";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* Arm64Mir2Lir::OpMem(OpKind op, RegStorage r_base, int disp) {
+  UNUSED(op, r_base, disp);
   LOG(FATAL) << "Unexpected use of OpMem for Arm64";
-  return NULL;
+  UNREACHABLE();
 }
 
-LIR* Arm64Mir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) {
+LIR* Arm64Mir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt,
+                                    QuickEntrypointEnum trampoline ATTRIBUTE_UNUSED) {
+  // The address of the trampoline is already loaded into r_tgt.
   return OpReg(op, r_tgt);
 }
 
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index ee1c467..9403516 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"
@@ -104,17 +105,17 @@
 void Mir2Lir::UnlinkLIR(LIR* lir) {
   if (UNLIKELY(lir == first_lir_insn_)) {
     first_lir_insn_ = lir->next;
-    if (lir->next != NULL) {
-      lir->next->prev = NULL;
+    if (lir->next != nullptr) {
+      lir->next->prev = nullptr;
     } else {
-      DCHECK(lir->next == NULL);
+      DCHECK(lir->next == nullptr);
       DCHECK(lir == last_lir_insn_);
-      last_lir_insn_ = NULL;
+      last_lir_insn_ = nullptr;
     }
   } else if (lir == last_lir_insn_) {
     last_lir_insn_ = lir->prev;
-    lir->prev->next = NULL;
-  } else if ((lir->prev != NULL) && (lir->next != NULL)) {
+    lir->prev->next = nullptr;
+  } else if ((lir->prev != nullptr) && (lir->next != nullptr)) {
     lir->prev->next = lir->next;
     lir->next->prev = lir->prev;
   }
@@ -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();
@@ -332,10 +334,10 @@
             << static_cast<float>(total_size_) / static_cast<float>(insns_size * 2);
   DumpPromotionMap();
   UpdateLIROffsets();
-  for (lir_insn = first_lir_insn_; lir_insn != NULL; lir_insn = lir_insn->next) {
+  for (lir_insn = first_lir_insn_; lir_insn != nullptr; lir_insn = lir_insn->next) {
     DumpLIRInsn(lir_insn, 0);
   }
-  for (lir_insn = literal_list_; lir_insn != NULL; lir_insn = lir_insn->next) {
+  for (lir_insn = literal_list_; lir_insn != nullptr; lir_insn = lir_insn->next) {
     LOG(INFO) << StringPrintf("%x (%04x): .word (%#x)", lir_insn->offset, lir_insn->offset,
                               lir_insn->operands[0]);
   }
@@ -366,13 +368,13 @@
       return data_target;
     data_target = data_target->next;
   }
-  return NULL;
+  return nullptr;
 }
 
 /* Search the existing constants in the literal pool for an exact wide match */
 LIR* Mir2Lir::ScanLiteralPoolWide(LIR* data_target, int val_lo, int val_hi) {
   bool lo_match = false;
-  LIR* lo_target = NULL;
+  LIR* lo_target = nullptr;
   while (data_target) {
     if (lo_match && (data_target->operands[0] == val_hi)) {
       // Record high word in case we need to expand this later.
@@ -386,7 +388,7 @@
     }
     data_target = data_target->next;
   }
-  return NULL;
+  return nullptr;
 }
 
 /* Search the existing constants in the literal pool for an exact method match */
@@ -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.
@@ -417,7 +431,7 @@
     estimated_native_code_size_ += sizeof(value);
     return new_value;
   }
-  return NULL;
+  return nullptr;
 }
 
 /* Add a 64-bit constant to the constant pool or mixed with code */
@@ -433,15 +447,16 @@
   buf.push_back((data >> 24) & 0xff);
 }
 
-// Push 8 bytes on 64-bit target systems; 4 on 32-bit target systems.
-static void PushPointer(std::vector<uint8_t>&buf, const void* pointer, bool target64) {
-  uint64_t data = reinterpret_cast<uintptr_t>(pointer);
-  if (target64) {
-    Push32(buf, data & 0xFFFFFFFF);
-    Push32(buf, (data >> 32) & 0xFFFFFFFF);
-  } else {
-    Push32(buf, static_cast<uint32_t>(data));
-  }
+/**
+ * @brief Push a compressed reference which needs patching at link/patchoat-time.
+ * @details This needs to be kept consistent with the code which actually does the patching in
+ *   oat_writer.cc and in the patchoat tool.
+ */
+static void PushUnpatchedReference(std::vector<uint8_t>&buf) {
+  // Note that we can safely initialize the patches to zero. The code deduplication mechanism takes
+  // the patches into account when determining whether two pieces of codes are functionally
+  // equivalent.
+  Push32(buf, UINT32_C(0));
 }
 
 static void AlignBuffer(std::vector<uint8_t>&buf, size_t offset) {
@@ -454,69 +469,48 @@
 void Mir2Lir::InstallLiteralPools() {
   AlignBuffer(code_buffer_, data_offset_);
   LIR* data_lir = literal_list_;
-  while (data_lir != NULL) {
+  while (data_lir != nullptr) {
     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) {
+  while (data_lir != nullptr) {
     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());
-    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);
+    patches_.push_back(LinkerPatch::CodePatch(code_buffer_.size(),
+                                              target_dex_file, target_method_idx));
+    PushUnpatchedReference(code_buffer_);
     data_lir = NEXT_LIR(data_lir);
   }
   data_lir = method_literal_list_;
-  while (data_lir != NULL) {
+  while (data_lir != nullptr) {
     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());
-    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);
+    patches_.push_back(LinkerPatch::MethodPatch(code_buffer_.size(),
+                                                target_dex_file, target_method_idx));
+    PushUnpatchedReference(code_buffer_);
     data_lir = NEXT_LIR(data_lir);
   }
   // 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);
-    // unique value based on target to ensure code deduplication works
-    PushPointer(code_buffer_, &target_method_id, cu_->target64);
+  while (data_lir != nullptr) {
+    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));
+    PushUnpatchedReference(code_buffer_);
     data_lir = NEXT_LIR(data_lir);
   }
 }
 
 /* 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 +567,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);
@@ -586,7 +577,7 @@
 }
 
 static int AssignLiteralOffsetCommon(LIR* lir, CodeOffset offset) {
-  for (; lir != NULL; lir = lir->next) {
+  for (; lir != nullptr; lir = lir->next) {
     lir->offset = offset;
     offset += 4;
   }
@@ -597,7 +588,7 @@
                                             unsigned int element_size) {
   // Align to natural pointer size.
   offset = RoundUp(offset, element_size);
-  for (; lir != NULL; lir = lir->next) {
+  for (; lir != nullptr; lir = lir->next) {
     lir->offset = offset;
     offset += element_size;
   }
@@ -640,15 +631,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)) {
+  for (LIR* tgt_lir = first_lir_insn_; tgt_lir != nullptr; 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 +664,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;
@@ -683,7 +682,11 @@
   pc2dex_dalvik_offset = 0u;
   dex2pc_offset = 0u;
   dex2pc_dalvik_offset = 0u;
-  for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
+  for (LIR* tgt_lir = first_lir_insn_; tgt_lir != nullptr; 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);
@@ -714,7 +717,7 @@
     CHECK_EQ(table.PcToDexSize(), pc2dex_entries);
     auto it = table.PcToDexBegin();
     auto it2 = table.DexToPcBegin();
-    for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
+    for (LIR* tgt_lir = first_lir_insn_; tgt_lir != nullptr; tgt_lir = NEXT_LIR(tgt_lir)) {
       if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) {
         CHECK_EQ(tgt_lir->offset, it.NativePcOffset());
         CHECK_EQ(tgt_lir->dalvik_offset, it.DexPc());
@@ -755,7 +758,7 @@
     uint32_t native_offset = it.NativePcOffset();
     uint32_t dex_pc = it.DexPc();
     const uint8_t* references = dex_gc_map.FindBitMap(dex_pc, false);
-    CHECK(references != NULL) << "Missing ref for dex pc 0x" << std::hex << dex_pc <<
+    CHECK(references != nullptr) << "Missing ref for dex pc 0x" << std::hex << dex_pc <<
         ": " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
     native_gc_map_builder.AddEntry(native_offset, references);
   }
@@ -764,7 +767,9 @@
 /* Determine the offset of each literal field */
 int Mir2Lir::AssignLiteralOffset(CodeOffset offset) {
   offset = AssignLiteralOffsetCommon(literal_list_, offset);
-  unsigned int ptr_size = GetInstructionSetPointerSize(cu_->instruction_set);
+  constexpr unsigned int ptr_size = sizeof(uint32_t);
+  static_assert(ptr_size >= sizeof(mirror::HeapReference<mirror::Object>),
+                "Pointer size cannot hold a heap reference");
   offset = AssignLiteralPointerOffsetCommon(code_literal_list_, offset, ptr_size);
   offset = AssignLiteralPointerOffsetCommon(method_literal_list_, offset, ptr_size);
   offset = AssignLiteralPointerOffsetCommon(class_literal_list_, offset, ptr_size);
@@ -772,10 +777,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 +791,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 +848,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) {
@@ -908,6 +904,7 @@
 
 /* Set up special LIR to mark a Dalvik byte-code instruction start for pretty printing */
 void Mir2Lir::MarkBoundary(DexOffset offset, const char* inst_str) {
+  UNUSED(offset);
   // NOTE: only used for debug listings.
   NewLIR1(kPseudoDalvikByteCodeBoundary, WrapPointer(ArenaStrdup(inst_str)));
 }
@@ -929,7 +926,7 @@
     case Instruction::IF_LEZ: is_taken = (src1 <= 0); break;
     default:
       LOG(FATAL) << "Unexpected opcode " << opcode;
-      is_taken = false;
+      UNREACHABLE();
   }
   return is_taken;
 }
@@ -945,8 +942,8 @@
     case kCondLe: res = kCondGe; break;
     case kCondGe: res = kCondLe; break;
     default:
-      res = static_cast<ConditionCode>(0);
       LOG(FATAL) << "Unexpected ccode " << before;
+      UNREACHABLE();
   }
   return res;
 }
@@ -961,8 +958,8 @@
     case kCondLe: res = kCondGt; break;
     case kCondGe: res = kCondLt; break;
     default:
-      res = static_cast<ConditionCode>(0);
       LOG(FATAL) << "Unexpected ccode " << before;
+      UNREACHABLE();
   }
   return res;
 }
@@ -970,40 +967,47 @@
 // TODO: move to mir_to_lir.cc
 Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
     : Backend(arena),
-      literal_list_(NULL),
-      method_literal_list_(NULL),
-      class_literal_list_(NULL),
-      code_literal_list_(NULL),
-      first_fixup_(NULL),
+      literal_list_(nullptr),
+      method_literal_list_(nullptr),
+      class_literal_list_(nullptr),
+      code_literal_list_(nullptr),
+      first_fixup_(nullptr),
       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),
+      block_label_list_(nullptr),
+      promotion_map_(nullptr),
       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),
       core_spill_mask_(0),
       fp_spill_mask_(0),
-      first_lir_insn_(NULL),
-      last_lir_insn_(NULL),
-      slow_paths_(arena, 32, kGrowableArraySlowPaths),
+      first_lir_insn_(nullptr),
+      last_lir_insn_(nullptr),
+      slow_paths_(arena->Adapter(kArenaAllocSlowPaths)),
       mem_ref_type_(ResourceMask::kHeapRef),
       mask_cache_(arena) {
-  // Reserve pointer id 0 for NULL.
-  size_t null_idx = WrapPointer(NULL);
+  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 nullptr.
+  size_t null_idx = WrapPointer(nullptr);
   DCHECK_EQ(null_idx, 0U);
 }
 
@@ -1077,11 +1081,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 +1106,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 +1115,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);
@@ -1115,14 +1127,14 @@
  * unit
  */
 void Mir2Lir::AppendLIR(LIR* lir) {
-  if (first_lir_insn_ == NULL) {
-    DCHECK(last_lir_insn_ == NULL);
+  if (first_lir_insn_ == nullptr) {
+    DCHECK(last_lir_insn_ == nullptr);
     last_lir_insn_ = first_lir_insn_ = lir;
-    lir->prev = lir->next = NULL;
+    lir->prev = lir->next = nullptr;
   } else {
     last_lir_insn_->next = lir;
     lir->prev = last_lir_insn_;
-    lir->next = NULL;
+    lir->next = nullptr;
     last_lir_insn_ = lir;
   }
 }
@@ -1134,7 +1146,7 @@
  * prev_lir <-> new_lir <-> current_lir
  */
 void Mir2Lir::InsertLIRBefore(LIR* current_lir, LIR* new_lir) {
-  DCHECK(current_lir->prev != NULL);
+  DCHECK(current_lir->prev != nullptr);
   LIR *prev_lir = current_lir->prev;
 
   prev_lir->next = new_lir;
@@ -1174,12 +1186,18 @@
   return bit_posn;
 }
 
-bool Mir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest) {
+bool Mir2Lir::PartiallyIntersects(RegLocation rl_src, RegLocation rl_dest) {
   DCHECK(rl_src.wide);
   DCHECK(rl_dest.wide);
   return (abs(mir_graph_->SRegToVReg(rl_src.s_reg_low) - mir_graph_->SRegToVReg(rl_dest.s_reg_low)) == 1);
 }
 
+bool Mir2Lir::Intersects(RegLocation rl_src, RegLocation rl_dest) {
+  DCHECK(rl_src.wide);
+  DCHECK(rl_dest.wide);
+  return (abs(mir_graph_->SRegToVReg(rl_src.s_reg_low) - mir_graph_->SRegToVReg(rl_dest.s_reg_low)) <= 1);
+}
+
 LIR *Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg,
                                 int offset, int check_value, LIR* target, LIR** compare) {
   // Handle this for architectures that can't compare to memory.
@@ -1192,13 +1210,14 @@
 }
 
 void Mir2Lir::AddSlowPath(LIRSlowPath* slowpath) {
-  slow_paths_.Insert(slowpath);
+  slow_paths_.push_back(slowpath);
+  ResetDefTracking();
 }
 
 void Mir2Lir::LoadCodeAddress(const MethodReference& target_method, InvokeType type,
                               SpecialTargetRegister symbolic_reg) {
   LIR* data_target = ScanLiteralPoolMethod(code_literal_list_, target_method);
-  if (data_target == NULL) {
+  if (data_target == nullptr) {
     data_target = AddWordData(&code_literal_list_, target_method.dex_method_index);
     data_target->operands[1] = WrapPointer(const_cast<DexFile*>(target_method.dex_file));
     // NOTE: The invoke type doesn't contribute to the literal identity. In fact, we can have
@@ -1215,7 +1234,7 @@
 void Mir2Lir::LoadMethodAddress(const MethodReference& target_method, InvokeType type,
                                 SpecialTargetRegister symbolic_reg) {
   LIR* data_target = ScanLiteralPoolMethod(method_literal_list_, target_method);
-  if (data_target == NULL) {
+  if (data_target == nullptr) {
     data_target = AddWordData(&method_literal_list_, target_method.dex_method_index);
     data_target->operands[1] = WrapPointer(const_cast<DexFile*>(target_method.dex_file));
     // NOTE: The invoke type doesn't contribute to the literal identity. In fact, we can have
@@ -1229,18 +1248,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;
 }
@@ -1271,7 +1292,9 @@
 }
 
 void Mir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) {
+  UNUSED(bb, mir);
   LOG(FATAL) << "Unknown MIR opcode not supported on this architecture";
+  UNREACHABLE();
 }
 
 }  // namespace art
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 34ed0f4..e12d305 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"
 
@@ -66,40 +68,41 @@
     false,  // kIntrinsicUnsafePut
     true,   // kIntrinsicSystemArrayCopyCharArray
 };
-COMPILE_ASSERT(arraysize(kIntrinsicIsStatic) == kInlineOpNop, check_arraysize_kIntrinsicIsStatic);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicDoubleCvt], DoubleCvt_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicFloatCvt], FloatCvt_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicReverseBits], ReverseBits_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicReverseBytes], ReverseBytes_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsInt], AbsInt_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsLong], AbsLong_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsFloat], AbsFloat_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsDouble], AbsDouble_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxInt], MinMaxInt_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxLong], MinMaxLong_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], MinMaxFloat_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], MinMaxDouble_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicSqrt], Sqrt_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicCeil], Ceil_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicFloor], Floor_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRint], Rint_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRoundFloat], RoundFloat_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRoundDouble], RoundDouble_must_be_static);
-COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicReferenceGetReferent], Get_must_not_be_static);
-COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCharAt], CharAt_must_not_be_static);
-COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCompareTo], CompareTo_must_not_be_static);
-COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], IsEmptyOrLength_must_not_be_static);
-COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicIndexOf], IndexOf_must_not_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicCurrentThread], CurrentThread_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicPeek], Peek_must_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicPoke], Poke_must_be_static);
-COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCas], Cas_must_not_be_static);
-COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], UnsafeGet_must_not_be_static);
-COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicUnsafePut], UnsafePut_must_not_be_static);
-COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray],
-               SystemArrayCopyCharArray_must_be_static);
+static_assert(arraysize(kIntrinsicIsStatic) == kInlineOpNop,
+              "arraysize of kIntrinsicIsStatic unexpected");
+static_assert(kIntrinsicIsStatic[kIntrinsicDoubleCvt], "DoubleCvt must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicFloatCvt], "FloatCvt must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicReverseBits], "ReverseBits must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicReverseBytes], "ReverseBytes must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAbsInt], "AbsInt must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAbsLong], "AbsLong must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAbsFloat], "AbsFloat must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAbsDouble], "AbsDouble must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxInt], "MinMaxInt must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxLong], "MinMaxLong_must_be_static");
+static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], "MinMaxFloat_must_be_static");
+static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], "MinMaxDouble_must_be_static");
+static_assert(kIntrinsicIsStatic[kIntrinsicSqrt], "Sqrt must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicCeil], "Ceil must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicFloor], "Floor must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicRint], "Rint must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicRoundFloat], "RoundFloat must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicRoundDouble], "RoundDouble must be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicReferenceGetReferent], "Get must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicCharAt], "CharAt must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicCompareTo], "CompareTo must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], "IsEmptyOrLength must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicIndexOf], "IndexOf must not be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicCurrentThread], "CurrentThread must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicPeek], "Peek must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicPoke], "Poke must be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicCas], "Cas must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet_must_not_be_static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray],
+              "SystemArrayCopyCharArray must be static");
 
-MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke, MIR* move_return) {
+MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke) {
   MIR* insn = mir_graph->NewMIR();
   insn->offset = invoke->offset;
   insn->optimization_flags = MIR_CALLEE;
@@ -393,12 +396,15 @@
 DexFileMethodInliner::DexFileMethodInliner()
     : lock_("DexFileMethodInliner lock", kDexFileMethodInlinerLock),
       dex_file_(NULL) {
-  COMPILE_ASSERT(kClassCacheFirst == 0, kClassCacheFirst_not_0);
-  COMPILE_ASSERT(arraysize(kClassCacheNames) == kClassCacheLast, bad_arraysize_kClassCacheNames);
-  COMPILE_ASSERT(kNameCacheFirst == 0, kNameCacheFirst_not_0);
-  COMPILE_ASSERT(arraysize(kNameCacheNames) == kNameCacheLast, bad_arraysize_kNameCacheNames);
-  COMPILE_ASSERT(kProtoCacheFirst == 0, kProtoCacheFirst_not_0);
-  COMPILE_ASSERT(arraysize(kProtoCacheDefs) == kProtoCacheLast, bad_arraysize_kProtoCacheNames);
+  static_assert(kClassCacheFirst == 0, "kClassCacheFirst not 0");
+  static_assert(arraysize(kClassCacheNames) == kClassCacheLast,
+                "bad arraysize for kClassCacheNames");
+  static_assert(kNameCacheFirst == 0, "kNameCacheFirst not 0");
+  static_assert(arraysize(kNameCacheNames) == kNameCacheLast,
+                "bad arraysize for kNameCacheNames");
+  static_assert(kProtoCacheFirst == 0, "kProtoCacheFirst not 0");
+  static_assert(arraysize(kProtoCacheDefs) == kProtoCacheLast,
+                "bad arraysize kProtoCacheNames");
 }
 
 DexFileMethodInliner::~DexFileMethodInliner() {
@@ -553,19 +559,33 @@
       break;
     case kInlineOpIGet:
       move_result = mir_graph->FindMoveResult(bb, invoke);
-      result = GenInlineIGet(mir_graph, bb, invoke, move_result, method, method_idx);
+      result = GenInlineIGet(mir_graph, bb, invoke, move_result, method);
       break;
     case kInlineOpIPut:
       move_result = mir_graph->FindMoveResult(bb, invoke);
-      result = GenInlineIPut(mir_graph, bb, invoke, move_result, method, method_idx);
+      result = GenInlineIPut(mir_graph, bb, invoke, move_result, method);
       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);
     }
   }
@@ -721,7 +741,7 @@
              method.d.data == 0u));
 
   // Insert the CONST instruction.
-  MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
+  MIR* insn = AllocReplacementMIR(mir_graph, invoke);
   insn->dalvikInsn.opcode = Instruction::CONST;
   insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
   insn->dalvikInsn.vB = method.d.data;
@@ -759,7 +779,7 @@
   }
 
   // Insert the move instruction
-  MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
+  MIR* insn = AllocReplacementMIR(mir_graph, invoke);
   insn->dalvikInsn.opcode = opcode;
   insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
   insn->dalvikInsn.vB = arg;
@@ -768,8 +788,7 @@
 }
 
 bool DexFileMethodInliner::GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
-                                         MIR* move_result, const InlineMethod& method,
-                                         uint32_t method_idx) {
+                                         MIR* move_result, const InlineMethod& method) {
   CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit();
   if (cu->enable_debug & (1 << kDebugSlowFieldPath)) {
     return false;
@@ -803,7 +822,7 @@
     invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
   }
 
-  MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
+  MIR* insn = AllocReplacementMIR(mir_graph, invoke);
   insn->offset = invoke->offset;
   insn->dalvikInsn.opcode = opcode;
   insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
@@ -820,8 +839,7 @@
 }
 
 bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
-                                         MIR* move_result, const InlineMethod& method,
-                                         uint32_t method_idx) {
+                                         MIR* move_result, const InlineMethod& method) {
   CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit();
   if (cu->enable_debug & (1 << kDebugSlowFieldPath)) {
     return false;
@@ -865,7 +883,7 @@
     invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
   }
 
-  MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
+  MIR* insn = AllocReplacementMIR(mir_graph, invoke);
   insn->dalvikInsn.opcode = opcode;
   insn->dalvikInsn.vA = src_reg;
   insn->dalvikInsn.vB = object_reg;
@@ -879,7 +897,7 @@
   bb->InsertMIRAfter(invoke, insn);
 
   if (move_result != nullptr) {
-    MIR* move = AllocReplacementMIR(mir_graph, invoke, move_result);
+    MIR* move = AllocReplacementMIR(mir_graph, invoke);
     move->offset = move_result->offset;
     if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT) {
       move->dalvikInsn.opcode = Instruction::MOVE_FROM16;
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index 30a2d90..cb521da 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -31,9 +31,9 @@
 class MethodVerifier;
 }  // namespace verifier
 
-struct BasicBlock;
+class BasicBlock;
 struct CallInfo;
-struct MIR;
+class MIR;
 class MIRGraph;
 class Mir2Lir;
 
@@ -315,9 +315,9 @@
     static bool GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
                                    MIR* move_result, const InlineMethod& method);
     static bool GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
-                              MIR* move_result, const InlineMethod& method, uint32_t method_idx);
+                              MIR* move_result, const InlineMethod& method);
     static bool GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
-                              MIR* move_result, const InlineMethod& method, uint32_t method_idx);
+                              MIR* move_result, const InlineMethod& method);
 
     ReaderWriterMutex lock_;
     /*
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index f6c77fc..061ee07 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -66,8 +66,8 @@
 void Mir2Lir::AddDivZeroCheckSlowPath(LIR* branch) {
   class DivZeroCheckSlowPath : public Mir2Lir::LIRSlowPath {
    public:
-    DivZeroCheckSlowPath(Mir2Lir* m2l, LIR* branch)
-        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch) {
+    DivZeroCheckSlowPath(Mir2Lir* m2l, LIR* branch_in)
+        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch_in) {
     }
 
     void Compile() OVERRIDE {
@@ -84,9 +84,10 @@
 void Mir2Lir::GenArrayBoundsCheck(RegStorage index, RegStorage length) {
   class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
    public:
-    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, RegStorage index, RegStorage length)
-        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
-          index_(index), length_(length) {
+    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch_in, RegStorage index_in,
+                             RegStorage length_in)
+        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch_in),
+          index_(index_in), length_(length_in) {
     }
 
     void Compile() OVERRIDE {
@@ -108,9 +109,9 @@
 void Mir2Lir::GenArrayBoundsCheck(int index, RegStorage length) {
   class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
    public:
-    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, int index, RegStorage length)
-        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
-          index_(index), length_(length) {
+    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch_in, int index_in, RegStorage length_in)
+        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch_in),
+          index_(index_in), length_(length_in) {
     }
 
     void Compile() OVERRIDE {
@@ -212,11 +213,9 @@
 }
 
 void Mir2Lir::GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1,
-                                  RegLocation rl_src2, LIR* taken,
-                                  LIR* fall_through) {
-  DCHECK(!rl_src1.fp);
-  DCHECK(!rl_src2.fp);
+                                  RegLocation rl_src2, LIR* taken) {
   ConditionCode cond;
+  RegisterClass reg_class = (rl_src1.ref || rl_src2.ref) ? kRefReg : kCoreReg;
   switch (opcode) {
     case Instruction::IF_EQ:
       cond = kCondEq;
@@ -249,7 +248,7 @@
     cond = FlipComparisonOrder(cond);
   }
 
-  rl_src1 = LoadValue(rl_src1);
+  rl_src1 = LoadValue(rl_src1, reg_class);
   // Is this really an immediate comparison?
   if (rl_src2.is_const) {
     // If it's already live in a register or not easily materialized, just keep going
@@ -273,15 +272,14 @@
     }
   }
 
-  rl_src2 = LoadValue(rl_src2);
+  rl_src2 = LoadValue(rl_src2, reg_class);
   OpCmpBranch(cond, rl_src1.reg, rl_src2.reg, taken);
 }
 
-void Mir2Lir::GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, LIR* taken,
-                                      LIR* fall_through) {
+void Mir2Lir::GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, LIR* taken) {
   ConditionCode cond;
-  DCHECK(!rl_src.fp);
-  rl_src = LoadValue(rl_src);
+  RegisterClass reg_class = rl_src.ref ? kRefReg : kCoreReg;
+  rl_src = LoadValue(rl_src, reg_class);
   switch (opcode) {
     case Instruction::IF_EQZ:
       cond = kCondEq;
@@ -361,7 +359,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 {
@@ -464,7 +462,7 @@
     // Set up the loop counter (known to be > 0)
     LoadConstant(r_idx, elems - 1);
     // Generate the copy loop.  Going backwards for convenience
-    LIR* target = NewLIR0(kPseudoTargetLabel);
+    LIR* loop_head_target = NewLIR0(kPseudoTargetLabel);
     // Copy next element
     {
       ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
@@ -474,7 +472,7 @@
     }
     StoreBaseIndexed(r_dst, r_idx, r_val, 2, k32);
     FreeTemp(r_val);
-    OpDecAndBranch(kCondGe, r_idx, target);
+    OpDecAndBranch(kCondGe, r_idx, loop_head_target);
     if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) {
       // Restore the target pointer
       OpRegRegImm(kOpAdd, ref_reg, r_dst,
@@ -497,20 +495,47 @@
   }
 }
 
+/*
+ * Array data table format:
+ *  ushort ident = 0x0300   magic value
+ *  ushort width            width of each element in the table
+ *  uint   size             number of elements in the table
+ *  ubyte  data[size*width] table of data values (may contain a single-byte
+ *                          padding at the end)
+ *
+ * Total size is 4+(width * size + 1)/2 16-bit code units.
+ */
+void Mir2Lir::GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
+  if (kIsDebugBuild) {
+    const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
+    const Instruction::ArrayDataPayload* payload =
+        reinterpret_cast<const Instruction::ArrayDataPayload*>(table);
+    CHECK_EQ(payload->ident, static_cast<uint16_t>(Instruction::kArrayDataSignature));
+  }
+  uint32_t table_offset_from_start = mir->offset + static_cast<int32_t>(table_offset);
+  CallRuntimeHelperImmRegLocation(kQuickHandleFillArrayData, table_offset_from_start, rl_src, true);
+}
+
 //
 // Slow path to ensure a class is initialized for sget/sput.
 //
 class StaticFieldSlowPath : public Mir2Lir::LIRSlowPath {
  public:
+  // There are up to two branches to the static field slow path, the "unresolved" when the type
+  // entry in the dex cache is null, and the "uninit" when the class is not yet initialized.
+  // At least one will be non-null here, otherwise we wouldn't generate the slow path.
   StaticFieldSlowPath(Mir2Lir* m2l, LIR* unresolved, LIR* uninit, LIR* cont, int storage_index,
-                      RegStorage r_base) :
-    LIRSlowPath(m2l, m2l->GetCurrentDexPc(), unresolved, cont), uninit_(uninit),
-               storage_index_(storage_index), r_base_(r_base) {
+                      RegStorage r_base)
+      : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), unresolved != nullptr ? unresolved : uninit, cont),
+        second_branch_(unresolved != nullptr ? uninit : nullptr),
+        storage_index_(storage_index), r_base_(r_base) {
   }
 
   void Compile() {
-    LIR* unresolved_target = GenerateTargetLabel();
-    uninit_->target = unresolved_target;
+    LIR* target = GenerateTargetLabel();
+    if (second_branch_ != nullptr) {
+      second_branch_->target = target;
+    }
     m2l_->CallRuntimeHelperImm(kQuickInitializeStaticStorage, storage_index_, true);
     // Copy helper's result into r_base, a no-op on all but MIPS.
     m2l_->OpRegCopy(r_base_,  m2l_->TargetReg(kRet0, kRef));
@@ -519,16 +544,16 @@
   }
 
  private:
-  LIR* const uninit_;
+  // Second branch to the slow path, or null if there's only one branch.
+  LIR* const second_branch_;
+
   const int storage_index_;
   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;
@@ -559,65 +584,95 @@
       int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
       LoadRefDisp(r_base, offset_of_field, r_base, kNotVolatile);
       // r_base now points at static storage (Class*) or NULL if the type is not yet resolved.
-      if (!field_info.IsInitialized() &&
-          (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
-        // Check if r_base is NULL or a not yet initialized class.
-
-        // The slow path is invoked if the r_base is NULL or the class pointed
-        // to by it is not initialized.
-        LIR* unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL);
+      LIR* unresolved_branch = nullptr;
+      if (!field_info.IsClassInDexCache() &&
+          (mir->optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) == 0) {
+        // Check if r_base is NULL.
+        unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL);
+      }
+      LIR* uninit_branch = nullptr;
+      if (!field_info.IsClassInitialized() &&
+          (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0) {
+        // Check if r_base is not yet initialized class.
         RegStorage r_tmp = TargetReg(kArg2, kNotWide);
         LockTemp(r_tmp);
-        LIR* uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base,
+        uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base,
                                           mirror::Class::StatusOffset().Int32Value(),
                                           mirror::Class::kStatusInitialized, nullptr, nullptr);
+        FreeTemp(r_tmp);
+      }
+      if (unresolved_branch != nullptr || uninit_branch != nullptr) {
+        // The slow path is invoked if the r_base is NULL or the class pointed
+        // to by it is not initialized.
         LIR* cont = NewLIR0(kPseudoTargetLabel);
-
         AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont,
                                                      field_info.StorageIndex(), r_base));
 
-        FreeTemp(r_tmp);
-        // Ensure load of status and store of value don't re-order.
-        // TODO: Presumably the actual value store is control-dependent on the status load,
-        // and will thus not be reordered in any case, since stores are never speculated.
-        // Does later code "know" that the class is now initialized?  If so, we still
-        // need the barrier to guard later static loads.
-        GenMemBarrier(kLoadAny);
+        if (uninit_branch != nullptr) {
+          // Ensure load of status and store of value don't re-order.
+          // TODO: Presumably the actual value store is control-dependent on the status load,
+          // and will thus not be reordered in any case, since stores are never speculated.
+          // Does later code "know" that the class is now initialized?  If so, we still
+          // need the barrier to guard later static loads.
+          GenMemBarrier(kLoadAny);
+        }
       }
       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;
@@ -644,57 +699,94 @@
       int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
       LoadRefDisp(r_base, offset_of_field, r_base, kNotVolatile);
       // r_base now points at static storage (Class*) or NULL if the type is not yet resolved.
-      if (!field_info.IsInitialized() &&
-          (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
-        // Check if r_base is NULL or a not yet initialized class.
-
-        // The slow path is invoked if the r_base is NULL or the class pointed
-        // to by it is not initialized.
-        LIR* unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL);
+      LIR* unresolved_branch = nullptr;
+      if (!field_info.IsClassInDexCache() &&
+          (mir->optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) == 0) {
+        // Check if r_base is NULL.
+        unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL);
+      }
+      LIR* uninit_branch = nullptr;
+      if (!field_info.IsClassInitialized() &&
+          (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0) {
+        // Check if r_base is not yet initialized class.
         RegStorage r_tmp = TargetReg(kArg2, kNotWide);
         LockTemp(r_tmp);
-        LIR* uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base,
+        uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base,
                                           mirror::Class::StatusOffset().Int32Value(),
                                           mirror::Class::kStatusInitialized, nullptr, nullptr);
+        FreeTemp(r_tmp);
+      }
+      if (unresolved_branch != nullptr || uninit_branch != nullptr) {
+        // The slow path is invoked if the r_base is NULL or the class pointed
+        // to by it is not initialized.
         LIR* cont = NewLIR0(kPseudoTargetLabel);
-
         AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont,
                                                      field_info.StorageIndex(), r_base));
 
-        FreeTemp(r_tmp);
-        // Ensure load of status and load of value don't re-order.
-        GenMemBarrier(kLoadAny);
+        if (uninit_branch != nullptr) {
+          // Ensure load of status and load of value don't re-order.
+          GenMemBarrier(kLoadAny);
+        }
       }
       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 +800,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 +819,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 +880,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 +897,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);
   }
@@ -818,7 +956,6 @@
   RegLocation rl_method = LoadCurrMethod();
   CheckRegLocation(rl_method);
   RegStorage res_reg = AllocTempRef();
-  RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
   if (!cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx,
                                                         *cu_->dex_file,
                                                         type_idx)) {
@@ -828,6 +965,7 @@
     RegLocation rl_result = GetReturn(kRefReg);
     StoreValue(rl_dest, rl_result);
   } else {
+    RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
     // We're don't need access checks, load type from dex cache
     int32_t dex_cache_offset =
         mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value();
@@ -844,10 +982,10 @@
       // Object to generate the slow path for class resolution.
       class SlowPath : public LIRSlowPath {
        public:
-        SlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont, const int type_idx,
-                 const RegLocation& rl_method, const RegLocation& rl_result) :
-                   LIRSlowPath(m2l, m2l->GetCurrentDexPc(), fromfast, cont), type_idx_(type_idx),
-                   rl_method_(rl_method), rl_result_(rl_result) {
+        SlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont_in, const int type_idx_in,
+                 const RegLocation& rl_method_in, const RegLocation& rl_result_in) :
+                   LIRSlowPath(m2l, m2l->GetCurrentDexPc(), fromfast, cont_in),
+                   type_idx_(type_idx_in), rl_method_(rl_method_in), rl_result_(rl_result_in) {
         }
 
         void Compile() {
@@ -908,9 +1046,10 @@
       // Object to generate the slow path for string resolution.
       class SlowPath : public LIRSlowPath {
        public:
-        SlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont, RegStorage r_method, int32_t string_idx) :
-          LIRSlowPath(m2l, m2l->GetCurrentDexPc(), fromfast, cont),
-          r_method_(r_method), string_idx_(string_idx) {
+        SlowPath(Mir2Lir* m2l, LIR* fromfast_in, LIR* cont_in, RegStorage r_method_in,
+                 int32_t string_idx_in) :
+            LIRSlowPath(m2l, m2l->GetCurrentDexPc(), fromfast_in, cont_in),
+            r_method_(r_method_in), string_idx_(string_idx_in) {
         }
 
         void Compile() {
@@ -961,7 +1100,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 {
@@ -1088,10 +1227,10 @@
 
       class InitTypeSlowPath : public Mir2Lir::LIRSlowPath {
        public:
-        InitTypeSlowPath(Mir2Lir* m2l, LIR* branch, LIR* cont, uint32_t type_idx,
-                         RegLocation rl_src)
-            : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch, cont), type_idx_(type_idx),
-              rl_src_(rl_src) {
+        InitTypeSlowPath(Mir2Lir* m2l, LIR* branch, LIR* cont, uint32_t type_idx_in,
+                         RegLocation rl_src_in)
+            : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch, cont), type_idx_(type_idx_in),
+              rl_src_(rl_src_in) {
         }
 
         void Compile() OVERRIDE {
@@ -1233,10 +1372,10 @@
       // Slow path to initialize the type.  Executed if the type is NULL.
       class SlowPath : public LIRSlowPath {
        public:
-        SlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont, const int type_idx,
-                 const RegStorage class_reg) :
-                   LIRSlowPath(m2l, m2l->GetCurrentDexPc(), fromfast, cont), type_idx_(type_idx),
-                   class_reg_(class_reg) {
+        SlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont_in, const int type_idx_in,
+                 const RegStorage class_reg_in) :
+                   LIRSlowPath(m2l, m2l->GetCurrentDexPc(), fromfast, cont_in),
+                   type_idx_(type_idx_in), class_reg_(class_reg_in) {
         }
 
         void Compile() {
@@ -1386,7 +1525,7 @@
 
 
 void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
-                            RegLocation rl_src1, RegLocation rl_src2) {
+                            RegLocation rl_src1, RegLocation rl_src2, int flags) {
   DCHECK(cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64);
   OpKind op = kOpBkpt;
   bool is_div_rem = false;
@@ -1485,18 +1624,19 @@
     if (cu_->instruction_set == kMips || cu_->instruction_set == kArm64) {
       rl_src1 = LoadValue(rl_src1, kCoreReg);
       rl_src2 = LoadValue(rl_src2, kCoreReg);
-      if (check_zero) {
+      if (check_zero && (flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
         GenDivZeroCheck(rl_src2.reg);
       }
       rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv);
       done = true;
     } else if (cu_->instruction_set == kThumb2) {
-      if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) {
+      if (cu_->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
+              HasDivideInstruction()) {
         // Use ARM SDIV instruction for division.  For remainder we also need to
         // calculate using a MUL and subtract.
         rl_src1 = LoadValue(rl_src1, kCoreReg);
         rl_src2 = LoadValue(rl_src2, kCoreReg);
-        if (check_zero) {
+        if (check_zero && (flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
           GenDivZeroCheck(rl_src2.reg);
         }
         rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv);
@@ -1510,7 +1650,7 @@
       LoadValueDirectFixed(rl_src2, TargetReg(kArg1, kNotWide));
       RegStorage r_tgt = CallHelperSetup(kQuickIdivmod);
       LoadValueDirectFixed(rl_src1, TargetReg(kArg0, kNotWide));
-      if (check_zero) {
+      if (check_zero && (flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
         GenDivZeroCheck(TargetReg(kArg1, kNotWide));
       }
       // NOTE: callout here is not a safepoint.
@@ -1645,6 +1785,34 @@
   return true;
 }
 
+// Returns true if it generates instructions.
+bool Mir2Lir::HandleEasyFloatingPointDiv(RegLocation rl_dest, RegLocation rl_src1,
+                                         RegLocation rl_src2) {
+  if (!rl_src2.is_const ||
+      ((cu_->instruction_set != kThumb2) && (cu_->instruction_set != kArm64))) {
+    return false;
+  }
+
+  if (!rl_src2.wide) {
+    int32_t divisor = mir_graph_->ConstantValue(rl_src2);
+    if (CanDivideByReciprocalMultiplyFloat(divisor)) {
+      // Generate multiply by reciprocal instead of div.
+      float recip = 1.0f/bit_cast<int32_t, float>(divisor);
+      GenMultiplyByConstantFloat(rl_dest, rl_src1, bit_cast<float, int32_t>(recip));
+      return true;
+    }
+  } else {
+    int64_t divisor = mir_graph_->ConstantValueWide(rl_src2);
+    if (CanDivideByReciprocalMultiplyDouble(divisor)) {
+      // Generate multiply by reciprocal instead of div.
+      double recip = 1.0/bit_cast<double, int64_t>(divisor);
+      GenMultiplyByConstantDouble(rl_dest, rl_src1, bit_cast<double, int64_t>(recip));
+      return true;
+    }
+  }
+  return false;
+}
+
 void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src,
                                int lit) {
   RegLocation rl_result;
@@ -1670,7 +1838,7 @@
     case Instruction::SUB_INT:
     case Instruction::SUB_INT_2ADDR:
       lit = -lit;
-      // Intended fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::ADD_INT:
     case Instruction::ADD_INT_2ADDR:
     case Instruction::ADD_INT_LIT8:
@@ -1760,7 +1928,8 @@
         rl_result = GenDivRemLit(rl_dest, rl_src, lit, is_div);
         done = true;
       } else if (cu_->instruction_set == kThumb2) {
-        if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) {
+        if (cu_->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
+                HasDivideInstruction()) {
           // Use ARM SDIV instruction for division.  For remainder we also need to
           // calculate using a MUL and subtract.
           rl_src = LoadValue(rl_src, kCoreReg);
@@ -1797,7 +1966,7 @@
 }
 
 void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                             RegLocation rl_src1, RegLocation rl_src2) {
+                             RegLocation rl_src1, RegLocation rl_src2, int flags) {
   RegLocation rl_result;
   OpKind first_op = kOpBkpt;
   OpKind second_op = kOpBkpt;
@@ -1882,7 +2051,9 @@
       RegStorage r_tmp2 = TargetReg(kArg2, kWide);
       LoadValueDirectWideFixed(rl_src2, r_tmp2);
       RegStorage r_tgt = CallHelperSetup(target);
-      GenDivZeroCheckWide(r_tmp2);
+      if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
+        GenDivZeroCheckWide(r_tmp2);
+      }
       LoadValueDirectWideFixed(rl_src1, r_tmp1);
       // NOTE: callout here is not a safepoint
       CallHelper(r_tgt, target, false /* not safepoint */);
@@ -1991,12 +2162,14 @@
 
 /* Call out to helper assembly routine that will null check obj and then lock it. */
 void Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
+  UNUSED(opt_flags);  // TODO: avoid null check with specialized non-null helper.
   FlushAllRegs();
   CallRuntimeHelperRegLocation(kQuickLockObject, rl_src, true);
 }
 
 /* Call out to helper assembly routine that will null check obj and then unlock it. */
 void Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
+  UNUSED(opt_flags);  // TODO: avoid null check with specialized non-null helper.
   FlushAllRegs();
   CallRuntimeHelperRegLocation(kQuickUnlockObject, rl_src, true);
 }
@@ -2009,29 +2182,29 @@
 }
 
 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]);
-  int32_t current_key = as_int32[0];
+  int32_t starting_key = as_int32[0];
   const int32_t* targets = &as_int32[1];
   rl_src = LoadValue(rl_src, kCoreReg);
   int i = 0;
-  for (; i < entries; i++, current_key++) {
-    if (!InexpensiveConstantInt(current_key, Instruction::Code::IF_EQ)) {
+  for (; i < entries; i++) {
+    if (!InexpensiveConstantInt(starting_key + i, Instruction::Code::IF_EQ)) {
       // Switch to using a temp and add.
       break;
     }
     BasicBlock* case_block =
         mir_graph_->FindBlock(current_dalvik_offset_ + targets[i]);
-    OpCmpImmBranch(kCondEq, rl_src.reg, current_key, &block_label_list_[case_block->id]);
+    OpCmpImmBranch(kCondEq, rl_src.reg, starting_key + i, &block_label_list_[case_block->id]);
   }
   if (i < entries) {
     // The rest do not seem to be inexpensive. Try to allocate a temp and use add.
     RegStorage key_temp = AllocTypedTemp(false, kCoreReg, false);
     if (key_temp.Valid()) {
-      LoadConstantNoClobber(key_temp, current_key);
-      for (; i < entries - 1; i++, current_key++) {
+      LoadConstantNoClobber(key_temp, starting_key + i);
+      for (; i < entries - 1; i++) {
         BasicBlock* case_block =
             mir_graph_->FindBlock(current_dalvik_offset_ + targets[i]);
         OpCmpBranch(kCondEq, rl_src.reg, key_temp, &block_label_list_[case_block->id]);
@@ -2042,17 +2215,17 @@
       OpCmpBranch(kCondEq, rl_src.reg, key_temp, &block_label_list_[case_block->id]);
     } else {
       // No free temp, just finish the old loop.
-      for (; i < entries; i++, current_key++) {
+      for (; i < entries; i++) {
         BasicBlock* case_block =
             mir_graph_->FindBlock(current_dalvik_offset_ + targets[i]);
-        OpCmpImmBranch(kCondEq, rl_src.reg, current_key, &block_label_list_[case_block->id]);
+        OpCmpImmBranch(kCondEq, rl_src.reg, starting_key + i, &block_label_list_[case_block->id]);
       }
     }
   }
 }
 
 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 +2240,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 +2255,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 +2269,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..4cb12f1 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 {
 
@@ -45,8 +44,8 @@
 void Mir2Lir::AddIntrinsicSlowPath(CallInfo* info, LIR* branch, LIR* resume) {
   class IntrinsicSlowPathPath : public Mir2Lir::LIRSlowPath {
    public:
-    IntrinsicSlowPathPath(Mir2Lir* m2l, CallInfo* info, LIR* branch, LIR* resume = nullptr)
-        : LIRSlowPath(m2l, info->offset, branch, resume), info_(info) {
+    IntrinsicSlowPathPath(Mir2Lir* m2l, CallInfo* info_in, LIR* branch_in, LIR* resume_in)
+        : LIRSlowPath(m2l, info_in->offset, branch_in, resume_in), info_(info_in) {
     }
 
     void Compile() {
@@ -249,13 +248,13 @@
         if (cu_->instruction_set == kMips) {
           LoadValueDirectFixed(arg1, TargetReg(arg1.fp ? kFArg2 : kArg1, kNotWide));
         } else {
-          LoadValueDirectFixed(arg1, TargetReg(kArg1, kNotWide));
+          LoadValueDirectFixed(arg1, TargetReg(arg1.fp ? kFArg1 : kArg1, kNotWide));
         }
       } else {
         if (cu_->instruction_set == kMips) {
           LoadValueDirectWideFixed(arg1, TargetReg(arg1.fp ? kFArg2 : kArg2, kWide));
         } else {
-          LoadValueDirectWideFixed(arg1, TargetReg(kArg1, kWide));
+          LoadValueDirectWideFixed(arg1, TargetReg(arg1.fp ? kFArg1 : kArg1, kWide));
         }
       }
     } else {
@@ -366,6 +365,7 @@
  * ArgLocs is an array of location records describing the incoming arguments
  * with one location record per word of argument.
  */
+// TODO: Support 64-bit argument registers.
 void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) {
   /*
    * Dummy up a RegLocation for the incoming StackReference<mirror::ArtMethod>
@@ -383,11 +383,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 +401,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);
 
@@ -473,8 +473,7 @@
   cg->MarkPossibleNullPointerException(info->opt_flags);
 }
 
-static bool CommonCallCodeLoadCodePointerIntoInvokeTgt(const CallInfo* info,
-                                                       const RegStorage* alt_from,
+static bool CommonCallCodeLoadCodePointerIntoInvokeTgt(const RegStorage* alt_from,
                                                        const CompilationUnit* cu, Mir2Lir* cg) {
   if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
     // Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt]
@@ -492,18 +491,20 @@
  */
 static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
                           int state, const MethodReference& target_method,
-                          uint32_t unused,
+                          uint32_t,
                           uintptr_t direct_code, uintptr_t direct_method,
                           InvokeType type) {
+  UNUSED(info);
+  DCHECK(cu->instruction_set != kX86 && cu->instruction_set != kX86_64 &&
+         cu->instruction_set != kThumb2 && cu->instruction_set != kArm &&
+         cu->instruction_set != kArm64);
   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 +532,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);
         }
@@ -546,13 +547,14 @@
       break;
     case 3:  // Grab the code from the method*
       if (direct_code == 0) {
-        if (CommonCallCodeLoadCodePointerIntoInvokeTgt(info, &arg0_ref, cu, cg)) {
+        if (CommonCallCodeLoadCodePointerIntoInvokeTgt(&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
+      DCHECK(cu->instruction_set == kX86 || cu->instruction_set == kX86_64);
+      FALLTHROUGH_INTENDED;
     default:
       return -1;
     }
@@ -569,8 +571,9 @@
  */
 static int NextVCallInsn(CompilationUnit* cu, CallInfo* info,
                          int state, const MethodReference& target_method,
-                         uint32_t method_idx, uintptr_t unused, uintptr_t unused2,
-                         InvokeType unused3) {
+                         uint32_t method_idx, uintptr_t, uintptr_t,
+                         InvokeType) {
+  UNUSED(target_method);
   Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
   /*
    * This is the fast path in which the target virtual method is
@@ -593,10 +596,11 @@
       break;
     }
     case 3:
-      if (CommonCallCodeLoadCodePointerIntoInvokeTgt(info, nullptr, cu, cg)) {
+      if (CommonCallCodeLoadCodePointerIntoInvokeTgt(nullptr, cu, cg)) {
         break;                                    // kInvokeTgt := kArg0->entrypoint
       }
-      // Intentional fallthrough for X86
+      DCHECK(cu->instruction_set == kX86 || cu->instruction_set == kX86_64);
+      FALLTHROUGH_INTENDED;
     default:
       return -1;
   }
@@ -611,8 +615,7 @@
  */
 static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state,
                                  const MethodReference& target_method,
-                                 uint32_t method_idx, uintptr_t unused,
-                                 uintptr_t direct_method, InvokeType unused2) {
+                                 uint32_t method_idx, uintptr_t, uintptr_t, InvokeType) {
   Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
 
   switch (state) {
@@ -638,10 +641,11 @@
       break;
     }
     case 4:
-      if (CommonCallCodeLoadCodePointerIntoInvokeTgt(info, nullptr, cu, cg)) {
+      if (CommonCallCodeLoadCodePointerIntoInvokeTgt(nullptr, cu, cg)) {
         break;                                    // kInvokeTgt := kArg0->entrypoint
       }
-      // Intentional fallthrough for X86
+      DCHECK(cu->instruction_set == kX86 || cu->instruction_set == kX86_64);
+      FALLTHROUGH_INTENDED;
     default:
       return -1;
   }
@@ -651,9 +655,9 @@
 static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info,
                             QuickEntrypointEnum trampoline, int state,
                             const MethodReference& target_method, uint32_t method_idx) {
+  UNUSED(info, method_idx);
   Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
 
-
   /*
    * This handles the case in which the base method is not fully
    * resolved at compile time, we bail to a runtime helper.
@@ -680,32 +684,28 @@
 static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info,
                                 int state,
                                 const MethodReference& target_method,
-                                uint32_t unused, uintptr_t unused2,
-                                uintptr_t unused3, InvokeType unused4) {
+                                uint32_t, uintptr_t, uintptr_t, InvokeType) {
   return NextInvokeInsnSP(cu, info, kQuickInvokeStaticTrampolineWithAccessCheck, state,
                           target_method, 0);
 }
 
 static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state,
                                 const MethodReference& target_method,
-                                uint32_t unused, uintptr_t unused2,
-                                uintptr_t unused3, InvokeType unused4) {
+                                uint32_t, uintptr_t, uintptr_t, InvokeType) {
   return NextInvokeInsnSP(cu, info, kQuickInvokeDirectTrampolineWithAccessCheck, state,
                           target_method, 0);
 }
 
 static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state,
                                const MethodReference& target_method,
-                               uint32_t unused, uintptr_t unused2,
-                               uintptr_t unused3, InvokeType unused4) {
+                               uint32_t, uintptr_t, uintptr_t, InvokeType) {
   return NextInvokeInsnSP(cu, info, kQuickInvokeSuperTrampolineWithAccessCheck, state,
                           target_method, 0);
 }
 
 static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state,
                            const MethodReference& target_method,
-                           uint32_t unused, uintptr_t unused2,
-                           uintptr_t unused3, InvokeType unused4) {
+                           uint32_t, uintptr_t, uintptr_t, InvokeType) {
   return NextInvokeInsnSP(cu, info, kQuickInvokeVirtualTrampolineWithAccessCheck, state,
                           target_method, 0);
 }
@@ -713,8 +713,7 @@
 static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu,
                                                 CallInfo* info, int state,
                                                 const MethodReference& target_method,
-                                                uint32_t unused, uintptr_t unused2,
-                                                uintptr_t unused3, InvokeType unused4) {
+                                                uint32_t, uintptr_t, uintptr_t, InvokeType) {
   return NextInvokeInsnSP(cu, info, kQuickInvokeInterfaceTrampolineWithAccessCheck, state,
                           target_method, 0);
 }
@@ -791,13 +790,13 @@
         if (rl_arg.reg.IsPair()) {
           reg = rl_arg.reg.GetHigh();
         } else {
-          RegisterInfo* info = GetRegInfo(rl_arg.reg);
-          info = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask);
-          if (info == nullptr) {
+          RegisterInfo* reg_info = GetRegInfo(rl_arg.reg);
+          reg_info = reg_info->FindMatchingView(RegisterInfo::kHighSingleStorageMask);
+          if (reg_info == nullptr) {
             // NOTE: For hard float convention we won't split arguments across reg/mem.
             UNIMPLEMENTED(FATAL) << "Needs hard float api.";
           }
-          reg = info->GetReg();
+          reg = reg_info->GetReg();
         }
       } else {
         // kArg2 & rArg3 can safely be used here
@@ -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);
@@ -1424,28 +1395,34 @@
 }
 
 bool Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
-  // Currently implemented only for ARM64
+  // Currently implemented only for ARM64.
+  UNUSED(info, size);
   return false;
 }
 
 bool Mir2Lir::GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) {
-  // Currently implemented only for ARM64
+  // Currently implemented only for ARM64.
+  UNUSED(info, is_min, is_double);
   return false;
 }
 
 bool Mir2Lir::GenInlinedCeil(CallInfo* info) {
+  UNUSED(info);
   return false;
 }
 
 bool Mir2Lir::GenInlinedFloor(CallInfo* info) {
+  UNUSED(info);
   return false;
 }
 
 bool Mir2Lir::GenInlinedRint(CallInfo* info) {
+  UNUSED(info);
   return false;
 }
 
 bool Mir2Lir::GenInlinedRound(CallInfo* info, bool is_double) {
+  UNUSED(info, is_double);
   return false;
 }
 
@@ -1472,6 +1449,7 @@
 }
 
 bool Mir2Lir::GenInlinedArrayCopyCharArray(CallInfo* info) {
+  UNUSED(info);
   return false;
 }
 
@@ -1672,7 +1650,7 @@
       FreeTemp(rl_temp_offset);
     }
   } else {
-    rl_value = LoadValue(rl_src_value);
+    rl_value = LoadValue(rl_src_value, LocToRegClass(rl_src_value));
     if (rl_value.ref) {
       StoreRefIndexed(rl_object.reg, rl_offset.reg, rl_value.reg, 0);
     } else {
@@ -1695,16 +1673,6 @@
 }
 
 void Mir2Lir::GenInvoke(CallInfo* info) {
-  if ((info->opt_flags & MIR_INLINED) != 0) {
-    // Already inlined but we may still need the null check.
-    if (info->type != kStatic &&
-        ((cu_->disable_opt & (1 << kNullCheckElimination)) != 0 ||
-         (info->opt_flags & MIR_IGNORE_NULL_CHECK) == 0))  {
-      RegLocation rl_obj = LoadValue(info->args[0], kRefReg);
-      GenNullCheck(rl_obj.reg);
-    }
-    return;
-  }
   DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
   if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file)
       ->GenIntrinsic(this, info)) {
@@ -1713,31 +1681,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;
@@ -1749,9 +1692,8 @@
 
   const MirMethodLoweringInfo& method_info = mir_graph_->GetMethodLoweringInfo(info->mir);
   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 +1703,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 +1734,8 @@
     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);
-    }
-  }
-  EndInvoke(info);
-  MarkSafepointPC(call_inst);
+  LIR* call_insn = GenCallInsn(method_info);
+  MarkSafepointPC(call_insn);
 
   ClobberCallerSave();
   if (info->result.location != kLocInvalid) {
@@ -1825,4 +1750,16 @@
   }
 }
 
+NextCallInsn Mir2Lir::GetNextSDCallInsn() {
+  return NextSDCallInsn;
+}
+
+LIR* Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
+  UNUSED(method_info);
+  DCHECK(cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64 &&
+         cu_->instruction_set != kThumb2 && cu_->instruction_set != kArm &&
+         cu_->instruction_set != kArm64);
+  return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc
index e5798fd..d314601 100644
--- a/compiler/dex/quick/gen_loadstore.cc
+++ b/compiler/dex/quick/gen_loadstore.cc
@@ -149,8 +149,9 @@
       // Wrong register class, realloc, copy and transfer ownership.
       RegStorage new_reg = AllocTypedTemp(rl_src.fp, op_kind);
       OpRegCopy(new_reg, rl_src.reg);
-      // Clobber the old reg.
+      // Clobber the old regs and free it.
       Clobber(rl_src.reg);
+      FreeTemp(rl_src.reg);
       // ...and mark the new one live.
       rl_src.reg = new_reg;
       MarkLive(rl_src);
@@ -166,10 +167,6 @@
   return rl_src;
 }
 
-RegLocation Mir2Lir::LoadValue(RegLocation rl_src) {
-  return LoadValue(rl_src, LocToRegClass(rl_src));
-}
-
 void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src) {
   /*
    * Sanity checking - should never try to store to the same
@@ -236,8 +233,9 @@
       // Wrong register class, realloc, copy and transfer ownership.
       RegStorage new_regs = AllocTypedTempWide(rl_src.fp, op_kind);
       OpRegCopyWide(new_regs, rl_src.reg);
-      // Clobber the old regs.
+      // Clobber the old regs and free it.
       Clobber(rl_src.reg);
+      FreeTemp(rl_src.reg);
       // ...and mark the new ones live.
       rl_src.reg = new_regs;
       MarkLive(rl_src);
diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc
index c7e9190..ca71c30 100644
--- a/compiler/dex/quick/mips/assemble_mips.cc
+++ b/compiler/dex/quick/mips/assemble_mips.cc
@@ -146,12 +146,10 @@
                  kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF_HI | REG_DEF_LO | REG_USE01,
                  "div", "!0r,!1r", 4),
-#if __mips_isa_rev >= 2
     ENCODING_MAP(kMipsExt, 0x7c000000,
                  kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 10, 6,
                  kFmtBitBlt, 15, 11, IS_QUAD_OP | REG_DEF0 | REG_USE1,
                  "ext", "!0r,!1r,!2d,!3D", 4),
-#endif
     ENCODING_MAP(kMipsJal, 0x0c000000,
                  kFmtBitBlt, 25, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
@@ -240,7 +238,6 @@
                  kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
                  "sb", "!0r,!1d(!2r)", 4),
-#if __mips_isa_rev >= 2
     ENCODING_MAP(kMipsSeb, 0x7c000420,
                  kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
@@ -249,7 +246,6 @@
                  kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "seh", "!0r,!1r", 4),
-#endif
     ENCODING_MAP(kMipsSh, 0xA4000000,
                  kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
@@ -465,6 +461,7 @@
   switch (opcode) {
     case kMipsBal:
       LOG(FATAL) << "long branch and link unsupported";
+      UNREACHABLE();
     case kMipsB:
       unconditional = true;
       break;
@@ -478,6 +475,7 @@
     case kMipsBnez: opcode = kMipsBeqz; break;
     default:
       LOG(FATAL) << "Unexpected branch kind " << opcode;
+      UNREACHABLE();
   }
   LIR* hop_target = NULL;
   if (!unconditional) {
@@ -698,12 +696,12 @@
     // TUNING: replace with proper delay slot handling
     if (encoder->size == 8) {
       DCHECK(!IsPseudoLirOp(lir->opcode));
-      const MipsEncodingMap *encoder = &EncodingMap[kMipsNop];
-      uint32_t bits = encoder->skeleton;
-      code_buffer_.push_back(bits & 0xff);
-      code_buffer_.push_back((bits >> 8) & 0xff);
-      code_buffer_.push_back((bits >> 16) & 0xff);
-      code_buffer_.push_back((bits >> 24) & 0xff);
+      const MipsEncodingMap *encoder2 = &EncodingMap[kMipsNop];
+      uint32_t bits2 = encoder2->skeleton;
+      code_buffer_.push_back(bits2 & 0xff);
+      code_buffer_.push_back((bits2 >> 8) & 0xff);
+      code_buffer_.push_back((bits2 >> 16) & 0xff);
+      code_buffer_.push_back((bits2 >> 24) & 0xff);
     }
   }
   return res;
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..01784e2 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -24,9 +24,9 @@
 
 namespace art {
 
-bool MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
-                                 const InlineMethod& special) {
+bool MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special) {
   // TODO
+  UNUSED(bb, mir, special);
   return false;
 }
 
@@ -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);
@@ -210,54 +210,6 @@
   branch_over->target = target;
 }
 
-/*
- * Array data table format:
- *  ushort ident = 0x0300   magic value
- *  ushort width            width of each element in the table
- *  uint   size             number of elements in the table
- *  ubyte  data[size*width] table of data values (may contain a single-byte
- *                          padding at the end)
- *
- * 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;
-  // Add the table to the list - we'll process it later
-  FillArrayData* tab_rec =
-      reinterpret_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData),
-                                                     kArenaAllocData));
-  tab_rec->table = table;
-  tab_rec->vaddr = current_dalvik_offset_;
-  uint16_t width = tab_rec->table[1];
-  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);
-
-  // Making a call - use explicit registers
-  FlushAllRegs();   /* Everything to home location */
-  LockCallTemps();
-  LoadValueDirectFixed(rl_src, rs_rMIPS_ARG0);
-
-  // Must prevent code motion for the curr pc pair
-  GenBarrier();
-  NewLIR0(kMipsCurrPC);  // Really a jal to .+8
-  // Now, fill the branch delay slot with the helper load
-  RegStorage r_tgt = LoadHelper(kQuickHandleFillArrayData);
-  GenBarrier();  // Scheduling barrier
-
-  // Construct BaseLabel and set up table base register
-  LIR* base_label = NewLIR0(kPseudoTargetLabel);
-
-  // Materialize a pointer to the fill data image
-  NewLIR4(kMipsDelta, rMIPS_ARG1, 0, WrapPointer(base_label), WrapPointer(tab_rec));
-
-  // And go...
-  ClobberCallerSave();
-  LIR* call_inst = OpReg(kOpBlx, r_tgt);  // ( array*, fill_data* )
-  MarkSafepointPC(call_inst);
-}
-
 void MipsMir2Lir::GenMoveException(RegLocation rl_dest) {
   int ex_offset = Thread::ExceptionOffset<4>().Int32Value();
   RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 43cbde7..7e9d80d 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 {
@@ -30,6 +31,10 @@
     bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src,
                             RegLocation rl_dest, int lit);
     bool EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE;
+    void GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
+                                    int32_t constant) OVERRIDE;
+    void GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
+                                     int64_t constant) OVERRIDE;
     LIR* CheckSuspendUsingLoad() OVERRIDE;
     RegStorage LoadHelper(QuickEntrypointEnum trampoline) OVERRIDE;
     LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest,
@@ -85,13 +90,13 @@
 
     // Required for target - Dalvik-level generators.
     void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                           RegLocation rl_src1, RegLocation rl_src2);
+                           RegLocation rl_src1, RegLocation rl_src2, int flags);
     void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
                      RegLocation rl_index, RegLocation rl_dest, int scale);
     void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
                      RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark);
     void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                           RegLocation rl_shift);
+                           RegLocation rl_shift, int flags);
     void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
                           RegLocation rl_src2);
     void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
@@ -107,7 +112,7 @@
     bool GenInlinedPeek(CallInfo* info, OpSize size);
     bool GenInlinedPoke(CallInfo* info, OpSize size);
     void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                        RegLocation rl_src2) OVERRIDE;
+                        RegLocation rl_src2, int flags) OVERRIDE;
     RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div);
     RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div);
     void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -115,13 +120,12 @@
     void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
     void GenExitSequence();
     void GenSpecialExitSequence();
-    void GenFillArrayData(uint32_t 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);
     void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
                           int32_t true_val, int32_t false_val, RegStorage rs_dest,
-                          int dest_reg_class) OVERRIDE;
+                          RegisterClass dest_reg_class) OVERRIDE;
     bool GenMemBarrier(MemBarrierKind barrier_kind);
     void GenMoveException(RegLocation rl_dest);
     void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
@@ -172,10 +176,10 @@
     bool InexpensiveConstantLong(int64_t value);
     bool InexpensiveConstantDouble(int64_t value);
 
-    bool WideGPRsAreAliases() OVERRIDE {
+    bool WideGPRsAreAliases() const OVERRIDE {
       return false;  // Wide GPRs are formed by pairing.
     }
-    bool WideFPRsAreAliases() OVERRIDE {
+    bool WideFPRsAreAliases() const OVERRIDE {
       return false;  // Wide FPRs are formed by pairing.
     }
 
@@ -190,8 +194,8 @@
 
     void ConvertShortToLongBranch(LIR* lir);
     RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
-                          RegLocation rl_src2, bool is_div, bool check_zero);
-    RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div);
+                          RegLocation rl_src2, bool is_div, int flags) OVERRIDE;
+    RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) OVERRIDE;
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/mips/fp_mips.cc b/compiler/dex/quick/mips/fp_mips.cc
index 3a4128a..0a7aa99 100644
--- a/compiler/dex/quick/mips/fp_mips.cc
+++ b/compiler/dex/quick/mips/fp_mips.cc
@@ -113,6 +113,20 @@
   StoreValueWide(rl_dest, rl_result);
 }
 
+void MipsMir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
+                                             int32_t constant) {
+  // TODO: need mips implementation.
+  UNUSED(rl_dest, rl_src1, constant);
+  LOG(FATAL) << "Unimplemented GenMultiplyByConstantFloat in mips";
+}
+
+void MipsMir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
+                                              int64_t constant) {
+  // TODO: need mips implementation.
+  UNUSED(rl_dest, rl_src1, constant);
+  LOG(FATAL) << "Unimplemented GenMultiplyByConstantDouble in mips";
+}
+
 void MipsMir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest,
                                 RegLocation rl_src) {
   int op = kMipsNop;
@@ -207,8 +221,8 @@
   StoreValue(rl_dest, rl_result);
 }
 
-void MipsMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir,
-                                bool gt_bias, bool is_double) {
+void MipsMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) {
+  UNUSED(bb, mir, gt_bias, is_double);
   UNIMPLEMENTED(FATAL) << "Need codegen for fused fp cmp branch";
 }
 
@@ -230,7 +244,8 @@
 }
 
 bool MipsMir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) {
-  // TODO: need Mips implementation
+  // TODO: need Mips implementation.
+  UNUSED(info, is_min, is_long);
   return false;
 }
 
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index ea56989..d58ddb0 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -21,7 +21,7 @@
 #include "dex/reg_storage_eq.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mips_lir.h"
-#include "mirror/array.h"
+#include "mirror/array-inl.h"
 
 namespace art {
 
@@ -217,21 +217,24 @@
 
 void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
                                    int32_t true_val, int32_t false_val, RegStorage rs_dest,
-                                   int dest_reg_class) {
+                                   RegisterClass dest_reg_class) {
+  UNUSED(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;
 }
 
 void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
+  UNUSED(bb, mir);
   UNIMPLEMENTED(FATAL) << "Need codegen for select";
 }
 
 void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
+  UNUSED(bb, mir);
   UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch";
 }
 
@@ -262,34 +265,39 @@
   return rl_result;
 }
 
-RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
-                      RegLocation rl_src2, bool is_div, bool check_zero) {
+RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                                   bool is_div, int flags) {
+  UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags);
   LOG(FATAL) << "Unexpected use of GenDivRem for Mips";
-  return rl_dest;
+  UNREACHABLE();
 }
 
-RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) {
+RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit,
+                                      bool is_div) {
+  UNUSED(rl_dest, rl_src1, lit, is_div);
   LOG(FATAL) << "Unexpected use of GenDivRemLit for Mips";
-  return rl_dest;
+  UNREACHABLE();
 }
 
 bool MipsMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
-  DCHECK_NE(cu_->instruction_set, kThumb2);
+  UNUSED(info, is_long, is_object);
   return false;
 }
 
 bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
-  // TODO - add Mips implementation
+  UNUSED(info);
+  // TODO: add Mips implementation.
   return false;
 }
 
 bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
-  // TODO - add Mips implementation
+  UNUSED(info);
+  // TODO: add Mips implementation.
   return false;
 }
 
 bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) {
-  DCHECK_NE(cu_->instruction_set, kThumb2);
+  UNUSED(info);
   return false;
 }
 
@@ -325,23 +333,27 @@
 }
 
 LIR* MipsMir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
+  UNUSED(reg, target);
   LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* MipsMir2Lir::OpVldm(RegStorage r_base, int count) {
+  UNUSED(r_base, count);
   LOG(FATAL) << "Unexpected use of OpVldm for Mips";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* MipsMir2Lir::OpVstm(RegStorage r_base, int count) {
+  UNUSED(r_base, count);
   LOG(FATAL) << "Unexpected use of OpVstm for Mips";
-  return NULL;
+  UNREACHABLE();
 }
 
 void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
                                                 RegLocation rl_result, int lit,
                                                 int first_bit, int second_bit) {
+  UNUSED(lit);
   RegStorage t_reg = AllocTemp();
   OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit);
   OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg);
@@ -373,27 +385,31 @@
 
 bool MipsMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                      RegLocation rl_src, RegLocation rl_dest, int lit) {
+  UNUSED(dalvik_opcode, is_div, rl_src, rl_dest, lit);
   LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips";
-  return false;
+  UNREACHABLE();
 }
 
 bool MipsMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) {
+  UNUSED(rl_src, rl_dest, lit);
   LOG(FATAL) << "Unexpected use of easyMultiply in Mips";
-  return false;
+  UNREACHABLE();
 }
 
 LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) {
+  UNUSED(cond, guide);
   LOG(FATAL) << "Unexpected use of OpIT in Mips";
-  return NULL;
+  UNREACHABLE();
 }
 
 void MipsMir2Lir::OpEndIT(LIR* it) {
+  UNUSED(it);
   LOG(FATAL) << "Unexpected use of OpEndIT in Mips";
 }
 
-
 void MipsMir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest,
                              RegLocation rl_src1, RegLocation rl_src2) {
+  UNUSED(opcode);
   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
@@ -416,6 +432,7 @@
 
 void MipsMir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest,
                              RegLocation rl_src1, RegLocation rl_src2) {
+  UNUSED(opcode);
   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
@@ -437,7 +454,7 @@
 }
 
 void MipsMir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                                 RegLocation rl_src2) {
+                                 RegLocation rl_src2, int flags) {
   switch (opcode) {
     case Instruction::ADD_LONG:
     case Instruction::ADD_LONG_2ADDR:
@@ -456,7 +473,7 @@
   }
 
   // Fallback for all other ops.
-  Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+  Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
 }
 
 void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
@@ -628,15 +645,17 @@
 }
 
 void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                                    RegLocation rl_src1, RegLocation rl_shift) {
+                                    RegLocation rl_src1, RegLocation rl_shift, int flags) {
+  UNUSED(flags);
   // Default implementation is just to ignore the constant case.
   GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
 }
 
 void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode,
-                                    RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
+                                    RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                                    int flags) {
   // Default - bail to non-const handler.
-  GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+  GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
 }
 
 }  // namespace art
diff --git a/compiler/dex/quick/mips/mips_lir.h b/compiler/dex/quick/mips/mips_lir.h
index 495eb16..3615916 100644
--- a/compiler/dex/quick/mips/mips_lir.h
+++ b/compiler/dex/quick/mips/mips_lir.h
@@ -142,7 +142,7 @@
 // This bit determines how the CPU access FP registers.
 #define FR_BIT   0
 
-enum MipsNativeRegisterPool {
+enum MipsNativeRegisterPool {  // private marker to avoid generate-operator-out.py from processing.
   rZERO = RegStorage::k32BitSolo | RegStorage::kCoreRegister |  0,
   rAT   = RegStorage::k32BitSolo | RegStorage::kCoreRegister |  1,
   rV0   = RegStorage::k32BitSolo | RegStorage::kCoreRegister |  2,
@@ -408,9 +408,7 @@
   kMipsBnez,  // bnez s,o [000101] s[25..21] [00000] o[15..0].
   kMipsBne,   // bne s,t,o [000101] s[25..21] t[20..16] o[15..0].
   kMipsDiv,   // div s,t [000000] s[25..21] t[20..16] [0000000000011010].
-#if __mips_isa_rev >= 2
   kMipsExt,   // ext t,s,p,z [011111] s[25..21] t[20..16] z[15..11] p[10..6] [000000].
-#endif
   kMipsJal,   // jal t [000011] t[25..0].
   kMipsJalr,  // jalr d,s [000000] s[25..21] [00000] d[15..11] hint[10..6] [001001].
   kMipsJr,    // jr s [000000] s[25..21] [0000000000] hint[10..6] [001000].
@@ -433,10 +431,8 @@
   kMipsOri,   // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0].
   kMipsPref,  // pref h,o(b) [101011] b[25..21] h[20..16] o[15..0].
   kMipsSb,    // sb t,o(b) [101000] b[25..21] t[20..16] o[15..0].
-#if __mips_isa_rev >= 2
   kMipsSeb,   // seb d,t [01111100000] t[20..16] d[15..11] [10000100000].
   kMipsSeh,   // seh d,t [01111100000] t[20..16] d[15..11] [11000100000].
-#endif
   kMipsSh,    // sh t,o(b) [101001] b[25..21] t[20..16] o[15..0].
   kMipsSll,   // sll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [000000].
   kMipsSllv,  // sllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000100].
@@ -481,15 +477,17 @@
   kMipsUndefined,  // undefined [011001xxxxxxxxxxxxxxxx].
   kMipsLast
 };
+std::ostream& operator<<(std::ostream& os, const MipsOpCode& rhs);
 
 // Instruction assembly field_loc kind.
 enum MipsEncodingKind {
   kFmtUnused,
-  kFmtBitBlt,    /* Bit string using end/start */
-  kFmtDfp,       /* Double FP reg */
-  kFmtSfp,       /* Single FP reg */
-  kFmtBlt5_2,    /* Same 5-bit field to 2 locations */
+  kFmtBitBlt,    // Bit string using end/start.
+  kFmtDfp,       // Double FP reg.
+  kFmtSfp,       // Single FP reg
+  kFmtBlt5_2,    // Same 5-bit field to 2 locations.
 };
+std::ostream& operator<<(std::ostream& os, const MipsEncodingKind& rhs);
 
 // Struct used to define the snippet positions for each MIPS opcode.
 struct MipsEncodingMap {
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index bc91fbc..4a340ec 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"
@@ -420,6 +421,7 @@
 }
 
 bool MipsMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
+  UNUSED(barrier_kind);
 #if ANDROID_SMP != 0
   NewLIR1(kMipsSync, 0 /* Only stype currently supported */);
   return true;
@@ -429,16 +431,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;
@@ -573,11 +575,10 @@
 MipsMir2Lir::MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
     : Mir2Lir(cu, mir_graph, arena) {
   for (int i = 0; i < kMipsLast; i++) {
-    if (MipsMir2Lir::EncodingMap[i].opcode != i) {
-      LOG(FATAL) << "Encoding order for " << MipsMir2Lir::EncodingMap[i].name
-                 << " is wrong: expecting " << i << ", seeing "
-                 << static_cast<int>(MipsMir2Lir::EncodingMap[i].opcode);
-    }
+    DCHECK_EQ(MipsMir2Lir::EncodingMap[i].opcode, i)
+        << "Encoding order for " << MipsMir2Lir::EncodingMap[i].name
+        << " is wrong: expecting " << i << ", seeing "
+        << static_cast<int>(MipsMir2Lir::EncodingMap[i].opcode);
   }
 }
 
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index 7178ede..a7dc84f 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -56,14 +56,17 @@
 }
 
 bool MipsMir2Lir::InexpensiveConstantFloat(int32_t value) {
+  UNUSED(value);
   return false;  // TUNING
 }
 
 bool MipsMir2Lir::InexpensiveConstantLong(int64_t value) {
+  UNUSED(value);
   return false;  // TUNING
 }
 
 bool MipsMir2Lir::InexpensiveConstantDouble(int64_t value) {
+  UNUSED(value);
   return false;  // TUNING
 }
 
@@ -320,25 +323,28 @@
        return NewLIR3(kMipsAndi, r_dest_src1.GetReg(), r_src2.GetReg(), 0xFFFF);
     default:
       LOG(FATAL) << "Bad case in OpRegReg";
-      break;
+      UNREACHABLE();
   }
   return NewLIR2(opcode, r_dest_src1.GetReg(), r_src2.GetReg());
 }
 
 LIR* MipsMir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset,
                               MoveType move_type) {
+  UNUSED(r_dest, r_base, offset, move_type);
   UNIMPLEMENTED(FATAL);
-  return nullptr;
+  UNREACHABLE();
 }
 
 LIR* MipsMir2Lir::OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, MoveType move_type) {
+  UNUSED(r_base, offset, r_src, move_type);
   UNIMPLEMENTED(FATAL);
-  return nullptr;
+  UNREACHABLE();
 }
 
 LIR* MipsMir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src) {
+  UNUSED(op, cc, r_dest, r_src);
   LOG(FATAL) << "Unexpected use of OpCondRegReg for MIPS";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* MipsMir2Lir::LoadConstantWide(RegStorage r_dest, int64_t value) {
@@ -538,7 +544,7 @@
   }
 
   if (mem_ref_type_ == ResourceMask::kDalvikReg) {
-    DCHECK(r_base == rs_rMIPS_SP);
+    DCHECK_EQ(r_base, rs_rMIPS_SP);
     AnnotateDalvikRegAccess(load, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
                             true /* is_load */, pair /* is64bit */);
     if (pair) {
@@ -640,7 +646,7 @@
   }
 
   if (mem_ref_type_ == ResourceMask::kDalvikReg) {
-    DCHECK(r_base == rs_rMIPS_SP);
+    DCHECK_EQ(r_base, rs_rMIPS_SP);
     AnnotateDalvikRegAccess(store, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
                             false /* is_load */, pair /* is64bit */);
     if (pair) {
@@ -681,16 +687,19 @@
 }
 
 LIR* MipsMir2Lir::OpMem(OpKind op, RegStorage r_base, int disp) {
+  UNUSED(op, r_base, disp);
   LOG(FATAL) << "Unexpected use of OpMem for MIPS";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* MipsMir2Lir::OpCondBranch(ConditionCode cc, LIR* target) {
+  UNUSED(cc, target);
   LOG(FATAL) << "Unexpected use of OpCondBranch for MIPS";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* MipsMir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) {
+  UNUSED(trampoline);  // The address of the trampoline is already loaded into r_tgt.
   return OpReg(op, r_tgt);
 }
 
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..92ef70d 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;
@@ -380,10 +417,10 @@
   RegLocation rl_src[3];
   RegLocation rl_dest = mir_graph_->GetBadLoc();
   RegLocation rl_result = mir_graph_->GetBadLoc();
-  Instruction::Code opcode = mir->dalvikInsn.opcode;
-  int opt_flags = mir->optimization_flags;
-  uint32_t vB = mir->dalvikInsn.vB;
-  uint32_t vC = mir->dalvikInsn.vC;
+  const Instruction::Code opcode = mir->dalvikInsn.opcode;
+  const int opt_flags = mir->optimization_flags;
+  const uint32_t vB = mir->dalvikInsn.vB;
+  const uint32_t vC = mir->dalvikInsn.vC;
   DCHECK(CheckCorePoolSanity()) << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " @ 0x:"
                                 << std::hex << current_dalvik_offset_;
 
@@ -445,7 +482,7 @@
 
     case Instruction::RETURN_OBJECT:
       DCHECK(rl_src[0].ref);
-      // Intentional fallthrough.
+      FALLTHROUGH_INTENDED;
     case Instruction::RETURN:
       if (!kLeafOptimization || !mir_graph_->MethodIsLeaf()) {
         GenSuspendTest(opt_flags);
@@ -463,17 +500,11 @@
       break;
 
     case Instruction::MOVE_RESULT_WIDE:
-      if ((opt_flags & MIR_INLINED) != 0) {
-        break;  // Nop - combined w/ previous invoke.
-      }
       StoreValueWide(rl_dest, GetReturnWide(LocToRegClass(rl_dest)));
       break;
 
     case Instruction::MOVE_RESULT:
     case Instruction::MOVE_RESULT_OBJECT:
-      if ((opt_flags & MIR_INLINED) != 0) {
-        break;  // Nop - combined w/ previous invoke.
-      }
       StoreValue(rl_dest, GetReturn(LocToRegClass(rl_dest)));
       break;
 
@@ -541,7 +572,7 @@
       GenThrow(rl_src[0]);
       break;
 
-    case Instruction::ARRAY_LENGTH:
+    case Instruction::ARRAY_LENGTH: {
       int len_offset;
       len_offset = mirror::Array::LengthOffset().Int32Value();
       rl_src[0] = LoadValue(rl_src[0], kRefReg);
@@ -551,7 +582,7 @@
       MarkPossibleNullPointerException(opt_flags);
       StoreValue(rl_dest, rl_result);
       break;
-
+    }
     case Instruction::CONST_STRING:
     case Instruction::CONST_STRING_JUMBO:
       GenConstString(vB, rl_dest);
@@ -562,7 +593,7 @@
       break;
 
     case Instruction::FILL_ARRAY_DATA:
-      GenFillArrayData(vB, rl_src[0]);
+      GenFillArrayData(mir, vB, rl_src[0]);
       break;
 
     case Instruction::FILLED_NEW_ARRAY:
@@ -616,7 +647,6 @@
     case Instruction::IF_GT:
     case Instruction::IF_LE: {
       LIR* taken = &label_list[bb->taken];
-      LIR* fall_through = &label_list[bb->fall_through];
       // Result known at compile time?
       if (rl_src[0].is_const && rl_src[1].is_const) {
         bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg),
@@ -633,11 +663,10 @@
              !mir_graph_->HasSuspendTestBetween(bb, bb->fall_through))) {
           GenSuspendTest(opt_flags);
         }
-        GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken, fall_through);
+        GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken);
       }
       break;
-      }
-
+    }
     case Instruction::IF_EQZ:
     case Instruction::IF_NEZ:
     case Instruction::IF_LTZ:
@@ -645,7 +674,6 @@
     case Instruction::IF_GTZ:
     case Instruction::IF_LEZ: {
       LIR* taken = &label_list[bb->taken];
-      LIR* fall_through = &label_list[bb->fall_through];
       // Result known at compile time?
       if (rl_src[0].is_const) {
         bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg), 0);
@@ -661,10 +689,10 @@
              !mir_graph_->HasSuspendTestBetween(bb, bb->fall_through))) {
           GenSuspendTest(opt_flags);
         }
-        GenCompareZeroAndBranch(opcode, rl_src[0], taken, fall_through);
+        GenCompareZeroAndBranch(opcode, rl_src[0], taken);
       }
       break;
-      }
+    }
 
     case Instruction::AGET_WIDE:
       GenArrayGet(opt_flags, k64, rl_src[0], rl_src[1], rl_dest, 3);
@@ -720,89 +748,117 @@
       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:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kStatic, true));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         // If the invocation is not inlined, we can assume there is already a
         // suspend check at the return site
         mir_graph_->AppendGenSuspendTestList(bb);
@@ -810,71 +866,71 @@
       break;
     case Instruction::INVOKE_STATIC:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kStatic, false));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         mir_graph_->AppendGenSuspendTestList(bb);
       }
       break;
 
     case Instruction::INVOKE_DIRECT:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kDirect, false));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         mir_graph_->AppendGenSuspendTestList(bb);
       }
       break;
     case Instruction::INVOKE_DIRECT_RANGE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kDirect, true));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         mir_graph_->AppendGenSuspendTestList(bb);
       }
       break;
 
     case Instruction::INVOKE_VIRTUAL:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kVirtual, false));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         mir_graph_->AppendGenSuspendTestList(bb);
       }
       break;
     case Instruction::INVOKE_VIRTUAL_RANGE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kVirtual, true));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         mir_graph_->AppendGenSuspendTestList(bb);
       }
       break;
 
     case Instruction::INVOKE_SUPER:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kSuper, false));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         mir_graph_->AppendGenSuspendTestList(bb);
       }
       break;
     case Instruction::INVOKE_SUPER_RANGE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kSuper, true));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         mir_graph_->AppendGenSuspendTestList(bb);
       }
       break;
 
     case Instruction::INVOKE_INTERFACE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kInterface, false));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         mir_graph_->AppendGenSuspendTestList(bb);
       }
       break;
     case Instruction::INVOKE_INTERFACE_RANGE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kInterface, true));
-      if (!kLeafOptimization && (opt_flags & MIR_INLINED) == 0) {
+      if (!kLeafOptimization) {
         mir_graph_->AppendGenSuspendTestList(bb);
       }
       break;
 
     case Instruction::NEG_INT:
     case Instruction::NOT_INT:
-      GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[0]);
+      GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[0], opt_flags);
       break;
 
     case Instruction::NEG_LONG:
     case Instruction::NOT_LONG:
-      GenArithOpLong(opcode, rl_dest, rl_src[0], rl_src[0]);
+      GenArithOpLong(opcode, rl_dest, rl_src[0], rl_src[0], opt_flags);
       break;
 
     case Instruction::NEG_FLOAT:
@@ -934,7 +990,7 @@
         GenArithOpIntLit(opcode, rl_dest, rl_src[0],
                              mir_graph_->ConstantValue(rl_src[1].orig_sreg));
       } else {
-        GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1]);
+        GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1], opt_flags);
       }
       break;
 
@@ -954,7 +1010,7 @@
           InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src[1]), opcode)) {
         GenArithOpIntLit(opcode, rl_dest, rl_src[0], mir_graph_->ConstantValue(rl_src[1]));
       } else {
-        GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1]);
+        GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1], opt_flags);
       }
       break;
 
@@ -969,18 +1025,17 @@
     case Instruction::OR_LONG_2ADDR:
     case Instruction::XOR_LONG_2ADDR:
       if (rl_src[0].is_const || rl_src[1].is_const) {
-        GenArithImmOpLong(opcode, rl_dest, rl_src[0], rl_src[1]);
+        GenArithImmOpLong(opcode, rl_dest, rl_src[0], rl_src[1], opt_flags);
         break;
       }
-      // Note: intentional fallthrough.
-
+      FALLTHROUGH_INTENDED;
     case Instruction::MUL_LONG:
     case Instruction::DIV_LONG:
     case Instruction::REM_LONG:
     case Instruction::MUL_LONG_2ADDR:
     case Instruction::DIV_LONG_2ADDR:
     case Instruction::REM_LONG_2ADDR:
-      GenArithOpLong(opcode, rl_dest, rl_src[0], rl_src[1]);
+      GenArithOpLong(opcode, rl_dest, rl_src[0], rl_src[1], opt_flags);
       break;
 
     case Instruction::SHL_LONG:
@@ -990,34 +1045,42 @@
     case Instruction::SHR_LONG_2ADDR:
     case Instruction::USHR_LONG_2ADDR:
       if (rl_src[1].is_const) {
-        GenShiftImmOpLong(opcode, rl_dest, rl_src[0], rl_src[1]);
+        GenShiftImmOpLong(opcode, rl_dest, rl_src[0], rl_src[1], opt_flags);
       } else {
         GenShiftOpLong(opcode, rl_dest, rl_src[0], rl_src[1]);
       }
       break;
 
+    case Instruction::DIV_FLOAT:
+    case Instruction::DIV_FLOAT_2ADDR:
+      if (HandleEasyFloatingPointDiv(rl_dest, rl_src[0], rl_src[1])) {
+        break;
+      }
+      FALLTHROUGH_INTENDED;
     case Instruction::ADD_FLOAT:
     case Instruction::SUB_FLOAT:
     case Instruction::MUL_FLOAT:
-    case Instruction::DIV_FLOAT:
     case Instruction::REM_FLOAT:
     case Instruction::ADD_FLOAT_2ADDR:
     case Instruction::SUB_FLOAT_2ADDR:
     case Instruction::MUL_FLOAT_2ADDR:
-    case Instruction::DIV_FLOAT_2ADDR:
     case Instruction::REM_FLOAT_2ADDR:
       GenArithOpFloat(opcode, rl_dest, rl_src[0], rl_src[1]);
       break;
 
+    case Instruction::DIV_DOUBLE:
+    case Instruction::DIV_DOUBLE_2ADDR:
+      if (HandleEasyFloatingPointDiv(rl_dest, rl_src[0], rl_src[1])) {
+        break;
+      }
+      FALLTHROUGH_INTENDED;
     case Instruction::ADD_DOUBLE:
     case Instruction::SUB_DOUBLE:
     case Instruction::MUL_DOUBLE:
-    case Instruction::DIV_DOUBLE:
     case Instruction::REM_DOUBLE:
     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:
       GenArithOpDouble(opcode, rl_dest, rl_src[0], rl_src[1]);
       break;
@@ -1077,9 +1140,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 +1198,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();
@@ -1169,6 +1239,7 @@
       // Combine check and work halves of throwing instruction.
       MIR* work_half = mir->meta.throw_insn;
       mir->dalvikInsn.opcode = work_half->dalvikInsn.opcode;
+      mir->optimization_flags = work_half->optimization_flags;
       mir->meta = work_half->meta;  // Whatever the work_half had, we need to copy it.
       opcode = work_half->dalvikInsn.opcode;
       SSARepresentation* ssa_rep = work_half->ssa_rep;
@@ -1196,13 +1267,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;
     }
   }
@@ -1312,8 +1382,9 @@
 }
 
 size_t Mir2Lir::GetInstructionOffset(LIR* lir) {
-  UNIMPLEMENTED(FATAL) << "Unsuppored GetInstructionOffset()";
-  return 0;
+  UNUSED(lir);
+  UNIMPLEMENTED(FATAL) << "Unsupported GetInstructionOffset()";
+  UNREACHABLE();
 }
 
 }  // namespace art
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 3dc111f..bacc6d2 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -33,19 +33,11 @@
 #include "utils/array_ref.h"
 #include "utils/arena_allocator.h"
 #include "utils/arena_containers.h"
-#include "utils/growable_array.h"
+#include "utils/arena_object.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
 
@@ -138,15 +130,16 @@
 #define INVALID_SREG (-1)
 #endif
 
-struct BasicBlock;
+class BasicBlock;
 struct CallInfo;
 struct CompilationUnit;
 struct InlineMethod;
-struct MIR;
+class MIR;
 struct LIR;
 struct RegisterInfo;
 class DexFileMethodInliner;
 class MIRGraph;
+class MirMethodLoweringInfo;
 class Mir2Lir;
 
 typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int,
@@ -187,16 +180,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)
@@ -336,13 +319,10 @@
      * Working plan is, for all targets, to follow mechanism 1 for 64-bit core registers, and
      * mechanism 2 for aliased float registers and x86 vector registers.
      */
-    class RegisterInfo {
+    class RegisterInfo : public ArenaObject<kArenaAllocRegAlloc> {
      public:
       RegisterInfo(RegStorage r, const ResourceMask& mask = kEncodeAll);
       ~RegisterInfo() {}
-      static void* operator new(size_t size, ArenaAllocator* arena) {
-        return arena->Alloc(size, kArenaAllocRegAlloc);
-      }
 
       static const uint32_t k32SoloStorageMask     = 0x00000001;
       static const uint32_t kLowSingleStorageMask  = 0x00000001;
@@ -438,7 +418,7 @@
       RegisterInfo* alias_chain_;  // Chain of aliased registers.
     };
 
-    class RegisterPool {
+    class RegisterPool : public DeletableArenaObject<kArenaAllocRegAlloc> {
      public:
       RegisterPool(Mir2Lir* m2l, ArenaAllocator* arena,
                    const ArrayRef<const RegStorage>& core_regs,
@@ -452,23 +432,20 @@
                    const ArrayRef<const RegStorage>& sp_temps,
                    const ArrayRef<const RegStorage>& dp_temps);
       ~RegisterPool() {}
-      static void* operator new(size_t size, ArenaAllocator* arena) {
-        return arena->Alloc(size, kArenaAllocRegAlloc);
-      }
       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:
@@ -518,20 +495,15 @@
     // has completed.
     //
 
-    class LIRSlowPath {
+    class LIRSlowPath : public ArenaObject<kArenaAllocSlowPaths> {
      public:
       LIRSlowPath(Mir2Lir* m2l, const DexOffset dexpc, LIR* fromfast,
                   LIR* cont = nullptr) :
         m2l_(m2l), cu_(m2l->cu_), current_dex_pc_(dexpc), fromfast_(fromfast), cont_(cont) {
-          m2l->StartSlowPath(this);
       }
       virtual ~LIRSlowPath() {}
       virtual void Compile() = 0;
 
-      static void* operator new(size_t size, ArenaAllocator* arena) {
-        return arena->Alloc(size, kArenaAllocData);
-      }
-
       LIR *GetContinuationLabel() {
         return cont_;
       }
@@ -615,13 +587,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 +657,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();
@@ -710,11 +683,6 @@
     void MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec);
     void MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec);
 
-    virtual void StartSlowPath(LIRSlowPath* slowpath) {}
-    virtual void BeginInvoke(CallInfo* info) {}
-    virtual void EndInvoke(CallInfo* info) {}
-
-
     // Handle bookkeeping to convert a wide RegLocation to a narrow RegLocation.  No code generated.
     virtual RegLocation NarrowRegLoc(RegLocation loc);
 
@@ -730,7 +698,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 +713,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 +724,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);
@@ -821,6 +789,7 @@
     virtual bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                   RegLocation rl_src, RegLocation rl_dest, int lit);
     bool HandleEasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit);
+    bool HandleEasyFloatingPointDiv(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
     virtual void HandleSlowPaths();
     void GenBarrier();
     void GenDivZeroException();
@@ -838,24 +807,24 @@
     LIR* GenNullCheck(RegStorage m_reg, int opt_flags);
     LIR* GenExplicitNullCheck(RegStorage m_reg, int opt_flags);
     virtual void GenImplicitNullCheck(RegStorage reg, int opt_flags);
-    void GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1,
-                             RegLocation rl_src2, LIR* taken, LIR* fall_through);
-    void GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src,
-                                 LIR* taken, LIR* fall_through);
+    void GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1, RegLocation rl_src2,
+                             LIR* taken);
+    void GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, LIR* taken);
     virtual void GenIntToLong(RegLocation rl_dest, RegLocation rl_src);
     void GenIntNarrowing(Instruction::Code opcode, RegLocation rl_dest,
                          RegLocation rl_src);
     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 GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src);
+    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);
 
@@ -872,7 +841,7 @@
     void GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest,
                           RegLocation rl_src, int lit);
     virtual void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                                RegLocation rl_src1, RegLocation rl_src2);
+                                RegLocation rl_src1, RegLocation rl_src2, int flags);
     void GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest, RegLocation rl_src);
     virtual void GenSuspendTest(int opt_flags);
     virtual void GenSuspendTestAndBranch(int opt_flags, LIR* target);
@@ -880,7 +849,7 @@
     // This will be overridden by x86 implementation.
     virtual void GenConstWide(RegLocation rl_dest, int64_t value);
     virtual void GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
-                       RegLocation rl_src1, RegLocation rl_src2);
+                       RegLocation rl_src1, RegLocation rl_src2, int flags);
 
     // Shared by all targets - implemented in gen_invoke.cc.
     LIR* CallHelper(RegStorage r_tgt, QuickEntrypointEnum trampoline, bool safepoint_pc,
@@ -926,6 +895,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 +941,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 +973,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);
@@ -1011,8 +993,6 @@
     }
     // Load Dalvik value with 32-bit memory storage.  If compressed object reference, decompress.
     virtual RegLocation LoadValue(RegLocation rl_src, RegisterClass op_kind);
-    // Same as above, but derive the target register class from the location record.
-    virtual RegLocation LoadValue(RegLocation rl_src);
     // Load Dalvik value with 64-bit memory storage.
     virtual RegLocation LoadValueWide(RegLocation rl_src, RegisterClass op_kind);
     // Load Dalvik value with 32-bit memory storage.  If compressed object reference, decompress.
@@ -1113,11 +1093,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.
     /*
@@ -1139,6 +1121,10 @@
     virtual bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                     RegLocation rl_src, RegLocation rl_dest, int lit) = 0;
     virtual bool EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) = 0;
+    virtual void GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
+                                            int32_t constant) = 0;
+    virtual void GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
+                                             int64_t constant) = 0;
     virtual LIR* CheckSuspendUsingLoad() = 0;
 
     virtual RegStorage LoadHelper(QuickEntrypointEnum trampoline) = 0;
@@ -1164,6 +1150,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.
@@ -1185,14 +1179,18 @@
      */
     virtual RegStorage TargetReg(SpecialTargetRegister reg, WideKind wide_kind) {
       if (wide_kind == kWide) {
-        DCHECK((kArg0 <= reg && reg < kArg7) || (kFArg0 <= reg && reg < kFArg7) || (kRet0 == reg));
-        COMPILE_ASSERT((kArg1 == kArg0 + 1) && (kArg2 == kArg1 + 1) && (kArg3 == kArg2 + 1) &&
-                       (kArg4 == kArg3 + 1) && (kArg5 == kArg4 + 1) && (kArg6 == kArg5 + 1) &&
-                       (kArg7 == kArg6 + 1), kargs_range_unexpected);
-        COMPILE_ASSERT((kFArg1 == kFArg0 + 1) && (kFArg2 == kFArg1 + 1) && (kFArg3 == kFArg2 + 1) &&
-                       (kFArg4 == kFArg3 + 1) && (kFArg5 == kFArg4 + 1) && (kFArg6 == kFArg5 + 1) &&
-                       (kFArg7 == kFArg6 + 1), kfargs_range_unexpected);
-        COMPILE_ASSERT(kRet1 == kRet0 + 1, kret_range_unexpected);
+        DCHECK((kArg0 <= reg && reg < kArg7) || (kFArg0 <= reg && reg < kFArg15) || (kRet0 == reg));
+        static_assert((kArg1 == kArg0 + 1) && (kArg2 == kArg1 + 1) && (kArg3 == kArg2 + 1) &&
+                      (kArg4 == kArg3 + 1) && (kArg5 == kArg4 + 1) && (kArg6 == kArg5 + 1) &&
+                      (kArg7 == kArg6 + 1), "kargs range unexpected");
+        static_assert((kFArg1 == kFArg0 + 1) && (kFArg2 == kFArg1 + 1) && (kFArg3 == kFArg2 + 1) &&
+                      (kFArg4 == kFArg3 + 1) && (kFArg5 == kFArg4 + 1) && (kFArg6 == kFArg5 + 1) &&
+                      (kFArg7 == kFArg6 + 1) && (kFArg8 == kFArg7 + 1) && (kFArg9 == kFArg8 + 1) &&
+                      (kFArg10 == kFArg9 + 1) && (kFArg11 == kFArg10 + 1) &&
+                      (kFArg12 == kFArg11 + 1) && (kFArg13 == kFArg12 + 1) &&
+                      (kFArg14 == kFArg13 + 1) && (kFArg15 == kFArg14 + 1),
+                      "kfargs range unexpected");
+        static_assert(kRet1 == kRet0 + 1, "kret range unexpected");
         return RegStorage::MakeRegPair(TargetReg(reg),
                                        TargetReg(static_cast<SpecialTargetRegister>(reg + 1)));
       } else {
@@ -1253,7 +1251,7 @@
 
     // Required for target - Dalvik-level generators.
     virtual void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                                   RegLocation rl_src1, RegLocation rl_src2) = 0;
+                                   RegLocation rl_src1, RegLocation rl_src2, int flags) = 0;
     virtual void GenArithOpDouble(Instruction::Code opcode,
                                   RegLocation rl_dest, RegLocation rl_src1,
                                   RegLocation rl_src2) = 0;
@@ -1291,10 +1289,11 @@
      * @param rl_src1 Numerator Location.
      * @param rl_src2 Divisor Location.
      * @param is_div 'true' if this is a division, 'false' for a remainder.
-     * @param check_zero 'true' if an exception should be generated if the divisor is 0.
+     * @param flags The instruction optimization flags. It can include information
+     * if exception check can be elided.
      */
     virtual RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
-                                  RegLocation rl_src2, bool is_div, bool check_zero) = 0;
+                                  RegLocation rl_src2, bool is_div, int flags) = 0;
     /*
      * @brief Generate an integer div or rem operation by a literal.
      * @param rl_dest Destination Location.
@@ -1316,7 +1315,6 @@
 
     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 GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) = 0;
     virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) = 0;
 
@@ -1340,7 +1338,7 @@
      */
     virtual void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
                                   int32_t true_val, int32_t false_val, RegStorage rs_dest,
-                                  int dest_reg_class) = 0;
+                                  RegisterClass dest_reg_class) = 0;
 
     /**
      * @brief Used to generate a memory barrier in an architecture specific way.
@@ -1377,7 +1375,7 @@
                              RegLocation rl_index, RegLocation rl_src, int scale,
                              bool card_mark) = 0;
     virtual void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                                   RegLocation rl_src1, RegLocation rl_shift) = 0;
+                                   RegLocation rl_src1, RegLocation rl_shift, int flags) = 0;
 
     // Required for target - single operation generators.
     virtual LIR* OpUnconditionalBranch(LIR* target) = 0;
@@ -1442,9 +1440,30 @@
     virtual bool InexpensiveConstantLong(int64_t value) = 0;
     virtual bool InexpensiveConstantDouble(int64_t value) = 0;
     virtual bool InexpensiveConstantInt(int32_t value, Instruction::Code opcode) {
+      UNUSED(opcode);
       return InexpensiveConstantInt(value);
     }
 
+    /**
+     * @brief Whether division by the given divisor can be converted to multiply by its reciprocal.
+     * @param divisor A constant divisor bits of float type.
+     * @return Returns true iff, x/divisor == x*(1.0f/divisor), for every float x.
+     */
+    bool CanDivideByReciprocalMultiplyFloat(int32_t divisor) {
+      // True, if float value significand bits are 0.
+      return ((divisor & 0x7fffff) == 0);
+    }
+
+    /**
+     * @brief Whether division by the given divisor can be converted to multiply by its reciprocal.
+     * @param divisor A constant divisor bits of double type.
+     * @return Returns true iff, x/divisor == x*(1.0/divisor), for every double x.
+     */
+    bool CanDivideByReciprocalMultiplyDouble(int64_t divisor) {
+      // True, if double value significand bits are 0.
+      return ((divisor & ((UINT64_C(1) << 52) - 1)) == 0);
+    }
+
     // May be optimized by targets.
     virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src);
     virtual void GenMonitorExit(int opt_flags, RegLocation rl_src);
@@ -1482,7 +1501,18 @@
      * is not usual for dx to generate, but it is legal (for now).  In a future rev of
      * dex, we'll want to make this case illegal.
      */
-    bool BadOverlap(RegLocation rl_op1, RegLocation rl_op2);
+    bool PartiallyIntersects(RegLocation rl_op1, RegLocation rl_op2);
+
+    /*
+     * @brief Do these SRs intersect?
+     * @param rl_op1 One RegLocation
+     * @param rl_op2 The other RegLocation
+     * @return 'true' if the VR pairs intersect
+     *
+     * Check to see if a result pair has misaligned overlap or
+     * full overlap with an operand pair.
+     */
+    bool Intersects(RegLocation rl_op1, RegLocation rl_op2);
 
     /*
      * @brief Force a location (in a register) into a temporary register
@@ -1498,10 +1528,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 +1551,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.
@@ -1625,12 +1651,12 @@
     /**
      * Returns true iff wide GPRs are just different views on the same physical register.
      */
-    virtual bool WideGPRsAreAliases() = 0;
+    virtual bool WideGPRsAreAliases() const = 0;
 
     /**
      * Returns true iff wide FPRs are just different views on the same physical register.
      */
-    virtual bool WideFPRsAreAliases() = 0;
+    virtual bool WideFPRsAreAliases() const = 0;
 
 
     enum class WidenessCheck {  // private
@@ -1683,11 +1709,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 +1730,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 +1738,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 +1754,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 +1766,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..a54c55f
--- /dev/null
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -0,0 +1,664 @@
+/*
+ * 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);
+};
+
+static_assert(0U == static_cast<size_t>(kNone),   "kNone not 0");
+static_assert(1U == static_cast<size_t>(kArm),    "kArm not 1");
+static_assert(2U == static_cast<size_t>(kArm64),  "kArm64 not 2");
+static_assert(3U == static_cast<size_t>(kThumb2), "kThumb2 not 3");
+static_assert(4U == static_cast<size_t>(kX86),    "kX86 not 4");
+static_assert(5U == static_cast<size_t>(kX86_64), "kX86_64 not 5");
+static_assert(6U == static_cast<size_t>(kMips),   "kMips not 6");
+static_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
+};
+static_assert(sizeof(kDisabledOptimizationsPerISA) == 8 * sizeof(uint32_t),
+              "kDisabledOpts unexpected");
+
+// 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.
+    ""
+};
+static_assert(sizeof(kSupportedTypes) == 8 * sizeof(char*), "kSupportedTypes unexpected");
+
+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,
+};
+
+static int kInvokeOpcodes[] = {
+    Instruction::INVOKE_VIRTUAL,
+    Instruction::INVOKE_SUPER,
+    Instruction::INVOKE_DIRECT,
+    Instruction::INVOKE_STATIC,
+    Instruction::INVOKE_INTERFACE,
+    Instruction::INVOKE_VIRTUAL_RANGE,
+    Instruction::INVOKE_SUPER_RANGE,
+    Instruction::INVOKE_DIRECT_RANGE,
+    Instruction::INVOKE_STATIC_RANGE,
+    Instruction::INVOKE_INTERFACE_RANGE,
+    Instruction::INVOKE_VIRTUAL_QUICK,
+    Instruction::INVOKE_VIRTUAL_RANGE_QUICK,
+};
+
+// 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
+};
+static_assert(sizeof(kUnsupportedOpcodes) == 8 * sizeof(int*), "kUnsupportedOpcodes unexpected");
+
+// 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),
+};
+static_assert(sizeof(kUnsupportedOpcodesSize) == 8 * sizeof(size_t),
+              "kUnsupportedOpcodesSize unexpected");
+
+// 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 (std::find(kInvokeOpcodes, kInvokeOpcodes + arraysize(kInvokeOpcodes), opcode)
+          != kInvokeOpcodes + arraysize(kInvokeOpcodes)) {
+        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 {
+  UNUSED(compilation_unit);
+  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/runtime/log_severity.h b/compiler/dex/quick/quick_compiler.h
similarity index 62%
copy from runtime/log_severity.h
copy to compiler/dex/quick/quick_compiler.h
index 31682df..10de5fb 100644
--- a/runtime/log_severity.h
+++ b/compiler/dex/quick/quick_compiler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
+#define ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
 
-typedef int LogSeverity;
+namespace art {
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+class Compiler;
+class CompilerDriver;
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+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..0a98c80 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);
@@ -337,26 +316,27 @@
 
 // TODO: this is Thumb2 only.  Remove when DoPromotion refactored.
 RegStorage Mir2Lir::AllocPreservedDouble(int s_reg) {
-  RegStorage res;
+  UNUSED(s_reg);
   UNIMPLEMENTED(FATAL) << "Unexpected use of AllocPreservedDouble";
-  return res;
+  UNREACHABLE();
 }
 
 // TODO: this is Thumb2 only.  Remove when DoPromotion refactored.
 RegStorage Mir2Lir::AllocPreservedSingle(int s_reg) {
-  RegStorage res;
+  UNUSED(s_reg);
   UNIMPLEMENTED(FATAL) << "Unexpected use of AllocPreservedSingle";
-  return res;
+  UNREACHABLE();
 }
 
 
-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);
 }
@@ -1421,6 +1392,7 @@
 }
 
 bool Mir2Lir::LiveOut(int s_reg) {
+  UNUSED(s_reg);
   // For now.
   return true;
 }
diff --git a/compiler/dex/quick/resource_mask.cc b/compiler/dex/quick/resource_mask.cc
index 17995fb..088bec8 100644
--- a/compiler/dex/quick/resource_mask.cc
+++ b/compiler/dex/quick/resource_mask.cc
@@ -33,16 +33,16 @@
     ResourceMask::Bit(ResourceMask::kCCode),
 };
 // The 127-bit is the same as CLZ(masks_[1]) for a ResourceMask with only that bit set.
-COMPILE_ASSERT(kNoRegMasks[127-ResourceMask::kHeapRef].Equals(
-    kEncodeHeapRef), check_kNoRegMasks_heap_ref_index);
-COMPILE_ASSERT(kNoRegMasks[127-ResourceMask::kLiteral].Equals(
-    kEncodeLiteral), check_kNoRegMasks_literal_index);
-COMPILE_ASSERT(kNoRegMasks[127-ResourceMask::kDalvikReg].Equals(
-    kEncodeDalvikReg), check_kNoRegMasks_dalvik_reg_index);
-COMPILE_ASSERT(kNoRegMasks[127-ResourceMask::kFPStatus].Equals(
-    ResourceMask::Bit(ResourceMask::kFPStatus)), check_kNoRegMasks_fp_status_index);
-COMPILE_ASSERT(kNoRegMasks[127-ResourceMask::kCCode].Equals(
-    ResourceMask::Bit(ResourceMask::kCCode)), check_kNoRegMasks_ccode_index);
+static_assert(kNoRegMasks[127-ResourceMask::kHeapRef].Equals(
+    kEncodeHeapRef), "kNoRegMasks heap ref index unexpected");
+static_assert(kNoRegMasks[127-ResourceMask::kLiteral].Equals(
+    kEncodeLiteral), "kNoRegMasks literal index unexpected");
+static_assert(kNoRegMasks[127-ResourceMask::kDalvikReg].Equals(
+    kEncodeDalvikReg), "kNoRegMasks dalvik reg index unexpected");
+static_assert(kNoRegMasks[127-ResourceMask::kFPStatus].Equals(
+    ResourceMask::Bit(ResourceMask::kFPStatus)), "kNoRegMasks fp status index unexpected");
+static_assert(kNoRegMasks[127-ResourceMask::kCCode].Equals(
+    ResourceMask::Bit(ResourceMask::kCCode)), "kNoRegMasks ccode index unexpected");
 
 template <size_t special_bit>
 constexpr ResourceMask OneRegOneSpecial(size_t reg) {
@@ -74,19 +74,19 @@
 }
 
 // The 127-bit is the same as CLZ(masks_[1]) for a ResourceMask with only that bit set.
-COMPILE_ASSERT(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kHeapRef, 0)].Equals(
-    OneRegOneSpecial<ResourceMask::kHeapRef>(0)), check_kSingleRegMasks_heap_ref_index);
-COMPILE_ASSERT(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kLiteral, 0)].Equals(
-    OneRegOneSpecial<ResourceMask::kLiteral>(0)), check_kSingleRegMasks_literal_index);
-COMPILE_ASSERT(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kDalvikReg, 0)].Equals(
-    OneRegOneSpecial<ResourceMask::kDalvikReg>(0)), check_kSingleRegMasks_dalvik_reg_index);
-COMPILE_ASSERT(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kFPStatus, 0)].Equals(
-    OneRegOneSpecial<ResourceMask::kFPStatus>(0)), check_kSingleRegMasks_fp_status_index);
-COMPILE_ASSERT(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kCCode, 0)].Equals(
-    OneRegOneSpecial<ResourceMask::kCCode>(0)), check_kSingleRegMasks_ccode_index);
+static_assert(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kHeapRef, 0)].Equals(
+    OneRegOneSpecial<ResourceMask::kHeapRef>(0)), "kSingleRegMasks heap ref index unexpected");
+static_assert(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kLiteral, 0)].Equals(
+    OneRegOneSpecial<ResourceMask::kLiteral>(0)), "kSingleRegMasks literal index  unexpected");
+static_assert(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kDalvikReg, 0)].Equals(
+    OneRegOneSpecial<ResourceMask::kDalvikReg>(0)), "kSingleRegMasks dalvik reg index unexpected");
+static_assert(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kFPStatus, 0)].Equals(
+    OneRegOneSpecial<ResourceMask::kFPStatus>(0)), "kSingleRegMasks fp status index unexpected");
+static_assert(kSingleRegMasks[SingleRegMaskIndex(127-ResourceMask::kCCode, 0)].Equals(
+    OneRegOneSpecial<ResourceMask::kCCode>(0)), "kSingleRegMasks ccode index unexpected");
 
 // NOTE: arraysize(kNoRegMasks) multiplied by 32 due to the gcc bug workaround, see above.
-COMPILE_ASSERT(arraysize(kSingleRegMasks) == arraysize(kNoRegMasks) * 32, check_arraysizes);
+static_assert(arraysize(kSingleRegMasks) == arraysize(kNoRegMasks) * 32, "arraysizes unexpected");
 
 constexpr ResourceMask kTwoRegsMasks[] = {
 #define TWO(a, b) ResourceMask::Bit(a).Union(ResourceMask::Bit(b))
@@ -115,7 +115,7 @@
         TWO(8, 15), TWO(9, 15), TWO(10, 15), TWO(11, 15), TWO(12, 15), TWO(13, 15), TWO(14, 15),
 #undef TWO
 };
-COMPILE_ASSERT(arraysize(kTwoRegsMasks) ==  16 * 15 / 2, check_arraysize_kTwoRegsMasks);
+static_assert(arraysize(kTwoRegsMasks) ==  16 * 15 / 2, "arraysize of kTwoRegsMasks unexpected");
 
 constexpr size_t TwoRegsIndex(size_t higher, size_t lower) {
   return (higher * (higher - 1)) / 2u + lower;
@@ -136,7 +136,7 @@
       (CheckTwoRegsMaskLine(lines - 1) && CheckTwoRegsMaskTable(lines - 1u));
 }
 
-COMPILE_ASSERT(CheckTwoRegsMaskTable(16), check_two_regs_masks_table);
+static_assert(CheckTwoRegsMaskTable(16), "two regs masks table check failed");
 
 }  // anonymous namespace
 
diff --git a/compiler/dex/quick/resource_mask.h b/compiler/dex/quick/resource_mask.h
index 436cdb5..78e81b2 100644
--- a/compiler/dex/quick/resource_mask.h
+++ b/compiler/dex/quick/resource_mask.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include "base/logging.h"
+#include "base/value_object.h"
 #include "dex/reg_storage.h"
 
 namespace art {
@@ -113,10 +114,7 @@
     return (masks_[0] & other.masks_[0]) != 0u || (masks_[1] & other.masks_[1]) != 0u;
   }
 
-  void SetBit(size_t bit) {
-    DCHECK_LE(bit, kHighestCommonResource);
-    masks_[bit / 64u] |= UINT64_C(1) << (bit & 63u);
-  }
+  void SetBit(size_t bit);
 
   constexpr bool HasBit(size_t bit) const {
     return (masks_[bit / 64u] & (UINT64_C(1) << (bit & 63u))) != 0u;
@@ -139,6 +137,12 @@
 
   friend class ResourceMaskCache;
 };
+std::ostream& operator<<(std::ostream& os, const ResourceMask::ResourceBit& rhs);
+
+inline void ResourceMask::SetBit(size_t bit) {
+  DCHECK_LE(bit, kHighestCommonResource);
+  masks_[bit / 64u] |= UINT64_C(1) << (bit & 63u);
+}
 
 constexpr ResourceMask kEncodeNone = ResourceMask::NoBits();
 constexpr ResourceMask kEncodeAll = ResourceMask::AllBits();
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index 8ebe55c..3933b21 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),
@@ -524,12 +541,17 @@
   { kX86CallI, kCall, IS_UNARY_OP  | IS_BRANCH,                             { 0,             0, 0xE8, 0,    0, 0, 0, 4, false }, "CallI", "!0d" },
   { kX86Ret,   kNullary, NO_OPERAND | IS_BRANCH,                            { 0,             0, 0xC3, 0,    0, 0, 0, 0, false }, "Ret", "" },
 
-  { kX86StartOfMethod, kMacro,  IS_UNARY_OP | SETS_CCODES,             { 0, 0, 0,    0, 0, 0, 0, 0, false }, "StartOfMethod", "!0r" },
+  { kX86StartOfMethod, kMacro,  IS_UNARY_OP | REG_DEF0 | 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", "" },
 };
 
+std::ostream& operator<<(std::ostream& os, const X86OpCode& rhs) {
+  os << X86Mir2Lir::EncodingMap[rhs].name;
+  return os;
+}
+
 static bool NeedsRex(int32_t raw_reg) {
   return RegStorage::RegNum(raw_reg) > 7;
 }
@@ -563,7 +585,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 +613,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;
@@ -654,7 +677,7 @@
     ++size;  // modrm
   }
   if (!modrm_is_reg_reg) {
-    if (has_sib || LowRegisterBits(raw_base) == rs_rX86_SP.GetRegNum()
+    if (has_sib || (LowRegisterBits(raw_base) == rs_rX86_SP_32.GetRegNum())
         || (cu_->target64 && entry->skeleton.prefix1 == THREAD_PREFIX)) {
       // SP requires a SIB byte.
       // GS access also needs a SIB byte for absolute adressing in 64-bit mode.
@@ -662,7 +685,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 +792,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 +922,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) {
@@ -983,9 +1010,9 @@
 void X86Mir2Lir::EmitModrmThread(uint8_t reg_or_opcode) {
   if (cu_->target64) {
     // Absolute adressing for GS access.
-    uint8_t modrm = (0 << 6) | (reg_or_opcode << 3) | rs_rX86_SP.GetRegNum();
+    uint8_t modrm = (0 << 6) | (reg_or_opcode << 3) | rs_rX86_SP_32.GetRegNum();
     code_buffer_.push_back(modrm);
-    uint8_t sib = (0/*TIMES_1*/ << 6) | (rs_rX86_SP.GetRegNum() << 3) | rs_rBP.GetRegNum();
+    uint8_t sib = (0/*TIMES_1*/ << 6) | (rs_rX86_SP_32.GetRegNum() << 3) | rs_rBP.GetRegNum();
     code_buffer_.push_back(sib);
   } else {
     uint8_t modrm = (0 << 6) | (reg_or_opcode << 3) | rs_rBP.GetRegNum();
@@ -998,9 +1025,9 @@
   DCHECK_LT(base, 8);
   uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg_or_opcode << 3) | base;
   code_buffer_.push_back(modrm);
-  if (base == rs_rX86_SP.GetRegNum()) {
+  if (base == rs_rX86_SP_32.GetRegNum()) {
     // Special SIB for SP base
-    code_buffer_.push_back(0 << 6 | rs_rX86_SP.GetRegNum() << 3 | rs_rX86_SP.GetRegNum());
+    code_buffer_.push_back(0 << 6 | rs_rX86_SP_32.GetRegNum() << 3 | rs_rX86_SP_32.GetRegNum());
   }
   EmitDisp(base, disp);
 }
@@ -1009,7 +1036,7 @@
                                   int scale, int32_t disp) {
   DCHECK_LT(RegStorage::RegNum(reg_or_opcode), 8);
   uint8_t modrm = (ModrmForDisp(base, disp) << 6) | RegStorage::RegNum(reg_or_opcode) << 3 |
-      rs_rX86_SP.GetRegNum();
+      rs_rX86_SP_32.GetRegNum();
   code_buffer_.push_back(modrm);
   DCHECK_LT(scale, 4);
   DCHECK_LT(RegStorage::RegNum(index), 8);
@@ -1336,6 +1363,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);
@@ -1544,7 +1584,7 @@
     DCHECK_EQ(0, entry->skeleton.extra_opcode1);
     DCHECK_EQ(0, entry->skeleton.extra_opcode2);
     uint8_t low_reg = LowRegisterBits(raw_reg);
-    uint8_t modrm = (2 << 6) | (low_reg << 3) | rs_rX86_SP.GetRegNum();
+    uint8_t modrm = (2 << 6) | (low_reg << 3) | rs_rX86_SP_32.GetRegNum();
     code_buffer_.push_back(modrm);
     DCHECK_LT(scale, 4);
     uint8_t low_base_or_table = LowRegisterBits(raw_base_or_table);
@@ -1596,6 +1636,7 @@
  * sequence or request that the trace be shortened and retried.
  */
 AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) {
+  UNUSED(start_addr);
   LIR *lir;
   AssemblerStatus res = kSuccess;  // Assume success
 
@@ -1829,6 +1870,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 +1972,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..61dcc28 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);
@@ -124,54 +126,6 @@
   branch_over->target = target;
 }
 
-/*
- * Array data table format:
- *  ushort ident = 0x0300   magic value
- *  ushort width            width of each element in the table
- *  uint   size             number of elements in the table
- *  ubyte  data[size*width] table of data values (may contain a single-byte
- *                          padding at the end)
- *
- * 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;
-  // Add the table to the list - we'll process it later
-  FillArrayData* tab_rec =
-      static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData));
-  tab_rec->table = table;
-  tab_rec->vaddr = current_dalvik_offset_;
-  uint16_t width = tab_rec->table[1];
-  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);
-
-  // Making a call - use explicit registers
-  FlushAllRegs();   /* Everything to home location */
-  RegStorage array_ptr = TargetReg(kArg0, kRef);
-  RegStorage payload = TargetPtrReg(kArg1);
-  RegStorage method_start = TargetPtrReg(kArg2);
-
-  LoadValueDirectFixed(rl_src, array_ptr);
-  // Materialize a pointer to the fill data image
-  if (base_of_code_ != nullptr) {
-    // We can use the saved value.
-    RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
-    if (rl_method.wide) {
-      LoadValueDirectWide(rl_method, method_start);
-    } else {
-      LoadValueDirect(rl_method, method_start);
-    }
-    store_method_addr_used_ = true;
-  } else {
-    NewLIR1(kX86StartOfMethod, method_start.GetReg());
-  }
-  NewLIR2(kX86PcRelAdr, payload.GetReg(), WrapPointer(tab_rec));
-  OpRegReg(kOpAdd, payload, method_start);
-  CallRuntimeHelperRegReg(kQuickHandleFillArrayData, array_ptr, payload, true);
-}
-
 void X86Mir2Lir::GenMoveException(RegLocation rl_dest) {
   int ex_offset = cu_->target64 ?
       Thread::ExceptionOffset<8>().Int32Value() :
@@ -210,16 +164,20 @@
    * expanding the frame or flushing.  This leaves the utility
    * code with no spare temps.
    */
-  LockTemp(rs_rX86_ARG0);
-  LockTemp(rs_rX86_ARG1);
-  LockTemp(rs_rX86_ARG2);
+  const RegStorage arg0 = TargetReg32(kArg0);
+  const RegStorage arg1 = TargetReg32(kArg1);
+  const RegStorage arg2 = TargetReg32(kArg2);
+  LockTemp(arg0);
+  LockTemp(arg1);
+  LockTemp(arg2);
 
   /*
    * We can safely skip the stack overflow check if we're
    * a leaf *and* our frame size < fudge factor.
    */
-  InstructionSet isa =  cu_->target64 ? kX86_64 : kX86;
+  const InstructionSet isa =  cu_->target64 ? kX86_64 : kX86;
   bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, isa);
+  const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
 
   // If we doing an implicit stack overflow check, perform the load immediately
   // before the stack pointer is decremented and anything is saved.
@@ -228,12 +186,12 @@
     // Implicit stack overflow check.
     // test eax,[esp + -overflow]
     int overflow = GetStackOverflowReservedBytes(isa);
-    NewLIR3(kX86Test32RM, rs_rAX.GetReg(), rs_rX86_SP.GetReg(), -overflow);
+    NewLIR3(kX86Test32RM, rs_rAX.GetReg(), rs_rSP.GetReg(), -overflow);
     MarkPossibleStackOverflowException();
   }
 
   /* Build frame, return address already on stack */
-  stack_decrement_ = OpRegImm(kOpSub, rs_rX86_SP, frame_size_ -
+  stack_decrement_ = OpRegImm(kOpSub, rs_rSP, frame_size_ -
                               GetInstructionSetPointerSize(cu_->instruction_set));
 
   NewLIR0(kPseudoMethodEntry);
@@ -250,7 +208,8 @@
         m2l_->ResetRegPool();
         m2l_->ResetDefTracking();
         GenerateTargetLabel(kPseudoThrowTarget);
-        m2l_->OpRegImm(kOpAdd, rs_rX86_SP, sp_displace_);
+        const RegStorage local_rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
+        m2l_->OpRegImm(kOpAdd, local_rs_rSP, sp_displace_);
         m2l_->ClobberCallerSave();
         // Assumes codegen and target are in thumb2 mode.
         m2l_->CallHelper(RegStorage::InvalidReg(), kQuickThrowStackOverflow,
@@ -271,9 +230,9 @@
       // may have moved us outside of the reserved area at the end of the stack.
       // cmp rs_rX86_SP, fs:[stack_end_]; jcc throw_slowpath
       if (cu_->target64) {
-        OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<8>());
+        OpRegThreadMem(kOpCmp, rs_rX86_SP_64, Thread::StackEndOffset<8>());
       } else {
-        OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<4>());
+        OpRegThreadMem(kOpCmp, rs_rX86_SP_32, Thread::StackEndOffset<4>());
       }
       LIR* branch = OpCondBranch(kCondUlt, nullptr);
       AddSlowPath(
@@ -291,13 +250,13 @@
     setup_method_address_[0] = NewLIR1(kX86StartOfMethod, method_start.GetReg());
     int displacement = SRegOffset(base_of_code_->s_reg_low);
     // Native pointer - must be natural word size.
-    setup_method_address_[1] = StoreBaseDisp(rs_rX86_SP, displacement, method_start,
+    setup_method_address_[1] = StoreBaseDisp(rs_rSP, displacement, method_start,
                                              cu_->target64 ? k64 : k32, kNotVolatile);
   }
 
-  FreeTemp(rs_rX86_ARG0);
-  FreeTemp(rs_rX86_ARG1);
-  FreeTemp(rs_rX86_ARG2);
+  FreeTemp(arg0);
+  FreeTemp(arg1);
+  FreeTemp(arg2);
 }
 
 void X86Mir2Lir::GenExitSequence() {
@@ -312,7 +271,9 @@
   UnSpillCoreRegs();
   UnSpillFPRegs();
   /* Remove frame except for return address */
-  stack_increment_ = OpRegImm(kOpAdd, rs_rX86_SP, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set));
+  const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
+  stack_increment_ = OpRegImm(kOpAdd, rs_rSP,
+                              frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set));
   NewLIR0(kX86Ret);
 }
 
@@ -330,4 +291,59 @@
   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,
+                             uintptr_t direct_code, uintptr_t direct_method,
+                             InvokeType type) {
+  UNUSED(info, direct_code);
+  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..d57dffb 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);
 
@@ -67,6 +78,10 @@
   bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src,
                           RegLocation rl_dest, int lit) OVERRIDE;
   bool EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE;
+  void GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
+                                  int32_t constant) OVERRIDE;
+  void GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
+                                   int64_t constant) OVERRIDE;
   LIR* CheckSuspendUsingLoad() OVERRIDE;
   RegStorage LoadHelper(QuickEntrypointEnum trampoline) OVERRIDE;
   LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest,
@@ -124,7 +139,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 +174,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;
@@ -168,11 +184,11 @@
 
   // Long instructions.
   void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                      RegLocation rl_src2) OVERRIDE;
+                      RegLocation rl_src2, int flags) OVERRIDE;
   void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                         RegLocation rl_src2) OVERRIDE;
+                         RegLocation rl_src2, int flags) OVERRIDE;
   void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                         RegLocation rl_src1, RegLocation rl_shift) OVERRIDE;
+                         RegLocation rl_src1, RegLocation rl_shift, int flags) OVERRIDE;
   void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) OVERRIDE;
   void GenIntToLong(RegLocation rl_dest, RegLocation rl_src) OVERRIDE;
   void GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
@@ -233,13 +249,12 @@
   void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
   void GenExitSequence() OVERRIDE;
   void GenSpecialExitSequence() OVERRIDE;
-  void GenFillArrayData(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;
   void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
                         int32_t true_val, int32_t false_val, RegStorage rs_dest,
-                        int dest_reg_class) OVERRIDE;
+                        RegisterClass dest_reg_class) OVERRIDE;
   bool GenMemBarrier(MemBarrierKind barrier_kind) OVERRIDE;
   void GenMoveException(RegLocation rl_dest) OVERRIDE;
   void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
@@ -303,9 +318,10 @@
    * @param rl_dest Destination for the result.
    * @param rl_lhs Left hand operand.
    * @param rl_rhs Right hand operand.
+   * @param flags The instruction optimization flags.
    */
   void GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_lhs,
-                     RegLocation rl_rhs) OVERRIDE;
+                     RegLocation rl_rhs, int flags) OVERRIDE;
 
   /*
    * @brief Load the Method* of a dex method into the register.
@@ -319,14 +335,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 +366,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,21 +381,15 @@
   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;
 
  protected:
-  RegStorage TargetReg32(SpecialTargetRegister reg);
+  RegStorage TargetReg32(SpecialTargetRegister reg) const;
   // Casting of RegStorage
   RegStorage As32BitReg(RegStorage reg) {
     DCHECK(!reg.IsPair());
@@ -410,9 +430,9 @@
   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);
+  RegStorage GetCoreArgMappingToPhysicalReg(int core_arg_num) const;
 
   int AssignInsnOffsets();
   void AssignOffsets();
@@ -460,6 +480,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 +501,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 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 GenMultiplyVectorSignedByte(RegStorage rs_dest_src1, RegStorage rs_src2);
+  void GenMultiplyVectorLong(RegStorage rs_dest_src1, RegStorage rs_src2);
+  void GenShiftByteVector(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 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);
 
@@ -503,7 +530,7 @@
    * @brief Check if a register is byte addressable.
    * @returns true if a register is byte addressable.
    */
-  bool IsByteRegister(RegStorage reg);
+  bool IsByteRegister(RegStorage reg) const;
 
   void GenDivRemLongLit(RegLocation rl_dest, RegLocation rl_src, int64_t imm, bool is_div);
 
@@ -519,149 +546,144 @@
   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.
-   * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector
    * @note vA is the TypeSize for the register.
    * @note vB is the destination XMM register. arg[0..3] are 32 bit constant values.
    */
-  void GenConst128(BasicBlock* bb, MIR* mir);
+  void GenConst128(MIR* mir);
 
   /*
    * @brief MIR to move a vectorized register to another.
-   * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination
    * @note vC: source
    */
-  void GenMoveVector(BasicBlock *bb, MIR *mir);
+  void GenMoveVector(MIR* mir);
 
   /*
-   * @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.
+   * @brief Packed multiply of units in two vector registers: vB = vB .* @note vC using vA to know
+   * the type of the vector.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination and source
    * @note vC: source
    */
-  void GenMultiplyVector(BasicBlock *bb, MIR *mir);
+  void GenMultiplyVector(MIR* mir);
 
   /*
-   * @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.
+   * @brief Packed addition of units in two vector registers: vB = vB .+ vC using vA to know the
+   * type of the vector.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination and source
    * @note vC: source
    */
-  void GenAddVector(BasicBlock *bb, MIR *mir);
+  void GenAddVector(MIR* mir);
 
   /*
-   * @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.
+   * @brief Packed subtraction of units in two vector registers: vB = vB .- vC using vA to know the
+   * type of the vector.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination and source
    * @note vC: source
    */
-  void GenSubtractVector(BasicBlock *bb, MIR *mir);
+  void GenSubtractVector(MIR* mir);
 
   /*
-   * @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.
+   * @brief Packed shift left of units in two vector registers: vB = vB .<< vC using vA to know the
+   * type of the vector.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination and source
    * @note vC: immediate
    */
-  void GenShiftLeftVector(BasicBlock *bb, MIR *mir);
+  void GenShiftLeftVector(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.
-   * @param bb The basic block in which the MIR is from.
+   * @brief Packed signed shift right of units in two vector registers: vB = vB .>> vC using vA to
+   * know the type of the vector.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination and source
    * @note vC: immediate
    */
-  void GenSignedShiftRightVector(BasicBlock *bb, MIR *mir);
+  void GenSignedShiftRightVector(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.
-   * @param bb The basic block in which the MIR is from..
+   * @brief Packed unsigned shift right of units in two vector registers: vB = vB .>>> vC using vA
+   * to know the type of the vector.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination and source
    * @note vC: immediate
    */
-  void GenUnsignedShiftRightVector(BasicBlock *bb, MIR *mir);
+  void GenUnsignedShiftRightVector(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
    */
-  void GenAndVector(BasicBlock *bb, MIR *mir);
+  void GenAndVector(MIR* mir);
 
   /*
-   * @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.
+   * @brief Packed bitwise or of units in two vector registers: vB = vB .| vC using vA to know the
+   * type of the vector.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination and source
    * @note vC: source
    */
-  void GenOrVector(BasicBlock *bb, MIR *mir);
+  void GenOrVector(MIR* mir);
 
   /*
-   * @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.
+   * @brief Packed bitwise xor of units in two vector registers: vB = vB .^ vC using vA to know the
+   * type of the vector.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination and source
    * @note vC: source
    */
-  void GenXorVector(BasicBlock *bb, MIR *mir);
+  void GenXorVector(MIR* mir);
 
   /*
    * @brief Reduce a 128-bit packed element into a single VR by taking lower bits
-   * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @details Instruction does a horizontal addition of the packed elements and then adds it to VR.
    * @note vA: TypeSize
    * @note vB: destination and source VR (not vector register)
    * @note vC: source (vector register)
    */
-  void GenAddReduceVector(BasicBlock *bb, MIR *mir);
+  void GenAddReduceVector(MIR* mir);
 
   /*
    * @brief Extract a packed element into a single VR.
-   * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
    * @note vB: destination VR (not vector register)
    * @note vC: source (vector register)
    * @note arg[0]: The index to use for extraction from vector register (which packed element).
    */
-  void GenReduceVector(BasicBlock *bb, MIR *mir);
+  void GenReduceVector(MIR* mir);
 
   /*
    * @brief Create a vector value, with all TypeSize values equal to vC
@@ -671,7 +693,21 @@
    * @note vB: destination vector register.
    * @note vC: source VR (not vector register).
    */
-  void GenSetVector(BasicBlock *bb, MIR *mir);
+  void GenSetVector(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.
@@ -725,10 +761,11 @@
    * @param rl_src1 Numerator Location.
    * @param rl_src2 Divisor Location.
    * @param is_div 'true' if this is a division, 'false' for a remainder.
-   * @param check_zero 'true' if an exception should be generated if the divisor is 0.
+   * @param flags The instruction optimization flags. It can include information
+   * if exception check can be elided.
    */
   RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
-                        bool is_div, bool check_zero);
+                        bool is_div, int flags);
 
   /*
    * @brief Generate an integer div or rem operation by a literal.
@@ -745,10 +782,11 @@
    * @param rl_dest The destination.
    * @param rl_src The value to be shifted.
    * @param shift_amount How much to shift.
+   * @param flags The instruction optimization flags.
    * @returns the RegLocation of the result.
    */
   RegLocation GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                                RegLocation rl_src, int shift_amount);
+                                RegLocation rl_src, int shift_amount, int flags);
   /*
    * Generate an imul of a register by a constant or a better sequence.
    * @param dest Destination Register.
@@ -815,13 +853,13 @@
 
   // Try to do a long multiplication where rl_src2 is a constant. This simplified setup might fail,
   // in which case false will be returned.
-  bool GenMulLongConst(RegLocation rl_dest, RegLocation rl_src1, int64_t val);
+  bool GenMulLongConst(RegLocation rl_dest, RegLocation rl_src1, int64_t val, int flags);
   void GenMulLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                  RegLocation rl_src2);
+                  RegLocation rl_src2, int flags);
   void GenNotLong(RegLocation rl_dest, RegLocation rl_src);
   void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
   void GenDivRemLong(Instruction::Code, RegLocation rl_dest, RegLocation rl_src1,
-                     RegLocation rl_src2, bool is_div);
+                     RegLocation rl_src2, bool is_div, int flags);
 
   void SpillCoreRegs();
   void UnSpillCoreRegs();
@@ -844,8 +882,8 @@
    * the value is live in a temp register of the correct class.  Additionally, if the value is in
    * a temp register of the wrong register class, it will be clobbered.
    */
-  RegLocation UpdateLocTyped(RegLocation loc, int reg_class);
-  RegLocation UpdateLocWideTyped(RegLocation loc, int reg_class);
+  RegLocation UpdateLocTyped(RegLocation loc);
+  RegLocation UpdateLocWideTyped(RegLocation loc);
 
   /*
    * @brief Analyze MIR before generating code, to prepare for the code generation.
@@ -856,7 +894,7 @@
    * @brief Analyze one basic block.
    * @param bb Basic block to analyze.
    */
-  void AnalyzeBB(BasicBlock * bb);
+  void AnalyzeBB(BasicBlock* bb);
 
   /*
    * @brief Analyze one extended MIR instruction
@@ -864,7 +902,7 @@
    * @param bb Basic block containing instruction.
    * @param mir Extended instruction to analyze.
    */
-  void AnalyzeExtendedMIR(int opcode, BasicBlock * bb, MIR *mir);
+  void AnalyzeExtendedMIR(int opcode, BasicBlock* bb, MIR* mir);
 
   /*
    * @brief Analyze one MIR instruction
@@ -872,7 +910,7 @@
    * @param bb Basic block containing instruction.
    * @param mir Instruction to analyze.
    */
-  virtual void AnalyzeMIR(int opcode, BasicBlock * bb, MIR *mir);
+  virtual void AnalyzeMIR(int opcode, BasicBlock* bb, MIR* mir);
 
   /*
    * @brief Analyze one MIR float/double instruction
@@ -880,7 +918,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.
@@ -894,7 +932,7 @@
    * @param bb Basic block containing instruction.
    * @param mir Instruction to analyze.
    */
-  void AnalyzeInvokeStatic(int opcode, BasicBlock * bb, MIR *mir);
+  void AnalyzeInvokeStatic(int opcode, BasicBlock* bb, MIR* mir);
 
   // Information derived from analysis of MIR
 
@@ -911,13 +949,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,27 +964,26 @@
   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_;
-
-  bool WideGPRsAreAliases() OVERRIDE {
+  bool WideGPRsAreAliases() const OVERRIDE {
     return cu_->target64;  // On 64b, we have 64b GPRs.
   }
-  bool WideFPRsAreAliases() OVERRIDE {
+
+  bool WideFPRsAreAliases() const OVERRIDE {
     return true;  // xmm registers have 64b views even on x86.
   }
 
@@ -956,11 +993,17 @@
    */
   static void DumpRegLocation(RegLocation loc);
 
-  static const X86EncodingMap EncodingMap[kX86Last];
+  InToRegStorageMapping in_to_reg_storage_mapping_;
 
  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);
+
+  static const X86EncodingMap EncodingMap[kX86Last];
+
+  friend std::ostream& operator<<(std::ostream& os, const X86OpCode& rhs);
+
+  DISALLOW_COPY_AND_ASSIGN(X86Mir2Lir);
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index 2920fb6..bc02eee 100755
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -122,6 +122,20 @@
   StoreValueWide(rl_dest, rl_result);
 }
 
+void X86Mir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
+                                            int32_t constant) {
+  // TODO: need x86 implementation.
+  UNUSED(rl_dest, rl_src1, constant);
+  LOG(FATAL) << "Unimplemented GenMultiplyByConstantFloat in x86";
+}
+
+void X86Mir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
+                                             int64_t constant) {
+  // TODO: need x86 implementation.
+  UNUSED(rl_dest, rl_src1, constant);
+  LOG(FATAL) << "Unimplemented GenMultiplyByConstantDouble in x86";
+}
+
 void X86Mir2Lir::GenLongToFP(RegLocation rl_dest, RegLocation rl_src, bool is_double) {
   // Compute offsets to the source and destination VRs on stack
   int src_v_reg_offset = SRegOffset(rl_src.s_reg_low);
@@ -145,12 +159,13 @@
     } else {
       // It must have been register promoted if it is not a temp but is still in physical
       // register. Since we need it to be in memory to convert, we place it there now.
-      StoreBaseDisp(rs_rX86_SP, src_v_reg_offset, rl_src.reg, k64, kNotVolatile);
+      const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
+      StoreBaseDisp(rs_rSP, src_v_reg_offset, rl_src.reg, k64, kNotVolatile);
     }
   }
 
   // Push the source virtual register onto the x87 stack.
-  LIR *fild64 = NewLIR2NoDest(kX86Fild64M, rs_rX86_SP.GetReg(),
+  LIR *fild64 = NewLIR2NoDest(kX86Fild64M, rs_rX86_SP_32.GetReg(),
                               src_v_reg_offset + LOWORD_OFFSET);
   AnnotateDalvikRegAccess(fild64, (src_v_reg_offset + LOWORD_OFFSET) >> 2,
                           true /* is_load */, true /* is64bit */);
@@ -158,7 +173,7 @@
   // Now pop off x87 stack and store it in the destination VR's stack location.
   int opcode = is_double ? kX86Fstp64M : kX86Fstp32M;
   int displacement = is_double ? dest_v_reg_offset + LOWORD_OFFSET : dest_v_reg_offset;
-  LIR *fstp = NewLIR2NoDest(opcode, rs_rX86_SP.GetReg(), displacement);
+  LIR *fstp = NewLIR2NoDest(opcode, rs_rX86_SP_32.GetReg(), displacement);
   AnnotateDalvikRegAccess(fstp, displacement >> 2, false /* is_load */, is_double);
 
   /*
@@ -169,8 +184,7 @@
    * If the result's location is in memory, then we do not need to do anything
    * more since the fstp has already placed the correct value in memory.
    */
-  RegLocation rl_result = is_double ? UpdateLocWideTyped(rl_dest, kFPReg) :
-      UpdateLocTyped(rl_dest, kFPReg);
+  RegLocation rl_result = is_double ? UpdateLocWideTyped(rl_dest) : UpdateLocTyped(rl_dest);
   if (rl_result.location == kLocPhysReg) {
     /*
      * We already know that the result is in a physical register but do not know if it is the
@@ -178,12 +192,13 @@
      * correct register class.
      */
     rl_result = EvalLoc(rl_dest, kFPReg, true);
+    const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
     if (is_double) {
-      LoadBaseDisp(rs_rX86_SP, dest_v_reg_offset, rl_result.reg, k64, kNotVolatile);
+      LoadBaseDisp(rs_rSP, dest_v_reg_offset, rl_result.reg, k64, kNotVolatile);
 
       StoreFinalValueWide(rl_dest, rl_result);
     } else {
-      Load32Disp(rs_rX86_SP, dest_v_reg_offset, rl_result.reg);
+      Load32Disp(rs_rSP, dest_v_reg_offset, rl_result.reg);
 
       StoreFinalValue(rl_dest, rl_result);
     }
@@ -353,6 +368,7 @@
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
 
   // If the source is in physical register, then put it in its location on stack.
+  const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
   if (rl_src1.location == kLocPhysReg) {
     RegisterInfo* reg_info = GetRegInfo(rl_src1.reg);
 
@@ -364,7 +380,7 @@
     } else {
       // It must have been register promoted if it is not a temp but is still in physical
       // register. Since we need it to be in memory to convert, we place it there now.
-      StoreBaseDisp(rs_rX86_SP, src1_v_reg_offset, rl_src1.reg, is_double ? k64 : k32,
+      StoreBaseDisp(rs_rSP, src1_v_reg_offset, rl_src1.reg, is_double ? k64 : k32,
                     kNotVolatile);
     }
   }
@@ -375,7 +391,7 @@
       FlushSpecificReg(reg_info);
       ResetDef(rl_src2.reg);
     } else {
-      StoreBaseDisp(rs_rX86_SP, src2_v_reg_offset, rl_src2.reg, is_double ? k64 : k32,
+      StoreBaseDisp(rs_rSP, src2_v_reg_offset, rl_src2.reg, is_double ? k64 : k32,
                     kNotVolatile);
     }
   }
@@ -383,12 +399,12 @@
   int fld_opcode = is_double ? kX86Fld64M : kX86Fld32M;
 
   // Push the source virtual registers onto the x87 stack.
-  LIR *fld_2 = NewLIR2NoDest(fld_opcode, rs_rX86_SP.GetReg(),
+  LIR *fld_2 = NewLIR2NoDest(fld_opcode, rs_rSP.GetReg(),
                              src2_v_reg_offset + LOWORD_OFFSET);
   AnnotateDalvikRegAccess(fld_2, (src2_v_reg_offset + LOWORD_OFFSET) >> 2,
                           true /* is_load */, is_double /* is64bit */);
 
-  LIR *fld_1 = NewLIR2NoDest(fld_opcode, rs_rX86_SP.GetReg(),
+  LIR *fld_1 = NewLIR2NoDest(fld_opcode, rs_rSP.GetReg(),
                              src1_v_reg_offset + LOWORD_OFFSET);
   AnnotateDalvikRegAccess(fld_1, (src1_v_reg_offset + LOWORD_OFFSET) >> 2,
                           true /* is_load */, is_double /* is64bit */);
@@ -417,7 +433,7 @@
   // Now store result in the destination VR's stack location.
   int displacement = dest_v_reg_offset + LOWORD_OFFSET;
   int opcode = is_double ? kX86Fst64M : kX86Fst32M;
-  LIR *fst = NewLIR2NoDest(opcode, rs_rX86_SP.GetReg(), displacement);
+  LIR *fst = NewLIR2NoDest(opcode, rs_rSP.GetReg(), displacement);
   AnnotateDalvikRegAccess(fst, displacement >> 2, false /* is_load */, is_double /* is64bit */);
 
   // Pop ST(1) and ST(0).
@@ -431,15 +447,14 @@
    * If the result's location is in memory, then we do not need to do anything
    * more since the fstp has already placed the correct value in memory.
    */
-  RegLocation rl_result = is_double ? UpdateLocWideTyped(rl_dest, kFPReg) :
-      UpdateLocTyped(rl_dest, kFPReg);
+  RegLocation rl_result = is_double ? UpdateLocWideTyped(rl_dest) : UpdateLocTyped(rl_dest);
   if (rl_result.location == kLocPhysReg) {
     rl_result = EvalLoc(rl_dest, kFPReg, true);
     if (is_double) {
-      LoadBaseDisp(rs_rX86_SP, dest_v_reg_offset, rl_result.reg, k64, kNotVolatile);
+      LoadBaseDisp(rs_rSP, dest_v_reg_offset, rl_result.reg, k64, kNotVolatile);
       StoreFinalValueWide(rl_dest, rl_result);
     } else {
-      Load32Disp(rs_rX86_SP, dest_v_reg_offset, rl_result.reg);
+      Load32Disp(rs_rSP, dest_v_reg_offset, rl_result.reg);
       StoreFinalValue(rl_dest, rl_result);
     }
   }
@@ -627,7 +642,7 @@
     // Operate directly into memory.
     int displacement = SRegOffset(rl_dest.s_reg_low);
     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-    LIR *lir = NewLIR3(kX86And32MI, rs_rX86_SP.GetReg(), displacement, 0x7fffffff);
+    LIR *lir = NewLIR3(kX86And32MI, rs_rX86_SP_32.GetReg(), displacement, 0x7fffffff);
     AnnotateDalvikRegAccess(lir, displacement >> 2, false /*is_load */, false /* is_64bit */);
     AnnotateDalvikRegAccess(lir, displacement >> 2, true /* is_load */, false /* is_64bit*/);
     return true;
@@ -691,7 +706,7 @@
     // Operate directly into memory.
     int displacement = SRegOffset(rl_dest.s_reg_low);
     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-    LIR *lir = NewLIR3(kX86And32MI, rs_rX86_SP.GetReg(), displacement  + HIWORD_OFFSET, 0x7fffffff);
+    LIR *lir = NewLIR3(kX86And32MI, rs_rX86_SP_32.GetReg(), displacement  + HIWORD_OFFSET, 0x7fffffff);
     AnnotateDalvikRegAccess(lir, (displacement + HIWORD_OFFSET) >> 2, true /* is_load */, true /* is_64bit*/);
     AnnotateDalvikRegAccess(lir, (displacement + HIWORD_OFFSET) >> 2, false /*is_load */, true /* is_64bit */);
     return true;
@@ -730,6 +745,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..781c128 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -20,7 +20,7 @@
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
 #include "mirror/art_method.h"
-#include "mirror/array.h"
+#include "mirror/array-inl.h"
 #include "x86_lir.h"
 
 namespace art {
@@ -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);
@@ -208,7 +208,7 @@
 
 void X86Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
                                   int32_t true_val, int32_t false_val, RegStorage rs_dest,
-                                  int dest_reg_class) {
+                                  RegisterClass dest_reg_class) {
   DCHECK(!left_op.IsPair() && !right_op.IsPair() && !rs_dest.IsPair());
   DCHECK(!left_op.IsFloat() && !right_op.IsFloat() && !rs_dest.IsFloat());
 
@@ -268,6 +268,7 @@
 }
 
 void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
+  UNUSED(bb);
   RegLocation rl_result;
   RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
   RegLocation rl_dest = mir_graph_->GetDest(mir);
@@ -407,8 +408,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);
@@ -594,8 +595,9 @@
 }
 
 RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div) {
+  UNUSED(rl_dest, reg_lo, lit, is_div);
   LOG(FATAL) << "Unexpected use of GenDivRemLit for x86";
-  return rl_dest;
+  UNREACHABLE();
 }
 
 RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src,
@@ -616,6 +618,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 +632,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 +645,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 +661,7 @@
     if (imm < 0) {
       OpReg(kOpNeg, rl_result.reg);
     }
+    done->target = NewLIR0(kPseudoTargetLabel);
   } else {
     CHECK(imm <= -2 || imm >= 2);
 
@@ -676,26 +694,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 +752,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;
@@ -746,15 +765,18 @@
 
 RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi,
                                   bool is_div) {
+  UNUSED(rl_dest, reg_lo, reg_hi, is_div);
   LOG(FATAL) << "Unexpected use of GenDivRem for x86";
-  return rl_dest;
+  UNREACHABLE();
 }
 
 RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
-                                  RegLocation rl_src2, bool is_div, bool check_zero) {
+                                  RegLocation rl_src2, bool is_div, int flags) {
+  UNUSED(rl_dest);
   // 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);
@@ -765,18 +787,24 @@
   // Copy LHS sign bit into EDX.
   NewLIR0(kx86Cdq32Da);
 
-  if (check_zero) {
+  if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
     // Handle division by zero case.
     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 +830,120 @@
 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 (rl_dest.s_reg_low == INVALID_SREG) {
+      // Result is unused, the code is dead. Inlining successful, no code generated.
+      return true;
+    }
+
+    /*
+     * 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.
@@ -886,7 +1026,7 @@
     DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
     // In 32-bit mode the only EAX..EDX registers can be used with Mov8MR.
     if (!cu_->target64 && size == kSignedByte) {
-      rl_src_value = UpdateLocTyped(rl_src_value, kCoreReg);
+      rl_src_value = UpdateLocTyped(rl_src_value);
       if (rl_src_value.location == kLocPhysReg && !IsByteRegister(rl_src_value.reg)) {
         RegStorage temp = AllocateByteRegister();
         OpRegCopy(temp, rl_src_value.reg);
@@ -984,15 +1124,16 @@
     }
     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
     const size_t push_offset = (push_si ? 4u : 0u) + (push_di ? 4u : 0u);
+    const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
     if (!obj_in_si && !obj_in_di) {
-      LoadWordDisp(rs_rX86_SP, SRegOffset(rl_src_obj.s_reg_low) + push_offset, rs_obj);
+      LoadWordDisp(rs_rSP, SRegOffset(rl_src_obj.s_reg_low) + push_offset, rs_obj);
       // Dalvik register annotation in LoadBaseIndexedDisp() used wrong offset. Fix it.
       DCHECK(!DECODE_ALIAS_INFO_WIDE(last_lir_insn_->flags.alias_info));
       int reg_id = DECODE_ALIAS_INFO_REG(last_lir_insn_->flags.alias_info) - push_offset / 4u;
       AnnotateDalvikRegAccess(last_lir_insn_, reg_id, true, false);
     }
     if (!off_in_si && !off_in_di) {
-      LoadWordDisp(rs_rX86_SP, SRegOffset(rl_src_offset.s_reg_low) + push_offset, rs_off);
+      LoadWordDisp(rs_rSP, SRegOffset(rl_src_offset.s_reg_low) + push_offset, rs_off);
       // Dalvik register annotation in LoadBaseIndexedDisp() used wrong offset. Fix it.
       DCHECK(!DECODE_ALIAS_INFO_WIDE(last_lir_insn_->flags.alias_info));
       int reg_id = DECODE_ALIAS_INFO_REG(last_lir_insn_->flags.alias_info) - push_offset / 4u;
@@ -1024,7 +1165,7 @@
     LockTemp(rs_r0);
 
     RegLocation rl_object = LoadValue(rl_src_obj, kRefReg);
-    RegLocation rl_new_value = LoadValue(rl_src_new_value);
+    RegLocation rl_new_value = LoadValue(rl_src_new_value, LocToRegClass(rl_src_new_value));
 
     if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) {
       // Mark card for object assuming new value is stored.
@@ -1070,6 +1211,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);
 
@@ -1096,18 +1314,21 @@
 }
 
 LIR* X86Mir2Lir::OpVldm(RegStorage r_base, int count) {
+  UNUSED(r_base, count);
   LOG(FATAL) << "Unexpected use of OpVldm for x86";
-  return NULL;
+  UNREACHABLE();
 }
 
 LIR* X86Mir2Lir::OpVstm(RegStorage r_base, int count) {
+  UNUSED(r_base, count);
   LOG(FATAL) << "Unexpected use of OpVstm for x86";
-  return NULL;
+  UNREACHABLE();
 }
 
 void X86Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
                                                RegLocation rl_result, int lit,
                                                int first_bit, int second_bit) {
+  UNUSED(lit);
   RegStorage t_reg = AllocTemp();
   OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit);
   OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg);
@@ -1142,10 +1363,10 @@
                                      int len_offset) {
   class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
    public:
-    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch,
-                             RegStorage index, RegStorage array_base, int32_t len_offset)
-        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
-          index_(index), array_base_(array_base), len_offset_(len_offset) {
+    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch_in,
+                             RegStorage index_in, RegStorage array_base_in, int32_t len_offset_in)
+        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch_in),
+          index_(index_in), array_base_(array_base_in), len_offset_(len_offset_in) {
     }
 
     void Compile() OVERRIDE {
@@ -1190,10 +1411,10 @@
                                      int32_t len_offset) {
   class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
    public:
-    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch,
-                             int32_t index, RegStorage array_base, int32_t len_offset)
-        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
-          index_(index), array_base_(array_base), len_offset_(len_offset) {
+    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch_in,
+                             int32_t index_in, RegStorage array_base_in, int32_t len_offset_in)
+        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch_in),
+          index_(index_in), array_base_(array_base_in), len_offset_(len_offset_in) {
     }
 
     void Compile() OVERRIDE {
@@ -1240,22 +1461,27 @@
 
 bool X86Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                     RegLocation rl_src, RegLocation rl_dest, int lit) {
+  UNUSED(dalvik_opcode, is_div, rl_src, rl_dest, lit);
   LOG(FATAL) << "Unexpected use of smallLiteralDive in x86";
-  return false;
+  UNREACHABLE();
 }
 
 bool X86Mir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) {
+  UNUSED(rl_src, rl_dest, lit);
   LOG(FATAL) << "Unexpected use of easyMultiply in x86";
-  return false;
+  UNREACHABLE();
 }
 
 LIR* X86Mir2Lir::OpIT(ConditionCode cond, const char* guide) {
+  UNUSED(cond, guide);
   LOG(FATAL) << "Unexpected use of OpIT in x86";
-  return NULL;
+  UNREACHABLE();
 }
 
 void X86Mir2Lir::OpEndIT(LIR* it) {
+  UNUSED(it);
   LOG(FATAL) << "Unexpected use of OpEndIT in x86";
+  UNREACHABLE();
 }
 
 void X86Mir2Lir::GenImulRegImm(RegStorage dest, RegStorage src, int val) {
@@ -1273,6 +1499,7 @@
 }
 
 void X86Mir2Lir::GenImulMemImm(RegStorage dest, int sreg, int displacement, int val) {
+  UNUSED(sreg);
   // All memory accesses below reference dalvik regs.
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
 
@@ -1281,19 +1508,21 @@
     case 0:
       NewLIR2(kX86Xor32RR, dest.GetReg(), dest.GetReg());
       break;
-    case 1:
-      LoadBaseDisp(rs_rX86_SP, displacement, dest, k32, kNotVolatile);
+    case 1: {
+      const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
+      LoadBaseDisp(rs_rSP, displacement, dest, k32, kNotVolatile);
       break;
+    }
     default:
       m = NewLIR4(IS_SIMM8(val) ? kX86Imul32RMI8 : kX86Imul32RMI, dest.GetReg(),
-                  rs_rX86_SP.GetReg(), displacement, val);
+                  rs_rX86_SP_32.GetReg(), displacement, val);
       AnnotateDalvikRegAccess(m, displacement >> 2, true /* is_load */, true /* is_64bit */);
       break;
   }
 }
 
 void X86Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
-                                RegLocation rl_src2) {
+                                RegLocation rl_src2, int flags) {
   if (!cu_->target64) {
     // Some x86 32b ops are fallback.
     switch (opcode) {
@@ -1302,7 +1531,7 @@
       case Instruction::DIV_LONG_2ADDR:
       case Instruction::REM_LONG:
       case Instruction::REM_LONG_2ADDR:
-        Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+        Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
         return;
 
       default:
@@ -1328,17 +1557,17 @@
 
     case Instruction::MUL_LONG:
     case Instruction::MUL_LONG_2ADDR:
-      GenMulLong(opcode, rl_dest, rl_src1, rl_src2);
+      GenMulLong(opcode, rl_dest, rl_src1, rl_src2, flags);
       return;
 
     case Instruction::DIV_LONG:
     case Instruction::DIV_LONG_2ADDR:
-      GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true);
+      GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags);
       return;
 
     case Instruction::REM_LONG:
     case Instruction::REM_LONG_2ADDR:
-      GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false);
+      GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags);
       return;
 
     case Instruction::AND_LONG_2ADDR:
@@ -1366,7 +1595,7 @@
   }
 }
 
-bool X86Mir2Lir::GenMulLongConst(RegLocation rl_dest, RegLocation rl_src1, int64_t val) {
+bool X86Mir2Lir::GenMulLongConst(RegLocation rl_dest, RegLocation rl_src1, int64_t val, int flags) {
   // All memory accesses below reference dalvik regs.
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
 
@@ -1384,14 +1613,14 @@
     StoreValueWide(rl_dest, rl_src1);
     return true;
   } else if (val == 2) {
-    GenArithOpLong(Instruction::ADD_LONG, rl_dest, rl_src1, rl_src1);
+    GenArithOpLong(Instruction::ADD_LONG, rl_dest, rl_src1, rl_src1, flags);
     return true;
   } else if (IsPowerOfTwo(val)) {
     int shift_amount = LowestSetBit(val);
-    if (!BadOverlap(rl_src1, rl_dest)) {
+    if (!PartiallyIntersects(rl_src1, rl_dest)) {
       rl_src1 = LoadValueWide(rl_src1, kCoreReg);
       RegLocation rl_result = GenShiftImmOpLong(Instruction::SHL_LONG, rl_dest, rl_src1,
-                                                shift_amount);
+                                                shift_amount, flags);
       StoreValueWide(rl_dest, rl_result);
       return true;
     }
@@ -1401,9 +1630,9 @@
   if (!cu_->target64) {
     int32_t val_lo = Low32Bits(val);
     int32_t val_hi = High32Bits(val);
-    FlushAllRegs();
-    LockCallTemps();  // Prepare for explicit register usage.
-    rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
+    // Prepare for explicit register usage.
+    ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
+    rl_src1 = UpdateLocWideTyped(rl_src1);
     bool src1_in_reg = rl_src1.location == kLocPhysReg;
     int displacement = SRegOffset(rl_src1.s_reg_low);
 
@@ -1427,7 +1656,7 @@
     if (src1_in_reg) {
       NewLIR1(kX86Mul32DaR, rl_src1.reg.GetLowReg());
     } else {
-      LIR *m = NewLIR2(kX86Mul32DaM, rs_rX86_SP.GetReg(), displacement + LOWORD_OFFSET);
+      LIR *m = NewLIR2(kX86Mul32DaM, rs_rX86_SP_32.GetReg(), displacement + LOWORD_OFFSET);
       AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2,
                               true /* is_load */, true /* is_64bit */);
     }
@@ -1445,13 +1674,13 @@
 }
 
 void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation rl_src1,
-                            RegLocation rl_src2) {
+                            RegLocation rl_src2, int flags) {
   if (rl_src1.is_const) {
     std::swap(rl_src1, rl_src2);
   }
 
   if (rl_src2.is_const) {
-    if (GenMulLongConst(rl_dest, rl_src1, mir_graph_->ConstantValueWide(rl_src2))) {
+    if (GenMulLongConst(rl_dest, rl_src1, mir_graph_->ConstantValueWide(rl_src2), flags)) {
       return;
     }
   }
@@ -1485,20 +1714,21 @@
   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.
-  rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
-  rl_src2 = UpdateLocWideTyped(rl_src2, kCoreReg);
+  // Prepare for explicit register usage.
+  ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
+  rl_src1 = UpdateLocWideTyped(rl_src1);
+  rl_src2 = UpdateLocWideTyped(rl_src2);
 
   // At this point, the VRs are in their home locations.
   bool src1_in_reg = rl_src1.location == kLocPhysReg;
   bool src2_in_reg = rl_src2.location == kLocPhysReg;
+  const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
 
   // ECX <- 1H
   if (src1_in_reg) {
     NewLIR2(kX86Mov32RR, rs_r1.GetReg(), rl_src1.reg.GetHighReg());
   } else {
-    LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src1.s_reg_low) + HIWORD_OFFSET, rs_r1, k32,
+    LoadBaseDisp(rs_rSP, SRegOffset(rl_src1.s_reg_low) + HIWORD_OFFSET, rs_r1, k32,
                  kNotVolatile);
   }
 
@@ -1509,7 +1739,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_32.GetReg(),
                        displacement + LOWORD_OFFSET);
       AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2,
                               true /* is_load */, true /* is_64bit */);
@@ -1522,7 +1752,7 @@
     if (src2_in_reg) {
       NewLIR2(kX86Mov32RR, rs_r0.GetReg(), rl_src2.reg.GetHighReg());
     } else {
-      LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + HIWORD_OFFSET, rs_r0, k32,
+      LoadBaseDisp(rs_rSP, SRegOffset(rl_src2.s_reg_low) + HIWORD_OFFSET, rs_r0, k32,
                    kNotVolatile);
     }
 
@@ -1531,7 +1761,7 @@
       NewLIR2(kX86Imul32RR, rs_r0.GetReg(), rl_src1.reg.GetLowReg());
     } else {
       int displacement = SRegOffset(rl_src1.s_reg_low);
-      LIR *m = NewLIR3(kX86Imul32RM, rs_r0.GetReg(), rs_rX86_SP.GetReg(),
+      LIR *m = NewLIR3(kX86Imul32RM, rs_r0.GetReg(), rs_rX86_SP_32.GetReg(),
                        displacement + LOWORD_OFFSET);
       AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2,
                               true /* is_load */, true /* is_64bit */);
@@ -1542,7 +1772,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_32.GetReg(),
                        displacement + LOWORD_OFFSET);
       AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2,
                               true /* is_load */, true /* is_64bit */);
@@ -1556,7 +1786,7 @@
   if (src2_in_reg) {
     NewLIR2(kX86Mov32RR, rs_r0.GetReg(), rl_src2.reg.GetLowReg());
   } else {
-    LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + LOWORD_OFFSET, rs_r0, k32,
+    LoadBaseDisp(rs_rSP, SRegOffset(rl_src2.s_reg_low) + LOWORD_OFFSET, rs_r0, k32,
                  kNotVolatile);
   }
 
@@ -1565,7 +1795,7 @@
     NewLIR1(kX86Mul32DaR, rl_src1.reg.GetLowReg());
   } else {
     int displacement = SRegOffset(rl_src1.s_reg_low);
-    LIR *m = NewLIR2(kX86Mul32DaM, rs_rX86_SP.GetReg(), displacement + LOWORD_OFFSET);
+    LIR *m = NewLIR2(kX86Mul32DaM, rs_rX86_SP_32.GetReg(), displacement + LOWORD_OFFSET);
     AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2,
                             true /* is_load */, true /* is_64bit */);
   }
@@ -1600,7 +1830,6 @@
 
       x86op = GetOpcode(op, rl_dest, rl_src, true);
       NewLIR2(x86op, rl_dest.reg.GetHighReg(), rl_src.reg.GetHighReg());
-      FreeTemp(rl_src.reg);  // ???
     }
     return;
   }
@@ -1608,7 +1837,7 @@
   // RHS is in memory.
   DCHECK((rl_src.location == kLocDalvikFrame) ||
          (rl_src.location == kLocCompilerTemp));
-  int r_base = rs_rX86_SP.GetReg();
+  int r_base = rs_rX86_SP_32.GetReg();
   int displacement = SRegOffset(rl_src.s_reg_low);
 
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
@@ -1625,12 +1854,20 @@
 }
 
 void X86Mir2Lir::GenLongArith(RegLocation rl_dest, RegLocation rl_src, Instruction::Code op) {
-  rl_dest = UpdateLocWideTyped(rl_dest, kCoreReg);
+  rl_dest = UpdateLocWideTyped(rl_dest);
   if (rl_dest.location == kLocPhysReg) {
     // Ensure we are in a register pair
     RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
 
-    rl_src = UpdateLocWideTyped(rl_src, kCoreReg);
+    rl_src = UpdateLocWideTyped(rl_src);
+    GenLongRegOrMemOp(rl_result, rl_src, op);
+    StoreFinalValueWide(rl_dest, rl_result);
+    return;
+  } else if (!cu_->target64 && Intersects(rl_src, rl_dest)) {
+    // Handle the case when src and dest are intersect.
+    rl_src = LoadValueWide(rl_src, kCoreReg);
+    RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
+    rl_src = UpdateLocWideTyped(rl_src);
     GenLongRegOrMemOp(rl_result, rl_src, op);
     StoreFinalValueWide(rl_dest, rl_result);
     return;
@@ -1643,7 +1880,7 @@
 
   // Operate directly into memory.
   X86OpCode x86op = GetOpcode(op, rl_dest, rl_src, false);
-  int r_base = rs_rX86_SP.GetReg();
+  int r_base = rs_rX86_SP_32.GetReg();
   int displacement = SRegOffset(rl_dest.s_reg_low);
 
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
@@ -1661,7 +1898,6 @@
     AnnotateDalvikRegAccess(lir, (displacement + HIWORD_OFFSET) >> 2,
                             false /* is_load */, true /* is64bit */);
   }
-  FreeTemp(rl_src.reg);
 }
 
 void X86Mir2Lir::GenLongArith(RegLocation rl_dest, RegLocation rl_src1,
@@ -1691,7 +1927,7 @@
     rl_result = ForceTempWide(rl_result);
 
     // Perform the operation using the RHS.
-    rl_src2 = UpdateLocWideTyped(rl_src2, kCoreReg);
+    rl_src2 = UpdateLocWideTyped(rl_src2);
     GenLongRegOrMemOp(rl_result, rl_src2, op);
 
     // And now record that the result is in the temp.
@@ -1700,10 +1936,9 @@
   }
 
   // It wasn't in registers, so it better be in memory.
-  DCHECK((rl_dest.location == kLocDalvikFrame) ||
-         (rl_dest.location == kLocCompilerTemp));
-  rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
-  rl_src2 = UpdateLocWideTyped(rl_src2, kCoreReg);
+  DCHECK((rl_dest.location == kLocDalvikFrame) || (rl_dest.location == kLocCompilerTemp));
+  rl_src1 = UpdateLocWideTyped(rl_src1);
+  rl_src2 = UpdateLocWideTyped(rl_src2);
 
   // Get one of the source operands into temporary register.
   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
@@ -1830,7 +2065,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 +2090,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);
+      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_32.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 +2166,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);
@@ -1935,7 +2180,7 @@
 }
 
 void X86Mir2Lir::GenDivRemLong(Instruction::Code, RegLocation rl_dest, RegLocation rl_src1,
-                               RegLocation rl_src2, bool is_div) {
+                               RegLocation rl_src2, bool is_div, int flags) {
   if (!cu_->target64) {
     LOG(FATAL) << "Unexpected use GenDivRemLong()";
     return;
@@ -1949,8 +2194,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);
@@ -1962,11 +2207,13 @@
   NewLIR0(kx86Cqo64Da);
 
   // Handle division by zero case.
-  GenDivZeroCheckWide(rs_r1q);
+  if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
+    GenDivZeroCheckWide(rs_r1q);
+  }
 
   // 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 +2396,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.
@@ -2163,7 +2410,8 @@
 }
 
 RegLocation X86Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                                          RegLocation rl_src, int shift_amount) {
+                                          RegLocation rl_src, int shift_amount, int flags) {
+  UNUSED(flags);
   RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
   if (cu_->target64) {
     OpKind op = static_cast<OpKind>(0);    /* Make gcc happy */
@@ -2248,7 +2496,7 @@
 }
 
 void X86Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
-                                   RegLocation rl_src, RegLocation rl_shift) {
+                                   RegLocation rl_src, RegLocation rl_shift, int flags) {
   // Per spec, we only care about low 6 bits of shift amount.
   int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
   if (shift_amount == 0) {
@@ -2258,20 +2506,21 @@
   } else if (shift_amount == 1 &&
             (opcode ==  Instruction::SHL_LONG || opcode == Instruction::SHL_LONG_2ADDR)) {
     // Need to handle this here to avoid calling StoreValueWide twice.
-    GenArithOpLong(Instruction::ADD_LONG, rl_dest, rl_src, rl_src);
+    GenArithOpLong(Instruction::ADD_LONG, rl_dest, rl_src, rl_src, flags);
     return;
   }
-  if (BadOverlap(rl_src, rl_dest)) {
+  if (PartiallyIntersects(rl_src, rl_dest)) {
     GenShiftOpLong(opcode, rl_dest, rl_src, rl_shift);
     return;
   }
   rl_src = LoadValueWide(rl_src, kCoreReg);
-  RegLocation rl_result = GenShiftImmOpLong(opcode, rl_dest, rl_src, shift_amount);
+  RegLocation rl_result = GenShiftImmOpLong(opcode, rl_dest, rl_src, shift_amount, flags);
   StoreValueWide(rl_dest, rl_result);
 }
 
 void X86Mir2Lir::GenArithImmOpLong(Instruction::Code opcode,
-                                   RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
+                                   RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                                   int flags) {
   bool isConstSuccess = false;
   switch (opcode) {
     case Instruction::ADD_LONG:
@@ -2290,7 +2539,7 @@
       if (rl_src2.is_const) {
         isConstSuccess = GenLongLongImm(rl_dest, rl_src1, rl_src2, opcode);
       } else {
-        GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+        GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
         isConstSuccess = true;
       }
       break;
@@ -2316,7 +2565,7 @@
 
   if (!isConstSuccess) {
     // Default - bail to non-const handler.
-    GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+    GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
   }
 }
 
@@ -2460,7 +2709,7 @@
       return in_mem ? kX86Xor32MI : kX86Xor32RI;
     default:
       LOG(FATAL) << "Unexpected opcode: " << op;
-      return kX86Add32MI;
+      UNREACHABLE();
   }
 }
 
@@ -2474,11 +2723,11 @@
       return false;
     }
 
-    rl_dest = UpdateLocWideTyped(rl_dest, kCoreReg);
+    rl_dest = UpdateLocWideTyped(rl_dest);
 
     if ((rl_dest.location == kLocDalvikFrame) ||
         (rl_dest.location == kLocCompilerTemp)) {
-      int r_base = rs_rX86_SP.GetReg();
+      int r_base = rs_rX86_SP_32.GetReg();
       int displacement = SRegOffset(rl_dest.s_reg_low);
 
       ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
@@ -2504,12 +2753,12 @@
 
   int32_t val_lo = Low32Bits(val);
   int32_t val_hi = High32Bits(val);
-  rl_dest = UpdateLocWideTyped(rl_dest, kCoreReg);
+  rl_dest = UpdateLocWideTyped(rl_dest);
 
   // Can we just do this into memory?
   if ((rl_dest.location == kLocDalvikFrame) ||
       (rl_dest.location == kLocCompilerTemp)) {
-    int r_base = rs_rX86_SP.GetReg();
+    int r_base = rs_rX86_SP_32.GetReg();
     int displacement = SRegOffset(rl_dest.s_reg_low);
 
     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
@@ -2580,8 +2829,8 @@
 
   int32_t val_lo = Low32Bits(val);
   int32_t val_hi = High32Bits(val);
-  rl_dest = UpdateLocWideTyped(rl_dest, kCoreReg);
-  rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
+  rl_dest = UpdateLocWideTyped(rl_dest);
+  rl_src1 = UpdateLocWideTyped(rl_src1);
 
   // Can we do this directly into the destination registers?
   if (rl_dest.location == kLocPhysReg && rl_src1.location == kLocPhysReg &&
@@ -2688,7 +2937,7 @@
 }
 
 void X86Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
-                            RegLocation rl_lhs, RegLocation rl_rhs) {
+                            RegLocation rl_lhs, RegLocation rl_rhs, int flags) {
   OpKind op = kOpBkpt;
   bool is_div_rem = false;
   bool unary = false;
@@ -2706,25 +2955,25 @@
       break;
     case Instruction::ADD_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::ADD_INT:
       op = kOpAdd;
       break;
     case Instruction::SUB_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::SUB_INT:
       op = kOpSub;
       break;
     case Instruction::MUL_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::MUL_INT:
       op = kOpMul;
       break;
     case Instruction::DIV_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::DIV_INT:
       op = kOpDiv;
       is_div_rem = true;
@@ -2732,46 +2981,46 @@
     /* NOTE: returns in kArg1 */
     case Instruction::REM_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::REM_INT:
       op = kOpRem;
       is_div_rem = true;
       break;
     case Instruction::AND_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::AND_INT:
       op = kOpAnd;
       break;
     case Instruction::OR_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::OR_INT:
       op = kOpOr;
       break;
     case Instruction::XOR_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::XOR_INT:
       op = kOpXor;
       break;
     case Instruction::SHL_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::SHL_INT:
       shift_op = true;
       op = kOpLsl;
       break;
     case Instruction::SHR_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::SHR_INT:
       shift_op = true;
       op = kOpAsr;
       break;
     case Instruction::USHR_INT_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::USHR_INT:
       shift_op = true;
       op = kOpLsr;
@@ -2793,7 +3042,7 @@
 
   // Get the div/rem stuff out of the way.
   if (is_div_rem) {
-    rl_result = GenDivRem(rl_dest, rl_lhs, rl_rhs, op == kOpDiv, true);
+    rl_result = GenDivRem(rl_dest, rl_lhs, rl_rhs, op == kOpDiv, flags);
     StoreValue(rl_dest, rl_result);
     return;
   }
@@ -2803,7 +3052,7 @@
 
   if (unary) {
     rl_lhs = LoadValue(rl_lhs, kCoreReg);
-    rl_result = UpdateLocTyped(rl_dest, kCoreReg);
+    rl_result = UpdateLocTyped(rl_dest);
     rl_result = EvalLoc(rl_dest, kCoreReg, true);
     OpRegReg(op, rl_result.reg, rl_lhs.reg);
   } else {
@@ -2813,8 +3062,7 @@
       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);
+        rl_result = UpdateLocTyped(rl_dest);
         if (rl_result.location != kLocPhysReg) {
           // Okay, we can do this into memory
           OpMemReg(op, rl_result, t_reg.GetReg());
@@ -2837,12 +3085,12 @@
       // Multiply is 3 operand only (sort of).
       if (is_two_addr && op != kOpMul) {
         // Can we do this directly into memory?
-        rl_result = UpdateLocTyped(rl_dest, kCoreReg);
+        rl_result = UpdateLocTyped(rl_dest);
         if (rl_result.location == kLocPhysReg) {
           // Ensure res is in a core reg
           rl_result = EvalLoc(rl_dest, kCoreReg, true);
           // Can we do this from memory directly?
-          rl_rhs = UpdateLocTyped(rl_rhs, kCoreReg);
+          rl_rhs = UpdateLocTyped(rl_rhs);
           if (rl_rhs.location != kLocPhysReg) {
             OpRegMem(op, rl_result.reg, rl_rhs);
             StoreFinalValue(rl_dest, rl_result);
@@ -2857,7 +3105,7 @@
         // It might happen rl_rhs and rl_dest are the same VR
         // in this case rl_dest is in reg after LoadValue while
         // rl_result is not updated yet, so do this
-        rl_result = UpdateLocTyped(rl_dest, kCoreReg);
+        rl_result = UpdateLocTyped(rl_dest);
         if (rl_result.location != kLocPhysReg) {
           // Okay, we can do this into memory.
           OpMemReg(op, rl_result, rl_rhs.reg.GetReg());
@@ -2874,8 +3122,8 @@
         }
       } else {
         // Try to use reg/memory instructions.
-        rl_lhs = UpdateLocTyped(rl_lhs, kCoreReg);
-        rl_rhs = UpdateLocTyped(rl_rhs, kCoreReg);
+        rl_lhs = UpdateLocTyped(rl_lhs);
+        rl_rhs = UpdateLocTyped(rl_rhs);
         // We can't optimize with FP registers.
         if (!IsOperationSafeWithoutTemps(rl_lhs, rl_rhs)) {
           // Something is difficult, so fall back to the standard case.
@@ -2947,14 +3195,14 @@
     Mir2Lir::GenIntToLong(rl_dest, rl_src);
     return;
   }
-  rl_src = UpdateLocTyped(rl_src, kCoreReg);
+  rl_src = UpdateLocTyped(rl_src);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
   if (rl_src.location == kLocPhysReg) {
     NewLIR2(kX86MovsxdRR, rl_result.reg.GetReg(), rl_src.reg.GetReg());
   } else {
     int displacement = SRegOffset(rl_src.s_reg_low);
     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-    LIR *m = NewLIR3(kX86MovsxdRM, rl_result.reg.GetReg(), rs_rX86_SP.GetReg(),
+    LIR *m = NewLIR3(kX86MovsxdRM, rl_result.reg.GetReg(), rs_rX86_SP_32.GetReg(),
                      displacement + LOWORD_OFFSET);
     AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2,
                             true /* is_load */, true /* is_64bit */);
@@ -2965,7 +3213,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;
   }
 
@@ -2976,19 +3270,19 @@
   switch (opcode) {
     case Instruction::SHL_LONG_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::SHL_LONG:
       op = kOpLsl;
       break;
     case Instruction::SHR_LONG_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::SHR_LONG:
       op = kOpAsr;
       break;
     case Instruction::USHR_LONG_2ADDR:
       is_two_addr = true;
-      // Fallthrough
+      FALLTHROUGH_INTENDED;
     case Instruction::USHR_LONG:
       op = kOpLsr;
       break;
@@ -3001,7 +3295,7 @@
   LoadValueDirectFixed(rl_shift, t_reg);
   if (is_two_addr) {
     // Can we do this directly into memory?
-    rl_result = UpdateLocWideTyped(rl_dest, kCoreReg);
+    rl_result = UpdateLocWideTyped(rl_dest);
     if (rl_result.location != kLocPhysReg) {
       // Okay, we can do this into memory
       ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index f7cb820..db2f272 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/array-inl.h"
+#include "mirror/art_method.h"
 #include "mirror/string.h"
+#include "oat.h"
 #include "x86_lir.h"
+#include "utils/dwarf_cfi.h"
 
 namespace art {
 
@@ -136,46 +141,6 @@
 static constexpr ArrayRef<const RegStorage> xp_temps_32(xp_temps_arr_32);
 static constexpr ArrayRef<const RegStorage> xp_temps_64(xp_temps_arr_64);
 
-RegStorage rs_rX86_SP;
-
-X86NativeRegisterPool rX86_ARG0;
-X86NativeRegisterPool rX86_ARG1;
-X86NativeRegisterPool rX86_ARG2;
-X86NativeRegisterPool rX86_ARG3;
-X86NativeRegisterPool rX86_ARG4;
-X86NativeRegisterPool rX86_ARG5;
-X86NativeRegisterPool rX86_FARG0;
-X86NativeRegisterPool rX86_FARG1;
-X86NativeRegisterPool rX86_FARG2;
-X86NativeRegisterPool rX86_FARG3;
-X86NativeRegisterPool rX86_FARG4;
-X86NativeRegisterPool rX86_FARG5;
-X86NativeRegisterPool rX86_FARG6;
-X86NativeRegisterPool rX86_FARG7;
-X86NativeRegisterPool rX86_RET0;
-X86NativeRegisterPool rX86_RET1;
-X86NativeRegisterPool rX86_INVOKE_TGT;
-X86NativeRegisterPool rX86_COUNT;
-
-RegStorage rs_rX86_ARG0;
-RegStorage rs_rX86_ARG1;
-RegStorage rs_rX86_ARG2;
-RegStorage rs_rX86_ARG3;
-RegStorage rs_rX86_ARG4;
-RegStorage rs_rX86_ARG5;
-RegStorage rs_rX86_FARG0;
-RegStorage rs_rX86_FARG1;
-RegStorage rs_rX86_FARG2;
-RegStorage rs_rX86_FARG3;
-RegStorage rs_rX86_FARG4;
-RegStorage rs_rX86_FARG5;
-RegStorage rs_rX86_FARG6;
-RegStorage rs_rX86_FARG7;
-RegStorage rs_rX86_RET0;
-RegStorage rs_rX86_RET1;
-RegStorage rs_rX86_INVOKE_TGT;
-RegStorage rs_rX86_COUNT;
-
 RegLocation X86Mir2Lir::LocCReturn() {
   return x86_loc_c_return;
 }
@@ -196,44 +161,100 @@
   return x86_loc_c_return_double;
 }
 
+// 32-bit reg storage locations for 32-bit targets.
+static const RegStorage RegStorage32FromSpecialTargetRegister_Target32[] {
+  RegStorage::InvalidReg(),  // kSelf - Thread pointer.
+  RegStorage::InvalidReg(),  // kSuspend - Used to reduce suspend checks for some targets.
+  RegStorage::InvalidReg(),  // kLr - no register as the return address is pushed on entry.
+  RegStorage::InvalidReg(),  // kPc - not exposed on X86 see kX86StartOfMethod.
+  rs_rX86_SP_32,             // kSp
+  rs_rAX,                    // kArg0
+  rs_rCX,                    // kArg1
+  rs_rDX,                    // kArg2
+  rs_rBX,                    // kArg3
+  RegStorage::InvalidReg(),  // kArg4
+  RegStorage::InvalidReg(),  // kArg5
+  RegStorage::InvalidReg(),  // kArg6
+  RegStorage::InvalidReg(),  // kArg7
+  rs_rAX,                    // kFArg0
+  rs_rCX,                    // kFArg1
+  rs_rDX,                    // kFArg2
+  rs_rBX,                    // kFArg3
+  RegStorage::InvalidReg(),  // kFArg4
+  RegStorage::InvalidReg(),  // kFArg5
+  RegStorage::InvalidReg(),  // kFArg6
+  RegStorage::InvalidReg(),  // kFArg7
+  RegStorage::InvalidReg(),  // kFArg8
+  RegStorage::InvalidReg(),  // kFArg9
+  RegStorage::InvalidReg(),  // kFArg10
+  RegStorage::InvalidReg(),  // kFArg11
+  RegStorage::InvalidReg(),  // kFArg12
+  RegStorage::InvalidReg(),  // kFArg13
+  RegStorage::InvalidReg(),  // kFArg14
+  RegStorage::InvalidReg(),  // kFArg15
+  rs_rAX,                    // kRet0
+  rs_rDX,                    // kRet1
+  rs_rAX,                    // kInvokeTgt
+  rs_rAX,                    // kHiddenArg - used to hold the method index before copying to fr0.
+  rs_fr0,                    // kHiddenFpArg
+  rs_rCX,                    // kCount
+};
+
+// 32-bit reg storage locations for 64-bit targets.
+static const RegStorage RegStorage32FromSpecialTargetRegister_Target64[] {
+  RegStorage::InvalidReg(),  // kSelf - Thread pointer.
+  RegStorage::InvalidReg(),  // kSuspend - Used to reduce suspend checks for some targets.
+  RegStorage::InvalidReg(),  // kLr - no register as the return address is pushed on entry.
+  RegStorage::InvalidReg(),  // kPc - TODO: RIP based addressing.
+  rs_rX86_SP_32,             // kSp
+  rs_rDI,                    // kArg0
+  rs_rSI,                    // kArg1
+  rs_rDX,                    // kArg2
+  rs_rCX,                    // kArg3
+  rs_r8,                     // kArg4
+  rs_r9,                     // kArg5
+  RegStorage::InvalidReg(),  // kArg6
+  RegStorage::InvalidReg(),  // kArg7
+  rs_fr0,                    // kFArg0
+  rs_fr1,                    // kFArg1
+  rs_fr2,                    // kFArg2
+  rs_fr3,                    // kFArg3
+  rs_fr4,                    // kFArg4
+  rs_fr5,                    // kFArg5
+  rs_fr6,                    // kFArg6
+  rs_fr7,                    // kFArg7
+  RegStorage::InvalidReg(),  // kFArg8
+  RegStorage::InvalidReg(),  // kFArg9
+  RegStorage::InvalidReg(),  // kFArg10
+  RegStorage::InvalidReg(),  // kFArg11
+  RegStorage::InvalidReg(),  // kFArg12
+  RegStorage::InvalidReg(),  // kFArg13
+  RegStorage::InvalidReg(),  // kFArg14
+  RegStorage::InvalidReg(),  // kFArg15
+  rs_rAX,                    // kRet0
+  rs_rDX,                    // kRet1
+  rs_rAX,                    // kInvokeTgt
+  rs_rAX,                    // kHiddenArg
+  RegStorage::InvalidReg(),  // kHiddenFpArg
+  rs_rCX,                    // kCount
+};
+static_assert(arraysize(RegStorage32FromSpecialTargetRegister_Target32) ==
+              arraysize(RegStorage32FromSpecialTargetRegister_Target64),
+              "Mismatch in RegStorage array sizes");
+
 // Return a target-dependent special register for 32-bit.
-RegStorage X86Mir2Lir::TargetReg32(SpecialTargetRegister reg) {
-  RegStorage res_reg = RegStorage::InvalidReg();
-  switch (reg) {
-    case kSelf: res_reg = RegStorage::InvalidReg(); break;
-    case kSuspend: res_reg =  RegStorage::InvalidReg(); break;
-    case kLr: res_reg =  RegStorage::InvalidReg(); break;
-    case kPc: res_reg =  RegStorage::InvalidReg(); break;
-    case kSp: res_reg =  rs_rX86_SP_32; break;  // This must be the concrete one, as _SP is target-
-                                                // specific size.
-    case kArg0: res_reg = rs_rX86_ARG0; break;
-    case kArg1: res_reg = rs_rX86_ARG1; break;
-    case kArg2: res_reg = rs_rX86_ARG2; break;
-    case kArg3: res_reg = rs_rX86_ARG3; break;
-    case kArg4: res_reg = rs_rX86_ARG4; break;
-    case kArg5: res_reg = rs_rX86_ARG5; break;
-    case kFArg0: res_reg = rs_rX86_FARG0; break;
-    case kFArg1: res_reg = rs_rX86_FARG1; break;
-    case kFArg2: res_reg = rs_rX86_FARG2; break;
-    case kFArg3: res_reg = rs_rX86_FARG3; break;
-    case kFArg4: res_reg = rs_rX86_FARG4; break;
-    case kFArg5: res_reg = rs_rX86_FARG5; break;
-    case kFArg6: res_reg = rs_rX86_FARG6; break;
-    case kFArg7: res_reg = rs_rX86_FARG7; break;
-    case kRet0: res_reg = rs_rX86_RET0; break;
-    case kRet1: res_reg = rs_rX86_RET1; break;
-    case kInvokeTgt: res_reg = rs_rX86_INVOKE_TGT; break;
-    case kHiddenArg: res_reg = rs_rAX; break;
-    case kHiddenFpArg: DCHECK(!cu_->target64); res_reg = rs_fr0; break;
-    case kCount: res_reg = rs_rX86_COUNT; break;
-    default: res_reg = RegStorage::InvalidReg();
-  }
-  return res_reg;
+RegStorage X86Mir2Lir::TargetReg32(SpecialTargetRegister reg) const {
+  DCHECK_EQ(RegStorage32FromSpecialTargetRegister_Target32[kCount], rs_rCX);
+  DCHECK_EQ(RegStorage32FromSpecialTargetRegister_Target64[kCount], rs_rCX);
+  DCHECK_LT(reg, arraysize(RegStorage32FromSpecialTargetRegister_Target32));
+  return cu_->target64 ? RegStorage32FromSpecialTargetRegister_Target64[reg]
+                       : RegStorage32FromSpecialTargetRegister_Target32[reg];
 }
 
 RegStorage X86Mir2Lir::TargetReg(SpecialTargetRegister reg) {
+  UNUSED(reg);
   LOG(FATAL) << "Do not use this function!!!";
-  return RegStorage::InvalidReg();
+  UNREACHABLE();
 }
 
 /*
@@ -362,6 +383,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));
@@ -445,17 +467,17 @@
 RegStorage X86Mir2Lir::AllocateByteRegister() {
   RegStorage reg = AllocTypedTemp(false, kCoreReg);
   if (!cu_->target64) {
-    DCHECK_LT(reg.GetRegNum(), rs_rX86_SP.GetRegNum());
+    DCHECK_LT(reg.GetRegNum(), rs_rX86_SP_32.GetRegNum());
   }
   return reg;
 }
 
 RegStorage X86Mir2Lir::Get128BitRegister(RegStorage reg) {
-  return GetRegInfo(reg)->FindMatchingView(RegisterInfo::k128SoloStorageMask)->GetReg();
+  return GetRegInfo(reg)->Master()->GetReg();
 }
 
-bool X86Mir2Lir::IsByteRegister(RegStorage reg) {
-  return cu_->target64 || reg.GetRegNum() < rs_rX86_SP.GetRegNum();
+bool X86Mir2Lir::IsByteRegister(RegStorage reg) const {
+  return cu_->target64 || reg.GetRegNum() < rs_rX86_SP_32.GetRegNum();
 }
 
 /* Clobber all regs that might be used by an external C call */
@@ -495,8 +517,8 @@
 
 RegLocation X86Mir2Lir::GetReturnWideAlt() {
   RegLocation res = LocCReturnWide();
-  DCHECK(res.reg.GetLowReg() == rs_rAX.GetReg());
-  DCHECK(res.reg.GetHighReg() == rs_rDX.GetReg());
+  DCHECK_EQ(res.reg.GetLowReg(), rs_rAX.GetReg());
+  DCHECK_EQ(res.reg.GetHighReg(), rs_rDX.GetReg());
   Clobber(rs_rAX);
   Clobber(rs_rDX);
   MarkInUse(rs_rAX);
@@ -515,41 +537,41 @@
 
 /* To be used when explicitly managing register use */
 void X86Mir2Lir::LockCallTemps() {
-  LockTemp(rs_rX86_ARG0);
-  LockTemp(rs_rX86_ARG1);
-  LockTemp(rs_rX86_ARG2);
-  LockTemp(rs_rX86_ARG3);
+  LockTemp(TargetReg32(kArg0));
+  LockTemp(TargetReg32(kArg1));
+  LockTemp(TargetReg32(kArg2));
+  LockTemp(TargetReg32(kArg3));
   if (cu_->target64) {
-    LockTemp(rs_rX86_ARG4);
-    LockTemp(rs_rX86_ARG5);
-    LockTemp(rs_rX86_FARG0);
-    LockTemp(rs_rX86_FARG1);
-    LockTemp(rs_rX86_FARG2);
-    LockTemp(rs_rX86_FARG3);
-    LockTemp(rs_rX86_FARG4);
-    LockTemp(rs_rX86_FARG5);
-    LockTemp(rs_rX86_FARG6);
-    LockTemp(rs_rX86_FARG7);
+    LockTemp(TargetReg32(kArg4));
+    LockTemp(TargetReg32(kArg5));
+    LockTemp(TargetReg32(kFArg0));
+    LockTemp(TargetReg32(kFArg1));
+    LockTemp(TargetReg32(kFArg2));
+    LockTemp(TargetReg32(kFArg3));
+    LockTemp(TargetReg32(kFArg4));
+    LockTemp(TargetReg32(kFArg5));
+    LockTemp(TargetReg32(kFArg6));
+    LockTemp(TargetReg32(kFArg7));
   }
 }
 
 /* To be used when explicitly managing register use */
 void X86Mir2Lir::FreeCallTemps() {
-  FreeTemp(rs_rX86_ARG0);
-  FreeTemp(rs_rX86_ARG1);
-  FreeTemp(rs_rX86_ARG2);
-  FreeTemp(rs_rX86_ARG3);
+  FreeTemp(TargetReg32(kArg0));
+  FreeTemp(TargetReg32(kArg1));
+  FreeTemp(TargetReg32(kArg2));
+  FreeTemp(TargetReg32(kArg3));
   if (cu_->target64) {
-    FreeTemp(rs_rX86_ARG4);
-    FreeTemp(rs_rX86_ARG5);
-    FreeTemp(rs_rX86_FARG0);
-    FreeTemp(rs_rX86_FARG1);
-    FreeTemp(rs_rX86_FARG2);
-    FreeTemp(rs_rX86_FARG3);
-    FreeTemp(rs_rX86_FARG4);
-    FreeTemp(rs_rX86_FARG5);
-    FreeTemp(rs_rX86_FARG6);
-    FreeTemp(rs_rX86_FARG7);
+    FreeTemp(TargetReg32(kArg4));
+    FreeTemp(TargetReg32(kArg5));
+    FreeTemp(TargetReg32(kFArg0));
+    FreeTemp(TargetReg32(kFArg1));
+    FreeTemp(TargetReg32(kFArg2));
+    FreeTemp(TargetReg32(kFArg3));
+    FreeTemp(TargetReg32(kFArg4));
+    FreeTemp(TargetReg32(kFArg5));
+    FreeTemp(TargetReg32(kFArg6));
+    FreeTemp(TargetReg32(kFArg7));
   }
 }
 
@@ -594,6 +616,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 +637,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 +654,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 +664,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 +684,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 +708,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() {
@@ -693,11 +721,14 @@
   }
   // Spill mask not including fake return address register
   uint32_t mask = core_spill_mask_ & ~(1 << rs_rRET.GetRegNum());
-  int offset = frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * num_core_spills_);
+  int offset =
+      frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * num_core_spills_);
   OpSize size = cu_->target64 ? k64 : k32;
+  const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
   for (int reg = 0; mask; mask >>= 1, reg++) {
     if (mask & 0x1) {
-      StoreBaseDisp(rs_rX86_SP, offset, cu_->target64 ? RegStorage::Solo64(reg) :  RegStorage::Solo32(reg),
+      StoreBaseDisp(rs_rSP, offset,
+                    cu_->target64 ? RegStorage::Solo64(reg) :  RegStorage::Solo32(reg),
                    size, kNotVolatile);
       offset += GetInstructionSetPointerSize(cu_->instruction_set);
     }
@@ -712,9 +743,10 @@
   uint32_t mask = core_spill_mask_ & ~(1 << rs_rRET.GetRegNum());
   int offset = frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * num_core_spills_);
   OpSize size = cu_->target64 ? k64 : k32;
+  const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
   for (int reg = 0; mask; mask >>= 1, reg++) {
     if (mask & 0x1) {
-      LoadBaseDisp(rs_rX86_SP, offset, cu_->target64 ? RegStorage::Solo64(reg) :  RegStorage::Solo32(reg),
+      LoadBaseDisp(rs_rSP, offset, cu_->target64 ? RegStorage::Solo64(reg) :  RegStorage::Solo32(reg),
                    size, kNotVolatile);
       offset += GetInstructionSetPointerSize(cu_->instruction_set);
     }
@@ -726,11 +758,12 @@
     return;
   }
   uint32_t mask = fp_spill_mask_;
-  int offset = frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * (num_fp_spills_ + num_core_spills_));
+  int offset = frame_size_ -
+      (GetInstructionSetPointerSize(cu_->instruction_set) * (num_fp_spills_ + num_core_spills_));
+  const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
   for (int reg = 0; mask; mask >>= 1, reg++) {
     if (mask & 0x1) {
-      StoreBaseDisp(rs_rX86_SP, offset, RegStorage::FloatSolo64(reg),
-                   k64, kNotVolatile);
+      StoreBaseDisp(rs_rSP, offset, RegStorage::FloatSolo64(reg), k64, kNotVolatile);
       offset += sizeof(double);
     }
   }
@@ -740,10 +773,12 @@
     return;
   }
   uint32_t mask = fp_spill_mask_;
-  int offset = frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * (num_fp_spills_ + num_core_spills_));
+  int offset = frame_size_ -
+      (GetInstructionSetPointerSize(cu_->instruction_set) * (num_fp_spills_ + num_core_spills_));
+  const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
   for (int reg = 0; mask; mask >>= 1, reg++) {
     if (mask & 0x1) {
-      LoadBaseDisp(rs_rX86_SP, offset, RegStorage::FloatSolo64(reg),
+      LoadBaseDisp(rs_rSP, offset, RegStorage::FloatSolo64(reg),
                    k64, kNotVolatile);
       offset += sizeof(double);
     }
@@ -758,10 +793,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,93 +809,21 @@
 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++) {
-      if (X86Mir2Lir::EncodingMap[i].opcode != i) {
-        LOG(FATAL) << "Encoding order for " << X86Mir2Lir::EncodingMap[i].name
-                   << " is wrong: expecting " << i << ", seeing "
-                   << static_cast<int>(X86Mir2Lir::EncodingMap[i].opcode);
-      }
-    }
+      DCHECK_EQ(X86Mir2Lir::EncodingMap[i].opcode, i)
+          << "Encoding order for " << X86Mir2Lir::EncodingMap[i].name
+          << " is wrong: expecting " << i << ", seeing "
+          << static_cast<int>(X86Mir2Lir::EncodingMap[i].opcode);
   }
-  if (cu_->target64) {
-    rs_rX86_SP = rs_rX86_SP_64;
-
-    rs_rX86_ARG0 = rs_rDI;
-    rs_rX86_ARG1 = rs_rSI;
-    rs_rX86_ARG2 = rs_rDX;
-    rs_rX86_ARG3 = rs_rCX;
-    rs_rX86_ARG4 = rs_r8;
-    rs_rX86_ARG5 = rs_r9;
-    rs_rX86_FARG0 = rs_fr0;
-    rs_rX86_FARG1 = rs_fr1;
-    rs_rX86_FARG2 = rs_fr2;
-    rs_rX86_FARG3 = rs_fr3;
-    rs_rX86_FARG4 = rs_fr4;
-    rs_rX86_FARG5 = rs_fr5;
-    rs_rX86_FARG6 = rs_fr6;
-    rs_rX86_FARG7 = rs_fr7;
-    rX86_ARG0 = rDI;
-    rX86_ARG1 = rSI;
-    rX86_ARG2 = rDX;
-    rX86_ARG3 = rCX;
-    rX86_ARG4 = r8;
-    rX86_ARG5 = r9;
-    rX86_FARG0 = fr0;
-    rX86_FARG1 = fr1;
-    rX86_FARG2 = fr2;
-    rX86_FARG3 = fr3;
-    rX86_FARG4 = fr4;
-    rX86_FARG5 = fr5;
-    rX86_FARG6 = fr6;
-    rX86_FARG7 = fr7;
-    rs_rX86_INVOKE_TGT = rs_rDI;
-  } else {
-    rs_rX86_SP = rs_rX86_SP_32;
-
-    rs_rX86_ARG0 = rs_rAX;
-    rs_rX86_ARG1 = rs_rCX;
-    rs_rX86_ARG2 = rs_rDX;
-    rs_rX86_ARG3 = rs_rBX;
-    rs_rX86_ARG4 = RegStorage::InvalidReg();
-    rs_rX86_ARG5 = RegStorage::InvalidReg();
-    rs_rX86_FARG0 = rs_rAX;
-    rs_rX86_FARG1 = rs_rCX;
-    rs_rX86_FARG2 = rs_rDX;
-    rs_rX86_FARG3 = rs_rBX;
-    rs_rX86_FARG4 = RegStorage::InvalidReg();
-    rs_rX86_FARG5 = RegStorage::InvalidReg();
-    rs_rX86_FARG6 = RegStorage::InvalidReg();
-    rs_rX86_FARG7 = RegStorage::InvalidReg();
-    rX86_ARG0 = rAX;
-    rX86_ARG1 = rCX;
-    rX86_ARG2 = rDX;
-    rX86_ARG3 = rBX;
-    rX86_FARG0 = rAX;
-    rX86_FARG1 = rCX;
-    rX86_FARG2 = rDX;
-    rX86_FARG3 = rBX;
-    rs_rX86_INVOKE_TGT = rs_rAX;
-    // TODO(64): Initialize with invalid reg
-//    rX86_ARG4 = RegStorage::InvalidReg();
-//    rX86_ARG5 = RegStorage::InvalidReg();
-  }
-  rs_rX86_RET0 = rs_rAX;
-  rs_rX86_RET1 = rs_rDX;
-  rs_rX86_COUNT = rs_rCX;
-  rX86_RET0 = rAX;
-  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,
@@ -873,8 +833,9 @@
 
 // Not used in x86(-64)
 RegStorage X86Mir2Lir::LoadHelper(QuickEntrypointEnum trampoline) {
+  UNUSED(trampoline);
   LOG(FATAL) << "Unexpected use of LoadHelper in x86";
-  return RegStorage::InvalidReg();
+  UNREACHABLE();
 }
 
 LIR* X86Mir2Lir::CheckSuspendUsingLoad() {
@@ -912,7 +873,7 @@
       (rl_dest.location == kLocCompilerTemp)) {
     int32_t val_lo = Low32Bits(value);
     int32_t val_hi = High32Bits(value);
-    int r_base = rs_rX86_SP.GetReg();
+    int r_base = rs_rX86_SP_32.GetReg();
     int displacement = SRegOffset(rl_dest.s_reg_low);
 
     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
@@ -972,56 +933,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 +1020,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 +1049,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 +1077,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 +1224,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 +1244,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 +1286,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());
   }
 
@@ -1357,7 +1325,7 @@
         // Load the start index from stack, remembering that we pushed EDI.
         int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t);
         ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-        Load32Disp(rs_rX86_SP, displacement, rs_rDI);
+        Load32Disp(rs_rX86_SP_32, displacement, rs_rDI);
         // Dalvik register annotation in LoadBaseIndexedDisp() used wrong offset. Fix it.
         DCHECK(!DECODE_ALIAS_INFO_WIDE(last_lir_insn_->flags.alias_info));
         int reg_id = DECODE_ALIAS_INFO_REG(last_lir_insn_->flags.alias_info) - 1;
@@ -1426,142 +1394,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 +1419,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 +1447,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 +1462,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 +1478,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,49 +1497,58 @@
       ReserveVectorRegisters(mir);
       break;
     case kMirOpReturnVectorRegisters:
-      ReturnVectorRegisters();
+      ReturnVectorRegisters(mir);
       break;
     case kMirOpConstVector:
-      GenConst128(bb, mir);
+      GenConst128(mir);
       break;
     case kMirOpMoveVector:
-      GenMoveVector(bb, mir);
+      GenMoveVector(mir);
       break;
     case kMirOpPackedMultiply:
-      GenMultiplyVector(bb, mir);
+      GenMultiplyVector(mir);
       break;
     case kMirOpPackedAddition:
-      GenAddVector(bb, mir);
+      GenAddVector(mir);
       break;
     case kMirOpPackedSubtract:
-      GenSubtractVector(bb, mir);
+      GenSubtractVector(mir);
       break;
     case kMirOpPackedShiftLeft:
-      GenShiftLeftVector(bb, mir);
+      GenShiftLeftVector(mir);
       break;
     case kMirOpPackedSignedShiftRight:
-      GenSignedShiftRightVector(bb, mir);
+      GenSignedShiftRightVector(mir);
       break;
     case kMirOpPackedUnsignedShiftRight:
-      GenUnsignedShiftRightVector(bb, mir);
+      GenUnsignedShiftRightVector(mir);
       break;
     case kMirOpPackedAnd:
-      GenAndVector(bb, mir);
+      GenAndVector(mir);
       break;
     case kMirOpPackedOr:
-      GenOrVector(bb, mir);
+      GenOrVector(mir);
       break;
     case kMirOpPackedXor:
-      GenXorVector(bb, mir);
+      GenXorVector(mir);
       break;
     case kMirOpPackedAddReduce:
-      GenAddReduceVector(bb, mir);
+      GenAddReduceVector(mir);
       break;
     case kMirOpPackedReduce:
-      GenReduceVector(bb, mir);
+      GenReduceVector(mir);
       break;
     case kMirOpPackedSet:
-      GenSetVector(bb, mir);
+      GenSetVector(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;
@@ -1737,11 +1556,7 @@
 }
 
 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 +1564,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 +1582,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);
+void X86Mir2Lir::GenConst128(MIR* mir) {
   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 +1603,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,25 +1636,21 @@
   // 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;
 }
 
-void X86Mir2Lir::GenMoveVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenMoveVector(MIR* mir) {
   // 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 +1668,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::GenMultiplyVector(BasicBlock *bb, MIR *mir) {
+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(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 +1779,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;
@@ -1904,16 +1791,20 @@
   NewLIR2(opcode, rs_dest_src1.GetReg(), rs_src2.GetReg());
 }
 
-void X86Mir2Lir::GenAddVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenAddVector(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) {
     case k32:
       opcode = kX86PadddRR;
       break;
+    case k64:
+      opcode = kX86PaddqRR;
+      break;
     case kSignedHalf:
     case kUnsignedHalf:
       opcode = kX86PaddwRR;
@@ -1935,16 +1826,20 @@
   NewLIR2(opcode, rs_dest_src1.GetReg(), rs_src2.GetReg());
 }
 
-void X86Mir2Lir::GenSubtractVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenSubtractVector(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) {
     case k32:
       opcode = kX86PsubdRR;
       break;
+    case k64:
+      opcode = kX86PsubqRR;
+      break;
     case kSignedHalf:
     case kUnsignedHalf:
       opcode = kX86PsubwRR;
@@ -1966,59 +1861,55 @@
   NewLIR2(opcode, rs_dest_src1.GetReg(), rs_src2.GetReg());
 }
 
-void X86Mir2Lir::GenShiftByteVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenShiftByteVector(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) {
+void X86Mir2Lir::GenShiftLeftVector(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) {
@@ -2034,7 +1925,7 @@
       break;
     case kSignedByte:
     case kUnsignedByte:
-      GenShiftByteVector(bb, mir);
+      GenShiftByteVector(mir);
       return;
     default:
       LOG(FATAL) << "Unsupported vector shift left " << opsize;
@@ -2043,10 +1934,11 @@
   NewLIR2(opcode, rs_dest_src1.GetReg(), imm);
 }
 
-void X86Mir2Lir::GenSignedShiftRightVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenSignedShiftRightVector(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) {
@@ -2059,19 +1951,22 @@
       break;
     case kSignedByte:
     case kUnsignedByte:
-      GenShiftByteVector(bb, mir);
+      GenShiftByteVector(mir);
       return;
+    case k64:
+      // TODO Implement emulated shift algorithm.
     default:
       LOG(FATAL) << "Unsupported vector signed shift right " << opsize;
-      break;
+      UNREACHABLE();
   }
   NewLIR2(opcode, rs_dest_src1.GetReg(), imm);
 }
 
-void X86Mir2Lir::GenUnsignedShiftRightVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenUnsignedShiftRightVector(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) {
@@ -2087,7 +1982,7 @@
       break;
     case kSignedByte:
     case kUnsignedByte:
-      GenShiftByteVector(bb, mir);
+      GenShiftByteVector(mir);
       return;
     default:
       LOG(FATAL) << "Unsupported vector unsigned shift right " << opsize;
@@ -2096,26 +1991,29 @@
   NewLIR2(opcode, rs_dest_src1.GetReg(), imm);
 }
 
-void X86Mir2Lir::GenAndVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenAndVector(MIR* mir) {
   // 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());
 }
 
-void X86Mir2Lir::GenOrVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenOrVector(MIR* mir) {
   // 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());
 }
 
-void X86Mir2Lir::GenXorVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenXorVector(MIR* mir) {
   // 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());
 }
@@ -2138,233 +2036,357 @@
   AppendOpcodeWithConst(opcode, rs_src1.GetReg(), const_mirp);
 }
 
-void X86Mir2Lir::GenAddReduceVector(BasicBlock *bb, MIR *mir) {
+void X86Mir2Lir::GenAddReduceVector(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, mir->optimization_flags);
+  } 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);
+    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) {
+void X86Mir2Lir::GenReduceVector(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;
 
+  // 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_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);
+
+    // 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;
+        UNREACHABLE();
+    }
+
+    if (rl_result.location == kLocPhysReg) {
+      NewLIR3(extr_opcode, rl_result.reg.GetReg(), vector_src.GetReg(), extract_index);
+      StoreFinalValue(rl_dest, rl_result);
+    } else {
+      int displacement = SRegOffset(rl_result.s_reg_low);
+      LIR *l = NewLIR3(extr_opcode, rs_rX86_SP_32.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 {
+    NewLIR2(op_mov, rs_dest.GetReg(), rs_src.GetReg());
+  }
+}
+
+void X86Mir2Lir::GenSetVector(MIR* mir) {
+  DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
+  OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
+  RegStorage rs_dest = RegStorage::Solo128(mir->dalvikInsn.vA);
+  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:
-      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;
-  }
-
-  if (rl_result.location == kLocPhysReg) {
-    NewLIR3(extr_opcode, rl_result.reg.GetReg(), rs_src1.GetReg(), extract_index);
-    if (is_wide == true) {
-      StoreFinalValue(rl_dest, rl_result);
-    } else {
-      StoreFinalValueWide(rl_dest, rl_result);
-    }
-  } 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 */);
-  }
-}
-
-void X86Mir2Lir::GenSetVector(BasicBlock *bb, MIR *mir) {
-  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;
-  RegisterClass reg_type = kCoreReg;
-
-  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) {
+  UNUSED(bb, mir);
+  UNIMPLEMENTED(FATAL) << "Extended opcode kMirOpPackedArrayGet not supported.";
+}
+
+void X86Mir2Lir::GenPackedArrayPut(BasicBlock* bb, MIR* mir) {
+  UNUSED(bb, 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,27 +2451,23 @@
   }
 
   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);
 }
 
-RegStorage X86Mir2Lir::GetCoreArgMappingToPhysicalReg(int core_arg_num) {
+RegStorage X86Mir2Lir::GetCoreArgMappingToPhysicalReg(int core_arg_num) const {
   // For the 32-bit internal ABI, the first 3 arguments are passed in registers.
   // Not used for 64-bit, TODO: Move X86_32 to the same framework
   switch (core_arg_num) {
-    case 0:
-      return rs_rX86_ARG1;
-    case 1:
-      return rs_rX86_ARG2;
-    case 2:
-      return rs_rX86_ARG3;
-    default:
-      return RegStorage::InvalidReg();
+    case 0: return TargetReg32(kArg1);
+    case 1: return TargetReg32(kArg2);
+    case 2: return TargetReg32(kArg3);
+    default: return RegStorage::InvalidReg();
   }
 }
 
@@ -2479,14 +2497,15 @@
   StoreValue(rl_method, rl_src);
   // If Method* has been promoted, explicitly flush
   if (rl_method.location == kLocPhysReg) {
-    StoreRefDisp(rs_rX86_SP, 0, As32BitReg(TargetReg(kArg0, kRef)), kNotVolatile);
+    const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
+    StoreRefDisp(rs_rSP, 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 +2519,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);
 
@@ -2516,9 +2535,9 @@
       } else {
         // Needs flush.
         if (t_loc->ref) {
-          StoreRefDisp(rs_rX86_SP, SRegOffset(start_vreg + i), reg, kNotVolatile);
+          StoreRefDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), reg, kNotVolatile);
         } else {
-          StoreBaseDisp(rs_rX86_SP, SRegOffset(start_vreg + i), reg, t_loc->wide ? k64 : k32,
+          StoreBaseDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), reg, t_loc->wide ? k64 : k32,
                         kNotVolatile);
         }
       }
@@ -2526,9 +2545,9 @@
       // If arriving in frame & promoted.
       if (t_loc->location == kLocPhysReg) {
         if (t_loc->ref) {
-          LoadRefDisp(rs_rX86_SP, SRegOffset(start_vreg + i), t_loc->reg, kNotVolatile);
+          LoadRefDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), t_loc->reg, kNotVolatile);
         } else {
-          LoadBaseDisp(rs_rX86_SP, SRegOffset(start_vreg + i), t_loc->reg,
+          LoadBaseDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), t_loc->reg,
                        t_loc->wide ? k64 : k32, kNotVolatile);
         }
       }
@@ -2554,16 +2573,16 @@
                                   uintptr_t direct_method, InvokeType type, bool skip_this) {
   if (!cu_->target64) {
     return Mir2Lir::GenDalvikArgsNoRange(info,
-                                  call_state, pcrLabel, next_call_insn,
-                                  target_method,
-                                  vtable_idx, direct_code,
-                                  direct_method, type, skip_this);
+                                         call_state, pcrLabel, next_call_insn,
+                                         target_method,
+                                         vtable_idx, direct_code,
+                                         direct_method, type, skip_this);
   }
   return GenDalvikArgsRange(info,
-                       call_state, pcrLabel, next_call_insn,
-                       target_method,
-                       vtable_idx, direct_code,
-                       direct_method, type, skip_this);
+                            call_state, pcrLabel, next_call_insn,
+                            target_method,
+                            vtable_idx, direct_code,
+                            direct_method, type, skip_this);
 }
 
 /*
@@ -2619,22 +2638,19 @@
         loc = UpdateLocWide(loc);
         if (loc.location == kLocPhysReg) {
           ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          StoreBaseDisp(rs_rX86_SP, SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
+          StoreBaseDisp(rs_rX86_SP_64, SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
         }
         next_arg += 2;
       } else {
         loc = UpdateLoc(loc);
         if (loc.location == kLocPhysReg) {
           ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          StoreBaseDisp(rs_rX86_SP, SRegOffset(loc.s_reg_low), loc.reg, k32, kNotVolatile);
+          StoreBaseDisp(rs_rX86_SP_64, SRegOffset(loc.s_reg_low), loc.reg, k32, kNotVolatile);
         }
         next_arg++;
       }
     }
 
-    // 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,
@@ -2682,25 +2698,25 @@
         bool src_is_8b_aligned = (current_src_offset & 0x7) == 0;
         bool dest_is_8b_aligned = (current_dest_offset & 0x7) == 0;
 
-        ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+        ScopedMemRefType mem_ref_type2(this, ResourceMask::kDalvikReg);
         if (src_is_16b_aligned) {
-          ld1 = OpMovRegMem(temp, rs_rX86_SP, current_src_offset, kMovA128FP);
+          ld1 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset, kMovA128FP);
         } else if (src_is_8b_aligned) {
-          ld1 = OpMovRegMem(temp, rs_rX86_SP, current_src_offset, kMovLo128FP);
-          ld2 = OpMovRegMem(temp, rs_rX86_SP, current_src_offset + (bytes_to_move >> 1),
+          ld1 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset, kMovLo128FP);
+          ld2 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset + (bytes_to_move >> 1),
                             kMovHi128FP);
         } else {
-          ld1 = OpMovRegMem(temp, rs_rX86_SP, current_src_offset, kMovU128FP);
+          ld1 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset, kMovU128FP);
         }
 
         if (dest_is_16b_aligned) {
-          st1 = OpMovMemReg(rs_rX86_SP, current_dest_offset, temp, kMovA128FP);
+          st1 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset, temp, kMovA128FP);
         } else if (dest_is_8b_aligned) {
-          st1 = OpMovMemReg(rs_rX86_SP, current_dest_offset, temp, kMovLo128FP);
-          st2 = OpMovMemReg(rs_rX86_SP, current_dest_offset + (bytes_to_move >> 1),
+          st1 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset, temp, kMovLo128FP);
+          st2 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset + (bytes_to_move >> 1),
                             temp, kMovHi128FP);
         } else {
-          st1 = OpMovMemReg(rs_rX86_SP, current_dest_offset, temp, kMovU128FP);
+          st1 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset, temp, kMovU128FP);
         }
 
         // TODO If we could keep track of aliasing information for memory accesses that are wider
@@ -2737,8 +2753,8 @@
         RegStorage temp = TargetReg(kArg3, kNotWide);
 
         // Now load the argument VR and store to the outs.
-        Load32Disp(rs_rX86_SP, current_src_offset, temp);
-        Store32Disp(rs_rX86_SP, current_dest_offset, temp);
+        Load32Disp(rs_rX86_SP_64, current_src_offset, temp);
+        Store32Disp(rs_rX86_SP_64, current_dest_offset, temp);
       }
 
       current_src_offset += bytes_to_move;
@@ -2764,17 +2780,17 @@
           ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
           if (rl_arg.wide) {
             if (rl_arg.location == kLocPhysReg) {
-              StoreBaseDisp(rs_rX86_SP, out_offset, rl_arg.reg, k64, kNotVolatile);
+              StoreBaseDisp(rs_rX86_SP_64, out_offset, rl_arg.reg, k64, kNotVolatile);
             } else {
               LoadValueDirectWideFixed(rl_arg, regWide);
-              StoreBaseDisp(rs_rX86_SP, out_offset, regWide, k64, kNotVolatile);
+              StoreBaseDisp(rs_rX86_SP_64, out_offset, regWide, k64, kNotVolatile);
             }
           } else {
             if (rl_arg.location == kLocPhysReg) {
-              StoreBaseDisp(rs_rX86_SP, out_offset, rl_arg.reg, k32, kNotVolatile);
+              StoreBaseDisp(rs_rX86_SP_64, out_offset, rl_arg.reg, k32, kNotVolatile);
             } else {
               LoadValueDirectFixed(rl_arg, regSingle);
-              StoreBaseDisp(rs_rX86_SP, out_offset, regSingle, k32, kNotVolatile);
+              StoreBaseDisp(rs_rX86_SP_64, out_offset, regSingle, k32, kNotVolatile);
             }
           }
         }
@@ -2909,4 +2925,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..c1c79ca 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -54,14 +54,16 @@
 }
 
 bool X86Mir2Lir::InexpensiveConstantInt(int32_t value) {
+  UNUSED(value);
   return true;
 }
 
 bool X86Mir2Lir::InexpensiveConstantFloat(int32_t value) {
-  return false;
+  return value == 0;
 }
 
 bool X86Mir2Lir::InexpensiveConstantLong(int64_t value) {
+  UNUSED(value);
   return true;
 }
 
@@ -228,7 +230,7 @@
         // TODO: there are several instances of this check.  A utility function perhaps?
         // TODO: Similar to Arm's reg < 8 check.  Perhaps add attribute checks to RegStorage?
         // Use shifts instead of a byte operand if the source can't be byte accessed.
-        if (r_src2.GetRegNum() >= rs_rX86_SP.GetRegNum()) {
+        if (r_src2.GetRegNum() >= rs_rX86_SP_32.GetRegNum()) {
           NewLIR2(is64Bit ? kX86Mov64RR : kX86Mov32RR, r_dest_src1.GetReg(), r_src2.GetReg());
           NewLIR2(is64Bit ? kX86Sal64RI : kX86Sal32RI, r_dest_src1.GetReg(), is64Bit ? 56 : 24);
           return NewLIR2(is64Bit ? kX86Sar64RI : kX86Sar32RI, r_dest_src1.GetReg(),
@@ -383,7 +385,7 @@
   }
   LIR *l = NewLIR3(opcode, r_dest.GetReg(), r_base.GetReg(), offset);
   if (mem_ref_type_ == ResourceMask::kDalvikReg) {
-    DCHECK(r_base == rs_rX86_SP);
+    DCHECK_EQ(r_base, cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32);
     AnnotateDalvikRegAccess(l, offset >> 2, true /* is_load */, false /* is_64bit */);
   }
   return l;
@@ -409,7 +411,7 @@
       LOG(FATAL) << "Bad case in OpMemReg " << op;
       break;
   }
-  LIR *l = NewLIR3(opcode, rs_rX86_SP.GetReg(), displacement, r_value);
+  LIR *l = NewLIR3(opcode, rs_rX86_SP_32.GetReg(), displacement, r_value);
   if (mem_ref_type_ == ResourceMask::kDalvikReg) {
     AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is64Bit /* is_64bit */);
     AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is64Bit /* is_64bit */);
@@ -435,7 +437,7 @@
       LOG(FATAL) << "Bad case in OpRegMem " << op;
       break;
   }
-  LIR *l = NewLIR3(opcode, r_dest.GetReg(), rs_rX86_SP.GetReg(), displacement);
+  LIR *l = NewLIR3(opcode, r_dest.GetReg(), rs_rX86_SP_32.GetReg(), displacement);
   if (mem_ref_type_ == ResourceMask::kDalvikReg) {
     AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is64Bit /* is_64bit */);
   }
@@ -512,7 +514,7 @@
                      r_src.GetReg() /* index */, value /* scale */, 0 /* disp */);
     } else if (op == kOpAdd) {  // lea add special case
       return NewLIR5(r_dest.Is64Bit() ? kX86Lea64RA : kX86Lea32RA, r_dest.GetReg(),
-                     r_src.GetReg() /* base */, rs_rX86_SP.GetReg()/*r4sib_no_index*/ /* index */,
+                     r_src.GetReg() /* base */, rs_rX86_SP_32.GetReg()/*r4sib_no_index*/ /* index */,
                      0 /* scale */, value /* disp */);
     }
     OpRegCopy(r_dest, r_src);
@@ -592,7 +594,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()) {
@@ -658,7 +659,8 @@
         CHECK_EQ(is_array, false);
         CHECK_EQ(r_dest.IsFloat(), false);
         break;
-      }  // else fall-through to k32 case
+      }
+      FALLTHROUGH_INTENDED;  // else fall-through to k32 case
     case k32:
     case kSingle:
     case kReference:  // TODO: update for reference decompression on 64-bit targets.
@@ -703,7 +705,7 @@
       }
     }
     if (mem_ref_type_ == ResourceMask::kDalvikReg) {
-      DCHECK(r_base == rs_rX86_SP);
+      DCHECK_EQ(r_base, cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32);
       AnnotateDalvikRegAccess(load, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
                               true /* is_load */, is64bit);
       if (pair) {
@@ -779,15 +781,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;
+      FALLTHROUGH_INTENDED;
     case kDouble:
       if (r_src.IsFloat()) {
         opcode = is_array ? kX86MovsdAR : kX86MovsdMR;
@@ -804,8 +811,10 @@
         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
+      }
+      FALLTHROUGH_INTENDED;  // else fall-through to k32 case
     case k32:
     case kSingle:
     case kReference:
@@ -815,6 +824,7 @@
         DCHECK(r_src.IsSingle());
       }
       DCHECK_EQ((displacement & 0x3), 0);
+      consider_non_temporal = true;
       break;
     case kUnsignedHalf:
     case kSignedHalf:
@@ -829,6 +839,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());
@@ -838,7 +870,7 @@
       store2 = NewLIR3(opcode, r_base.GetReg(), displacement + HIWORD_OFFSET, r_src.GetHighReg());
     }
     if (mem_ref_type_ == ResourceMask::kDalvikReg) {
-      DCHECK(r_base == rs_rX86_SP);
+      DCHECK_EQ(r_base, cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32);
       AnnotateDalvikRegAccess(store, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
                               false /* is_load */, is64bit);
       if (pair) {
@@ -875,6 +907,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,18 +927,23 @@
     GenMemBarrier(kAnyAny);
   }
 
+  if (allocated_temp) {
+    FreeTemp(r_src);
+  }
+
   return store;
 }
 
 LIR* X86Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg,
                                    int offset, int check_value, LIR* target, LIR** compare) {
-    LIR* inst = NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base_reg.GetReg(),
-            offset, check_value);
-    if (compare != nullptr) {
-        *compare = inst;
-    }
-    LIR* branch = OpCondBranch(cond, target);
-    return branch;
+  UNUSED(temp_reg);  // Comparison performed directly with memory.
+  LIR* inst = NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base_reg.GetReg(),
+      offset, check_value);
+  if (compare != nullptr) {
+    *compare = inst;
+  }
+  LIR* branch = OpCondBranch(cond, target);
+  return branch;
 }
 
 void X86Mir2Lir::AnalyzeMIR() {
@@ -913,19 +961,20 @@
 
   // 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;
   }
 }
 
-void X86Mir2Lir::AnalyzeBB(BasicBlock * bb) {
+void X86Mir2Lir::AnalyzeBB(BasicBlock* bb) {
   if (bb->block_type == kDead) {
     // Ignore dead blocks
     return;
   }
 
-  for (MIR *mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+  for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
     int opcode = mir->dalvikInsn.opcode;
     if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
       AnalyzeExtendedMIR(opcode, bb, mir);
@@ -936,7 +985,7 @@
 }
 
 
-void X86Mir2Lir::AnalyzeExtendedMIR(int opcode, BasicBlock * bb, MIR *mir) {
+void X86Mir2Lir::AnalyzeExtendedMIR(int opcode, BasicBlock* bb, MIR* mir) {
   switch (opcode) {
     // Instructions referencing doubles.
     case kMirOpFusedCmplDouble:
@@ -946,13 +995,24 @@
     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;
   }
 }
 
-void X86Mir2Lir::AnalyzeMIR(int opcode, BasicBlock * bb, MIR *mir) {
+void X86Mir2Lir::AnalyzeMIR(int opcode, BasicBlock* bb, MIR* mir) {
   // Looking for
   // - Do we need a pointer to the code (used for packed switches and double lits)?
 
@@ -980,6 +1040,7 @@
       store_method_addr_ = true;
       break;
     case Instruction::INVOKE_STATIC:
+    case Instruction::INVOKE_STATIC_RANGE:
       AnalyzeInvokeStatic(opcode, bb, mir);
       break;
     default:
@@ -988,7 +1049,8 @@
   }
 }
 
-void X86Mir2Lir::AnalyzeFPInstruction(int opcode, BasicBlock * bb, MIR *mir) {
+void X86Mir2Lir::AnalyzeFPInstruction(int opcode, BasicBlock* bb, MIR* mir) {
+  UNUSED(bb);
   // Look at all the uses, and see if they are double constants.
   uint64_t attrs = MIRGraph::GetDataFlowAttributes(static_cast<Instruction::Code>(opcode));
   int next_sreg = 0;
@@ -1022,7 +1084,7 @@
   }
 }
 
-RegLocation X86Mir2Lir::UpdateLocTyped(RegLocation loc, int reg_class) {
+RegLocation X86Mir2Lir::UpdateLocTyped(RegLocation loc) {
   loc = UpdateLoc(loc);
   if ((loc.location == kLocPhysReg) && (loc.fp != loc.reg.IsFloat())) {
     if (GetRegInfo(loc.reg)->IsTemp()) {
@@ -1036,7 +1098,7 @@
   return loc;
 }
 
-RegLocation X86Mir2Lir::UpdateLocWideTyped(RegLocation loc, int reg_class) {
+RegLocation X86Mir2Lir::UpdateLocWideTyped(RegLocation loc) {
   loc = UpdateLocWide(loc);
   if ((loc.location == kLocPhysReg) && (loc.fp != loc.reg.IsFloat())) {
     if (GetRegInfo(loc.reg)->IsTemp()) {
@@ -1050,32 +1112,32 @@
   return loc;
 }
 
-void X86Mir2Lir::AnalyzeInvokeStatic(int opcode, BasicBlock * bb, MIR *mir) {
+void X86Mir2Lir::AnalyzeInvokeStatic(int opcode, BasicBlock* bb, MIR* mir) {
+  UNUSED(opcode, bb);
   // For now this is only actual for x86-32.
   if (cu_->target64) {
     return;
   }
 
   uint32_t index = mir->dalvikInsn.vB;
-  if (!(mir->optimization_flags & MIR_INLINED)) {
-    DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
-    DexFileMethodInliner* method_inliner =
-      cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file);
-    InlineMethod method;
-    if (method_inliner->IsIntrinsic(index, &method)) {
-      switch (method.opcode) {
-        case kIntrinsicAbsDouble:
-        case kIntrinsicMinMaxDouble:
-          store_method_addr_ = true;
-          break;
-        default:
-          break;
-      }
+  DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
+  DexFileMethodInliner* method_inliner =
+    cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file);
+  InlineMethod method;
+  if (method_inliner->IsIntrinsic(index, &method)) {
+    switch (method.opcode) {
+      case kIntrinsicAbsDouble:
+      case kIntrinsicMinMaxDouble:
+        store_method_addr_ = true;
+        break;
+      default:
+        break;
     }
   }
 }
 
 LIR* X86Mir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) {
+  UNUSED(r_tgt);  // Call to absolute memory location doesn't need a temporary target register.
   if (cu_->target64) {
     return OpThreadMem(op, GetThreadOffset<8>(trampoline));
   } else {
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 500c6b8..76a67c4 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 {
@@ -231,7 +234,7 @@
 constexpr RegStorage rs_rBX = rs_r3;
 constexpr RegStorage rs_rX86_SP_64(RegStorage::kValid | r4sp_64);
 constexpr RegStorage rs_rX86_SP_32(RegStorage::kValid | r4sp_32);
-extern RegStorage rs_rX86_SP;
+static_assert(rs_rX86_SP_64.GetRegNum() == rs_rX86_SP_32.GetRegNum(), "Unexpected mismatch");
 constexpr RegStorage rs_r5(RegStorage::kValid | r5);
 constexpr RegStorage rs_r5q(RegStorage::kValid | r5q);
 constexpr RegStorage rs_rBP = rs_r5;
@@ -310,43 +313,8 @@
 constexpr RegStorage rs_xr14(RegStorage::kValid | xr14);
 constexpr RegStorage rs_xr15(RegStorage::kValid | xr15);
 
-extern X86NativeRegisterPool rX86_ARG0;
-extern X86NativeRegisterPool rX86_ARG1;
-extern X86NativeRegisterPool rX86_ARG2;
-extern X86NativeRegisterPool rX86_ARG3;
-extern X86NativeRegisterPool rX86_ARG4;
-extern X86NativeRegisterPool rX86_ARG5;
-extern X86NativeRegisterPool rX86_FARG0;
-extern X86NativeRegisterPool rX86_FARG1;
-extern X86NativeRegisterPool rX86_FARG2;
-extern X86NativeRegisterPool rX86_FARG3;
-extern X86NativeRegisterPool rX86_FARG4;
-extern X86NativeRegisterPool rX86_FARG5;
-extern X86NativeRegisterPool rX86_FARG6;
-extern X86NativeRegisterPool rX86_FARG7;
-extern X86NativeRegisterPool rX86_RET0;
-extern X86NativeRegisterPool rX86_RET1;
-extern X86NativeRegisterPool rX86_INVOKE_TGT;
-extern X86NativeRegisterPool rX86_COUNT;
-
-extern RegStorage rs_rX86_ARG0;
-extern RegStorage rs_rX86_ARG1;
-extern RegStorage rs_rX86_ARG2;
-extern RegStorage rs_rX86_ARG3;
-extern RegStorage rs_rX86_ARG4;
-extern RegStorage rs_rX86_ARG5;
-extern RegStorage rs_rX86_FARG0;
-extern RegStorage rs_rX86_FARG1;
-extern RegStorage rs_rX86_FARG2;
-extern RegStorage rs_rX86_FARG3;
-extern RegStorage rs_rX86_FARG4;
-extern RegStorage rs_rX86_FARG5;
-extern RegStorage rs_rX86_FARG6;
-extern RegStorage rs_rX86_FARG7;
-extern RegStorage rs_rX86_RET0;
-extern RegStorage rs_rX86_RET1;
-extern RegStorage rs_rX86_INVOKE_TGT;
-extern RegStorage rs_rX86_COUNT;
+constexpr RegStorage rs_rX86_RET0 = rs_rAX;
+constexpr RegStorage rs_rX86_RET1 = rs_rDX;
 
 // RegisterLocation templates return values (r_V0, or r_V0/r_V1).
 const RegLocation x86_loc_c_return
@@ -440,12 +408,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 +452,10 @@
 #undef BinaryShiftOpcode
   kX86Cmc,
   kX86Shld32RRI,
+  kX86Shld32RRC,
   kX86Shld32MRI,
   kX86Shrd32RRI,
+  kX86Shrd32RRC,
   kX86Shrd32MRI,
   kX86Shld64RRI,
   kX86Shld64MRI,
@@ -550,20 +520,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 +565,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 +580,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 +596,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
@@ -656,6 +639,7 @@
   kX86RepneScasw,       // repne scasw
   kX86Last
 };
+std::ostream& operator<<(std::ostream& os, const X86OpCode& rhs);
 
 /* Instruction assembly field_loc kind */
 enum X86EncodingKind {
@@ -675,6 +659,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/reg_storage.h b/compiler/dex/reg_storage.h
index 706933a..46ed011 100644
--- a/compiler/dex/reg_storage.h
+++ b/compiler/dex/reg_storage.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_DEX_REG_STORAGE_H_
 
 #include "base/logging.h"
+#include "base/value_object.h"
 #include "compiler_enums.h"  // For WideKind
 
 namespace art {
@@ -72,7 +73,7 @@
  * records.
  */
 
-class RegStorage {
+class RegStorage : public ValueObject {
  public:
   enum RegStorageKind {
     kValidMask     = 0x8000,
@@ -112,7 +113,7 @@
   }
   constexpr RegStorage(RegStorageKind rs_kind, int low_reg, int high_reg)
       : reg_(
-          DCHECK_CONSTEXPR(rs_kind == k64BitPair, << rs_kind, 0u)
+          DCHECK_CONSTEXPR(rs_kind == k64BitPair, << static_cast<int>(rs_kind), 0u)
           DCHECK_CONSTEXPR((low_reg & kFloatingPoint) == (high_reg & kFloatingPoint),
                            << low_reg << ", " << high_reg, 0u)
           DCHECK_CONSTEXPR((high_reg & kRegNumMask) <= kHighRegNumMask,
@@ -331,14 +332,16 @@
       case k256BitSolo: return 32;
       case k512BitSolo: return 64;
       case k1024BitSolo: return 128;
-      default: LOG(FATAL) << "Unexpected shape";
+      default: LOG(FATAL) << "Unexpected shape"; UNREACHABLE();
     }
-    return 0;
   }
 
  private:
   uint16_t reg_;
 };
+static inline std::ostream& operator<<(std::ostream& o, const RegStorage& rhs) {
+  return o << rhs.GetRawBits();  // TODO: better output.
+}
 
 }  // namespace art
 
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index e26745a..d3d76ba 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,13 +61,14 @@
   block->visited = true;
   /* Enqueue the pre_order block id */
   if (block->id != NullBasicBlockId) {
-    dfs_order_->Insert(block->id);
+    dfs_order_.push_back(block->id);
   }
 }
 
 void MIRGraph::RecordDFSOrders(BasicBlock* block) {
-  DCHECK(temp_scoped_alloc_.get() != nullptr);
-  ScopedArenaVector<BasicBlock*> succ(temp_scoped_alloc_->Adapter());
+  ScopedArenaAllocator allocator(&cu_->arena_stack);
+  ScopedArenaVector<BasicBlock*> succ(allocator.Adapter());
+  succ.reserve(GetNumBlocks());
   MarkPreOrder(block);
   succ.push_back(block);
   while (!succ.empty()) {
@@ -82,9 +79,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 +89,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,17 +101,18 @@
   // 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.
     AllNodesIterator iter(this);
     for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
       if (!bb->visited) {
-        bb->Hide(cu_);
+        bb->Hide(this);
       }
     }
   }
+  dfs_orders_up_to_date_ = true;
 }
 
 /*
@@ -140,24 +126,27 @@
 
   for (uint32_t idx : bb->data_flow_info->def_v->Indexes()) {
     /* Block bb defines register idx */
-    def_block_matrix_[idx]->SetBit(bb->id);
+    temp_bit_matrix_[idx]->SetBit(bb->id);
   }
   return true;
 }
 
 void MIRGraph::ComputeDefBlockMatrix() {
-  int num_registers = cu_->num_dalvik_registers;
-  /* Allocate num_dalvik_registers bit vector pointers */
-  def_block_matrix_ = static_cast<ArenaBitVector**>
-      (arena_->Alloc(sizeof(ArenaBitVector *) * num_registers,
-                     kArenaAllocDFInfo));
+  int num_registers = GetNumOfCodeAndTempVRs();
+  /* Allocate num_registers bit vector pointers */
+  DCHECK(temp_scoped_alloc_ != nullptr);
+  DCHECK(temp_bit_matrix_ == nullptr);
+  temp_bit_matrix_ = static_cast<ArenaBitVector**>(
+      temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * num_registers, kArenaAllocDFInfo));
   int i;
 
   /* Initialize num_register vectors with num_blocks bits each */
   for (i = 0; i < num_registers; i++) {
-    def_block_matrix_[i] =
-        new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapBMatrix);
+    temp_bit_matrix_[i] = new (temp_scoped_alloc_.get()) ArenaBitVector(arena_, GetNumBlocks(),
+                                                                        false, kBitMapBMatrix);
+    temp_bit_matrix_[i]->ClearAllBits();
   }
+
   AllNodesIterator iter(this);
   for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
     FindLocalLiveIn(bb);
@@ -171,22 +160,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);
+    temp_bit_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 +194,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 +230,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 +254,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 +297,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 +312,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 +347,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 +435,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 +451,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,8 +481,8 @@
   }
 
   /* Iterate through each Dalvik register */
-  for (dalvik_reg = cu_->num_dalvik_registers - 1; dalvik_reg >= 0; dalvik_reg--) {
-    input_blocks->Copy(def_block_matrix_[dalvik_reg]);
+  for (dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
+    input_blocks->Copy(temp_bit_matrix_[dalvik_reg]);
     phi_blocks->ClearAllBits();
     do {
       // TUNING: When we repeat this, we could skip indexes from the previous pass.
@@ -554,8 +528,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 +536,11 @@
                                                  kArenaAllocDFInfo));
     mir->meta.phi_incoming = incoming;
     int idx = 0;
-    while (true) {
-      BasicBlock* pred_bb = GetBasicBlock(iter.Next());
-      if (!pred_bb) {
-       break;
-      }
-      int ssa_reg = pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg];
-      uses[idx] = ssa_reg;
-      incoming[idx] = pred_bb->id;
+    for (BasicBlockId pred_id : bb->predecessors) {
+      BasicBlock* pred_bb = GetBasicBlock(pred_id);
+      DCHECK(pred_bb != nullptr);
+      uses[idx] = pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg];
+      incoming[idx] = pred_id;
       idx++;
     }
   }
@@ -586,7 +556,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 +575,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/verification_results.cc b/compiler/dex/verification_results.cc
index a8e6b3c..4929b5b 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -106,6 +106,8 @@
   if (use_sea) {
     return true;
   }
+#else
+  UNUSED(method_ref);
 #endif
   if (!compiler_options_->IsCompilationEnabled()) {
     return false;
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..f6c7d52 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);
@@ -378,7 +378,20 @@
         changed |= SetWide(defs[1]);
         changed |= SetHigh(defs[1]);
       }
+
+      bool has_ins = (GetNumOfInVRs() > 0);
+
       for (int i = 0; i < ssa_rep->num_uses; i++) {
+        if (has_ins && IsInVReg(uses[i])) {
+          // NB: The SSA name for the first def of an in-reg will be the same as
+          // the reg's actual name.
+          if (!reg_location_[uses[i]].fp && defined_fp) {
+            // If we were about to infer that this first def of an in-reg is a float
+            // when it wasn't previously (because float/int is set during SSA initialization),
+            // do not allow this to happen.
+            continue;
+          }
+        }
         changed |= SetFp(uses[i], defined_fp);
         changed |= SetCore(uses[i], defined_core);
         changed |= SetRef(uses[i], defined_ref);
@@ -401,33 +414,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 +434,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 +447,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 +513,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 d278884..1805d59 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"
@@ -92,6 +93,10 @@
   return field->IsVolatile();
 }
 
+inline MemberOffset CompilerDriver::GetFieldOffset(mirror::ArtField* field) {
+  return field->GetOffset();
+}
+
 inline std::pair<bool, bool> CompilerDriver::IsFastInstanceField(
     mirror::DexCache* dex_cache, mirror::Class* referrer_class,
     mirror::ArtField* resolved_field, uint16_t field_idx) {
@@ -106,16 +111,12 @@
 
 inline std::pair<bool, bool> CompilerDriver::IsFastStaticField(
     mirror::DexCache* dex_cache, mirror::Class* referrer_class,
-    mirror::ArtField* resolved_field, uint16_t field_idx, MemberOffset* field_offset,
-    uint32_t* storage_index, bool* is_referrers_class, bool* is_initialized) {
+    mirror::ArtField* resolved_field, uint16_t field_idx, uint32_t* storage_index) {
   DCHECK(resolved_field->IsStatic());
   if (LIKELY(referrer_class != nullptr)) {
     mirror::Class* fields_class = resolved_field->GetDeclaringClass();
     if (fields_class == referrer_class) {
-      *field_offset = resolved_field->GetOffset();
       *storage_index = fields_class->GetDexTypeIndex();
-      *is_referrers_class = true;  // implies no worrying about class initialization
-      *is_initialized = true;
       return std::make_pair(true, true);
     }
     if (referrer_class->CanAccessResolvedField(fields_class, resolved_field,
@@ -147,23 +148,30 @@
         }
       }
       if (storage_idx != DexFile::kDexNoIndex) {
-        *field_offset = resolved_field->GetOffset();
         *storage_index = storage_idx;
-        *is_referrers_class = false;
-        *is_initialized = fields_class->IsInitialized() &&
-            CanAssumeTypeIsPresentInDexCache(*dex_file, storage_idx);
         return std::make_pair(true, !resolved_field->IsFinal());
       }
     }
   }
   // Conservative defaults.
-  *field_offset = MemberOffset(0u);
   *storage_index = DexFile::kDexNoIndex;
-  *is_referrers_class = false;
-  *is_initialized = false;
   return std::make_pair(false, false);
 }
 
+inline bool CompilerDriver::IsStaticFieldInReferrerClass(mirror::Class* referrer_class,
+                                                         mirror::ArtField* resolved_field) {
+  DCHECK(resolved_field->IsStatic());
+  mirror::Class* fields_class = resolved_field->GetDeclaringClass();
+  return referrer_class == fields_class;
+}
+
+inline bool CompilerDriver::IsStaticFieldsClassInitialized(mirror::Class* referrer_class,
+                                                           mirror::ArtField* resolved_field) {
+  DCHECK(resolved_field->IsStatic());
+  mirror::Class* fields_class = resolved_field->GetDeclaringClass();
+  return fields_class == referrer_class || fields_class->IsInitialized();
+}
+
 inline mirror::ArtMethod* CompilerDriver::ResolveMethod(
     ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
@@ -311,14 +319,13 @@
   return stats_flags;
 }
 
-inline bool CompilerDriver::NeedsClassInitialization(mirror::Class* referrer_class,
-                                                     mirror::ArtMethod* resolved_method) {
+inline bool CompilerDriver::IsMethodsClassInitialized(mirror::Class* referrer_class,
+                                                      mirror::ArtMethod* resolved_method) {
   if (!resolved_method->IsStatic()) {
-    return false;
+    return true;
   }
   mirror::Class* methods_class = resolved_method->GetDeclaringClass();
-  // NOTE: Unlike in IsFastStaticField(), we don't check CanAssumeTypeIsPresentInDexCache() here.
-  return methods_class != referrer_class && !methods_class->IsInitialized();
+  return methods_class == referrer_class || methods_class->IsInitialized();
 }
 
 }  // namespace art
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 9a44ade..aab94c0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -329,10 +329,10 @@
                                DexFileToMethodInlinerMap* method_inliner_map,
                                Compiler::Kind compiler_kind,
                                InstructionSet instruction_set,
-                               InstructionSetFeatures instruction_set_features,
+                               const InstructionSetFeatures* instruction_set_features,
                                bool image, std::set<std::string>* image_classes, size_t thread_count,
                                bool dump_stats, bool dump_passes, CumulativeLogger* timer,
-                               std::string profile_file)
+                               const std::string& profile_file)
     : profile_present_(false), compiler_options_(compiler_options),
       verification_results_(verification_results),
       method_inliner_map_(method_inliner_map),
@@ -342,21 +342,19 @@
       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),
-      start_ns_(0),
       stats_(new AOTCompilationStats),
       dump_stats_(dump_stats),
       dump_passes_(dump_passes),
       timings_logger_(timer),
-      compiler_library_(nullptr),
       compiler_context_(nullptr),
-      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 +376,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 +391,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 +424,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 +432,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 +613,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();
@@ -638,7 +623,7 @@
   if (code_item->tries_size_ == 0) {
     return;  // nothing to process
   }
-  const byte* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0);
+  const uint8_t* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0);
   size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list);
   for (size_t i = 0; i < num_encoded_catch_handlers; i++) {
     int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list);
@@ -671,7 +656,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);
@@ -733,9 +718,9 @@
     for (const std::pair<uint16_t, const DexFile*>& exception_type : unresolved_exception_types) {
       uint16_t exception_type_idx = exception_type.first;
       const DexFile* dex_file = exception_type.second;
-      StackHandleScope<2> hs(self);
-      Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(*dex_file)));
-      Handle<mirror::Class> klass(hs.NewHandle(
+      StackHandleScope<2> hs2(self);
+      Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->FindDexCache(*dex_file)));
+      Handle<mirror::Class> klass(hs2.NewHandle(
           class_linker->ResolveType(*dex_file, exception_type_idx, dex_cache,
                                     NullHandle<mirror::ClassLoader>())));
       if (klass.Get() == nullptr) {
@@ -762,7 +747,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);
@@ -772,13 +757,13 @@
     }
     VLOG(compiler) << "Adding " << descriptor << " to image classes";
     for (size_t i = 0; i < klass->NumDirectInterfaces(); ++i) {
-      StackHandleScope<1> hs(self);
-      MaybeAddToImageClasses(hs.NewHandle(mirror::Class::GetDirectInterface(self, klass, i)),
+      StackHandleScope<1> hs2(self);
+      MaybeAddToImageClasses(hs2.NewHandle(mirror::Class::GetDirectInterface(self, klass, i)),
                              image_classes);
     }
     if (klass->IsArrayClass()) {
-      StackHandleScope<1> hs(self);
-      MaybeAddToImageClasses(hs.NewHandle(klass->GetComponentType()), image_classes);
+      StackHandleScope<1> hs2(self);
+      MaybeAddToImageClasses(hs2.NewHandle(klass->GetComponentType()), image_classes);
     }
     klass.Assign(klass->GetSuperClass());
   }
@@ -796,14 +781,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);
   }
 }
 
@@ -976,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();
@@ -1052,7 +1071,8 @@
 bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
                                             bool is_put, MemberOffset* field_offset,
                                             uint32_t* storage_index, bool* is_referrers_class,
-                                            bool* is_volatile, bool* is_initialized) {
+                                            bool* is_volatile, bool* is_initialized,
+                                            Primitive::Type* type) {
   ScopedObjectAccess soa(Thread::Current());
   // Try to resolve the field and compiling method's class.
   mirror::ArtField* resolved_field;
@@ -1075,17 +1095,25 @@
   if (resolved_field != nullptr && referrer_class != nullptr) {
     *is_volatile = IsFieldVolatile(resolved_field);
     std::pair<bool, bool> fast_path = IsFastStaticField(
-        dex_cache, referrer_class, resolved_field, field_idx, field_offset,
-        storage_index, is_referrers_class, is_initialized);
+        dex_cache, referrer_class, resolved_field, field_idx, storage_index);
     result = is_put ? fast_path.second : fast_path.first;
   }
-  if (!result) {
+  if (result) {
+    *field_offset = GetFieldOffset(resolved_field);
+    *is_referrers_class = IsStaticFieldInReferrerClass(referrer_class, resolved_field);
+    // *is_referrers_class == true implies no worrying about class initialization.
+    *is_initialized = (*is_referrers_class) ||
+        (IsStaticFieldsClassInitialized(referrer_class, resolved_field) &&
+         CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index));
+    *type = resolved_field->GetTypeAsPrimitiveType();
+  } else {
     // Conservative defaults.
     *is_volatile = true;
     *field_offset = MemberOffset(static_cast<size_t>(-1));
     *storage_index = -1;
     *is_referrers_class = false;
     *is_initialized = false;
+    *type = Primitive::kPrimVoid;
   }
   ProcessedStaticField(result, *is_referrers_class);
   return result;
@@ -1291,75 +1319,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);
@@ -1555,7 +1514,7 @@
   // Note the class_data pointer advances through the headers,
   // static fields, instance fields, direct methods, and virtual
   // methods.
-  const byte* class_data = dex_file.GetClassData(class_def);
+  const uint8_t* class_data = dex_file.GetClassData(class_def);
   if (class_data == nullptr) {
     // Empty class such as a marker interface.
     requires_constructor_barrier = false;
@@ -1703,15 +1662,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.
@@ -1800,7 +1759,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
@@ -1814,7 +1773,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).
@@ -1833,11 +1792,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) {
@@ -1850,7 +1811,6 @@
               transaction.Abort();
               CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
             }
-            soa.Self()->EndAssertNoThreadSuspension(old_casue);
           }
         }
         soa.Self()->AssertNoPendingException();
@@ -1931,7 +1891,7 @@
   if (manager->GetCompiler()->verification_results_->IsClassRejected(ref)) {
     return;
   }
-  const byte* class_data = dex_file.GetClassData(class_def);
+  const uint8_t* class_data = dex_file.GetClassData(class_def);
   if (class_data == nullptr) {
     // empty class, probably a marker interface
     return;
@@ -2000,6 +1960,19 @@
   context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_);
 }
 
+// Does the runtime for the InstructionSet provide an implementation returned by
+// GetQuickGenericJniStub allowing down calls that aren't compiled using a JNI compiler?
+static bool InstructionSetHasGenericJniStub(InstructionSet isa) {
+  switch (isa) {
+    case kArm:
+    case kArm64:
+    case kThumb2:
+    case kX86:
+    case kX86_64: return true;
+    default: return false;
+  }
+}
+
 void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
                                    InvokeType invoke_type, uint16_t class_def_idx,
                                    uint32_t method_idx, jobject class_loader,
@@ -2011,13 +1984,14 @@
   if ((access_flags & kAccNative) != 0) {
     // Are we interpreting only and have support for generic JNI down calls?
     if (!compiler_options_->IsCompilationEnabled() &&
-        (instruction_set_ == kX86_64 || instruction_set_ == kArm64)) {
+        InstructionSetHasGenericJniStub(instruction_set_)) {
       // Leaving this empty will trigger the generic JNI version
     } else {
       compiled_method = compiler_->JniCompile(access_flags, method_idx, dex_file);
       CHECK(compiled_method != nullptr);
     }
   } else if ((access_flags & kAccAbstract) != 0) {
+    // Abstract methods don't have code.
   } else {
     MethodReference method_ref(&dex_file, method_idx);
     bool compile = verification_results_->IsCandidateForCompilation(method_ref, access_flags);
@@ -2044,11 +2018,23 @@
 
   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;
+      }
+    }
+    bool compile_pic = GetCompilerOptions().GetCompilePic();  // Off by default
+    // When compiling with PIC, there should be zero non-relative linker patches
+    CHECK(!compile_pic || non_relative_linker_patch_count == 0u);
+
     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);
   }
@@ -2106,6 +2092,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 598e196..682b17a 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -75,20 +75,7 @@
   kRequired,              // Dex-to-dex compilation required for correctness.
   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_;
-};
+std::ostream& operator<<(std::ostream& os, const DexToDexCompilationLevel& rhs);
 
 class CompilerDriver {
  public:
@@ -102,10 +89,10 @@
                           DexFileToMethodInlinerMap* method_inliner_map,
                           Compiler::Kind compiler_kind,
                           InstructionSet instruction_set,
-                          InstructionSetFeatures instruction_set_features,
+                          const InstructionSetFeatures* instruction_set_features,
                           bool image, std::set<std::string>* image_classes,
                           size_t thread_count, bool dump_stats, bool dump_passes,
-                          CumulativeLogger* timer, std::string profile_file = "");
+                          CumulativeLogger* timer, const std::string& profile_file);
 
   ~CompilerDriver();
 
@@ -129,7 +116,7 @@
     return instruction_set_;
   }
 
-  InstructionSetFeatures GetInstructionSetFeatures() const {
+  const InstructionSetFeatures* GetInstructionSetFeatures() const {
     return instruction_set_features_;
   }
 
@@ -183,6 +170,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 +200,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_);
@@ -239,6 +234,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool IsFieldVolatile(mirror::ArtField* field) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  MemberOffset GetFieldOffset(mirror::ArtField* field) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset.
   std::pair<bool, bool> IsFastInstanceField(
@@ -246,13 +242,20 @@
       mirror::ArtField* resolved_field, uint16_t field_idx)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Can we fast-path an SGET/SPUT access to a static field? If yes, compute the field offset,
-  // the type index of the declaring class in the referrer's dex file and whether the declaring
-  // class is the referrer's class or at least can be assumed to be initialized.
+  // Can we fast-path an SGET/SPUT access to a static field? If yes, compute the type index
+  // of the declaring class in the referrer's dex file.
   std::pair<bool, bool> IsFastStaticField(
       mirror::DexCache* dex_cache, mirror::Class* referrer_class,
-      mirror::ArtField* resolved_field, uint16_t field_idx, MemberOffset* field_offset,
-      uint32_t* storage_index, bool* is_referrers_class, bool* is_initialized)
+      mirror::ArtField* resolved_field, uint16_t field_idx, uint32_t* storage_index)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Is static field's in referrer's class?
+  bool IsStaticFieldInReferrerClass(mirror::Class* referrer_class, mirror::ArtField* resolved_field)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Is static field's class initialized?
+  bool IsStaticFieldsClassInitialized(mirror::Class* referrer_class,
+                                      mirror::ArtField* resolved_field)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Resolve a method. Returns nullptr on failure, including incompatible class change.
@@ -268,7 +271,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_);
@@ -283,8 +286,10 @@
       uintptr_t* direct_code, uintptr_t* direct_method)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Does invokation of the resolved method need class initialization?
-  bool NeedsClassInitialization(mirror::Class* referrer_class, mirror::ArtMethod* resolved_method)
+  // Is method's class initialized for an invoke?
+  // For static invokes to determine whether we need to consider potential call to <clinit>().
+  // For non-static invokes, assuming a non-null reference, the class is always initialized.
+  bool IsMethodsClassInitialized(mirror::Class* referrer_class, mirror::ArtMethod* resolved_method)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void ProcessedInstanceField(bool resolved);
@@ -307,7 +312,8 @@
   // field is within the referrer (which can avoid checking class initialization).
   bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
                               MemberOffset* field_offset, uint32_t* storage_index,
-                              bool* is_referrers_class, bool* is_volatile, bool* is_initialized)
+                              bool* is_referrers_class, bool* is_volatile, bool* is_initialized,
+                              Primitive::Type* type)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
   // Can we fastpath a interface, super class or virtual method call? Computes method's vtable
@@ -321,42 +327,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 +363,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 +378,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_;
 
@@ -702,10 +481,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_;
@@ -713,7 +488,7 @@
   std::unique_ptr<Compiler> compiler_;
 
   const InstructionSet instruction_set_;
-  const InstructionSetFeatures instruction_set_features_;
+  const InstructionSetFeatures* const instruction_set_features_;
 
   // All class references that require
   mutable ReaderWriterMutex freezing_constructor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -728,6 +503,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_;
 
@@ -737,7 +515,6 @@
   std::unique_ptr<std::set<std::string>> image_classes_;
 
   size_t thread_count_;
-  uint64_t start_ns_;
 
   class AOTCompilationStats;
   std::unique_ptr<AOTCompilationStats> stats_;
@@ -750,8 +527,6 @@
   typedef void (*CompilerCallbackFn)(CompilerDriver& driver);
   typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver);
 
-  void* compiler_library_;
-
   typedef void (*DexToDexCompilerFn)(CompilerDriver& driver,
                                      const DexFile::CodeItem* code_item,
                                      uint32_t access_flags, InvokeType invoke_type,
@@ -767,28 +542,19 @@
   // Arena pool used by the compiler.
   ArenaPool arena_pool_;
 
-  typedef void (*CompilerEnableAutoElfLoadingFn)(CompilerDriver& driver);
-  CompilerEnableAutoElfLoadingFn compiler_enable_auto_elf_loading_;
-
-  typedef const void* (*CompilerGetMethodCodeAddrFn)
-      (const CompilerDriver& driver, const CompiledMethod* cm, const mirror::ArtMethod* method);
-  CompilerGetMethodCodeAddrFn compiler_get_method_code_addr_;
-
   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
@@ -796,12 +562,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;
         }
       }
@@ -813,11 +579,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..5a0ec2f 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -86,11 +86,11 @@
           hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)));
       mirror::Class* c = class_linker->FindClass(soa.Self(), descriptor, loader);
       CHECK(c != NULL);
-      for (size_t i = 0; i < c->NumDirectMethods(); i++) {
-        MakeExecutable(c->GetDirectMethod(i));
+      for (size_t j = 0; j < c->NumDirectMethods(); j++) {
+        MakeExecutable(c->GetDirectMethod(j));
       }
-      for (size_t i = 0; i < c->NumVirtualMethods(); i++) {
-        MakeExecutable(c->GetVirtualMethod(i));
+      for (size_t j = 0; j < c->NumVirtualMethods(); j++) {
+        MakeExecutable(c->GetVirtualMethod(j));
       }
     }
   }
@@ -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 03b9371..0592f0c 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -17,9 +17,15 @@
 #ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
 #define ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
 
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "globals.h"
+
 namespace art {
 
-class CompilerOptions {
+class CompilerOptions FINAL {
  public:
   enum CompilerFilter {
     kVerifyNone,          // Skip verification and compile nothing except JNI stubs.
@@ -27,7 +33,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.
@@ -59,11 +66,12 @@
     implicit_null_checks_(false),
     implicit_so_checks_(false),
     implicit_suspend_checks_(false),
-    compile_pic_(false)
+    compile_pic_(false),
 #ifdef ART_SEA_IR_MODE
-    , sea_ir_mode_(false)
+    sea_ir_mode_(false),
 #endif
-    {}
+    verbose_methods_(nullptr) {
+  }
 
   CompilerOptions(CompilerFilter compiler_filter,
                   size_t huge_method_threshold,
@@ -78,10 +86,11 @@
                   bool implicit_null_checks,
                   bool implicit_so_checks,
                   bool implicit_suspend_checks,
-                  bool compile_pic
+                  bool compile_pic,
 #ifdef ART_SEA_IR_MODE
-                  , bool sea_ir_mode
+                  bool sea_ir_mode,
 #endif
+                  const std::vector<std::string>* verbose_methods
                   ) :  // NOLINT(whitespace/parens)
     compiler_filter_(compiler_filter),
     huge_method_threshold_(huge_method_threshold),
@@ -96,11 +105,12 @@
     implicit_null_checks_(implicit_null_checks),
     implicit_so_checks_(implicit_so_checks),
     implicit_suspend_checks_(implicit_suspend_checks),
-    compile_pic_(compile_pic)
+    compile_pic_(compile_pic),
 #ifdef ART_SEA_IR_MODE
-    , sea_ir_mode_(sea_ir_mode)
+    sea_ir_mode_(sea_ir_mode),
 #endif
-    {}
+    verbose_methods_(verbose_methods) {
+  }
 
   CompilerFilter GetCompilerFilter() const {
     return compiler_filter_;
@@ -167,28 +177,18 @@
     return implicit_null_checks_;
   }
 
-  void SetImplicitNullChecks(bool new_val) {
-    implicit_null_checks_ = new_val;
-  }
-
   bool GetImplicitStackOverflowChecks() const {
     return implicit_so_checks_;
   }
 
-  void SetImplicitStackOverflowChecks(bool new_val) {
-    implicit_so_checks_ = new_val;
-  }
-
   bool GetImplicitSuspendChecks() const {
     return implicit_suspend_checks_;
   }
 
-  void SetImplicitSuspendChecks(bool new_val) {
-    implicit_suspend_checks_ = new_val;
-  }
-
 #ifdef ART_SEA_IR_MODE
-  bool GetSeaIrMode();
+  bool GetSeaIrMode() const {
+    return sea_ir_mode_;
+  }
 #endif
 
   bool GetGenerateGDBInformation() const {
@@ -204,26 +204,46 @@
     return compile_pic_;
   }
 
+  bool HasVerboseMethods() const {
+    return verbose_methods_ != nullptr && !verbose_methods_->empty();
+  }
+
+  bool IsVerboseMethod(const std::string& pretty_method) const {
+    for (const std::string& cur_method : *verbose_methods_) {
+      if (pretty_method.find(cur_method) != std::string::npos) {
+        return true;
+      }
+    }
+    return false;
+  }
+
  private:
   CompilerFilter compiler_filter_;
-  size_t huge_method_threshold_;
-  size_t large_method_threshold_;
-  size_t small_method_threshold_;
-  size_t tiny_method_threshold_;
-  size_t num_dex_methods_threshold_;
-  bool generate_gdb_information_;
-  bool include_patch_information_;
+  const size_t huge_method_threshold_;
+  const size_t large_method_threshold_;
+  const size_t small_method_threshold_;
+  const size_t tiny_method_threshold_;
+  const size_t num_dex_methods_threshold_;
+  const bool generate_gdb_information_;
+  const bool include_patch_information_;
   // When using a profile file only the top K% of the profiled samples will be compiled.
-  double top_k_profile_threshold_;
-  bool include_debug_symbols_;
-  bool implicit_null_checks_;
-  bool implicit_so_checks_;
-  bool implicit_suspend_checks_;
-  bool compile_pic_;
+  const double top_k_profile_threshold_;
+  const bool include_debug_symbols_;
+  const bool implicit_null_checks_;
+  const bool implicit_so_checks_;
+  const bool implicit_suspend_checks_;
+  const bool compile_pic_;
+
 #ifdef ART_SEA_IR_MODE
-  bool sea_ir_mode_;
+  const bool sea_ir_mode_;
 #endif
+
+  // Vector of methods to have verbose output enabled for.
+  const std::vector<std::string>* const verbose_methods_;
+
+  DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
 };
+std::ostream& operator<<(std::ostream& os, const CompilerOptions::CompilerFilter& rhs);
 
 }  // namespace art
 
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..7f30565
--- /dev/null
+++ b/compiler/elf_builder.h
@@ -0,0 +1,1243 @@
+/*
+ * 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 "base/value_object.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 ValueObject {
+ 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)
+      : section_index_(0), 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;
+  }
+
+  ~ElfSectionBuilder() {}
+
+  Elf_Word GetLink() const {
+    return (link_ != nullptr) ? link_->section_index_ : 0;
+  }
+
+  const Elf_Shdr* GetSection() const {
+    return &section_;
+  }
+
+  Elf_Shdr* GetSection() {
+    return &section_;
+  }
+
+  Elf_Word GetSectionIndex() const {
+    return section_index_;
+  }
+
+  void SetSectionIndex(Elf_Word section_index) {
+    section_index_ = section_index;
+  }
+
+  const std::string& GetName() const {
+    return name_;
+  }
+
+ private:
+  Elf_Shdr section_;
+  Elf_Word section_index_;
+  const std::string name_;
+  const ElfSectionBuilder* const link_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Dyn, typename Elf_Shdr>
+class ElfDynamicBuilder FINAL : 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,
+                     const 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() const {
+    // 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) const {
+    std::vector<Elf_Dyn> ret;
+    for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
+      if (it->section_ != nullptr) {
+        // We are adding an address relative to a section.
+        ret.push_back(
+            {it->tag_, {it->off_ + it->section_->GetSection()->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;
+  }
+
+ private:
+  struct ElfDynamicState {
+    const 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 FINAL : 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(const std::vector<uint8_t>& buf) {
+    buf_ = buf;
+  }
+
+ private:
+  std::vector<uint8_t> buf_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfOatSectionBuilder FINAL : 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() const {
+    return offset_;
+  }
+
+  Elf_Word GetSize() const {
+    return size_;
+  }
+
+ private:
+  // 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 FINAL : 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() const {
+    // 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_.GetSection()->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_->GetSection()->sh_offset;
+      } else {
+        sym.st_value = it->addr_;
+      }
+      sym.st_size = it->size_;
+      sym.st_other = it->other_;
+      sym.st_shndx = it->section_->GetSectionIndex();
+      sym.st_info = it->info_;
+
+      ret.push_back(sym);
+    }
+    return ret;
+  }
+
+  Elf_Word GetSize() const {
+    // 1 is for the implicit NULL symbol.
+    return symbols_.size() + 1;
+  }
+
+  ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* GetStrTab() {
+    return &strtab_;
+  }
+
+ private:
+  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) {}
+
+  Elf_Word GetOffset() const {
+    return offset_;
+  }
+
+  virtual const char* GetDescription() const = 0;
+  virtual bool DoActualWrite(File* elf_file) = 0;
+
+ private:
+  const Elf_Word offset_;
+
+  DISALLOW_COPY_AND_ASSIGN(ElfFilePiece);
+};
+
+template <typename Elf_Word>
+class ElfFileMemoryPiece FINAL : 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) {}
+
+ protected:
+  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;
+  }
+
+  const char* GetDescription() const OVERRIDE {
+    return dbg_name_.c_str();
+  }
+
+ 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 FINAL : public ElfFilePiece<Elf_Word> {
+ public:
+  ElfFileRodataPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
+      output_(output) {}
+
+ protected:
+  bool DoActualWrite(File* elf_file) OVERRIDE {
+    output_->SetCodeOffset(this->GetOffset());
+    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;
+  }
+
+  const char* GetDescription() const OVERRIDE {
+    return ".rodata";
+  }
+
+ private:
+  CodeOutput* const output_;
+
+  DISALLOW_COPY_AND_ASSIGN(ElfFileRodataPiece);
+};
+
+template <typename Elf_Word>
+class ElfFileOatTextPiece FINAL : public ElfFilePiece<Elf_Word> {
+ public:
+  ElfFileOatTextPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
+      output_(output) {}
+
+ protected:
+  bool DoActualWrite(File* elf_file ATTRIBUTE_UNUSED) 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;
+  }
+
+  const char* GetDescription() const OVERRIDE {
+    return ".text";
+  }
+
+ private:
+  CodeOutput* const output_;
+
+  DISALLOW_COPY_AND_ASSIGN(ElfFileOatTextPiece);
+};
+
+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() {}
+
+  const ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>& GetTextBuilder() const {
+    return text_builder_;
+  }
+
+  ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr>* GetSymtabBuilder() {
+    return &symtab_builder_;
+  }
+
+  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_.GetSection());
+    AssignSectionStr(&dynsym_builder_, &shstrtab_);
+    dynsym_builder_.SetSectionIndex(section_index_);
+    section_index_++;
+
+    // Setup .dynstr
+    section_ptrs_.push_back(dynsym_builder_.GetStrTab()->GetSection());
+    AssignSectionStr(dynsym_builder_.GetStrTab(), &shstrtab_);
+    dynsym_builder_.GetStrTab()->SetSectionIndex(section_index_);
+    section_index_++;
+
+    // Setup .hash
+    section_ptrs_.push_back(hash_builder_.GetSection());
+    AssignSectionStr(&hash_builder_, &shstrtab_);
+    hash_builder_.SetSectionIndex(section_index_);
+    section_index_++;
+
+    // Setup .rodata
+    section_ptrs_.push_back(rodata_builder_.GetSection());
+    AssignSectionStr(&rodata_builder_, &shstrtab_);
+    rodata_builder_.SetSectionIndex(section_index_);
+    section_index_++;
+
+    // Setup .text
+    section_ptrs_.push_back(text_builder_.GetSection());
+    AssignSectionStr(&text_builder_, &shstrtab_);
+    text_builder_.SetSectionIndex(section_index_);
+    section_index_++;
+
+    // Setup .dynamic
+    section_ptrs_.push_back(dynamic_builder_.GetSection());
+    AssignSectionStr(&dynamic_builder_, &shstrtab_);
+    dynamic_builder_.SetSectionIndex(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_.GetSection()->sh_offset =
+        RoundUp(base_offset, dynsym_builder_.GetSection()->sh_addralign);
+    dynsym_builder_.GetSection()->sh_addr = dynsym_builder_.GetSection()->sh_offset;
+    dynsym_builder_.GetSection()->sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
+    dynsym_builder_.GetSection()->sh_link = dynsym_builder_.GetLink();
+
+    // Get the layout of the dynstr section.
+    dynsym_builder_.GetStrTab()->GetSection()->sh_offset =
+        NextOffset<Elf_Word, Elf_Shdr>(*dynsym_builder_.GetStrTab()->GetSection(),
+                                       *dynsym_builder_.GetSection());
+    dynsym_builder_.GetStrTab()->GetSection()->sh_addr =
+        dynsym_builder_.GetStrTab()->GetSection()->sh_offset;
+    dynsym_builder_.GetStrTab()->GetSection()->sh_size = dynstr_.size();
+    dynsym_builder_.GetStrTab()->GetSection()->sh_link = dynsym_builder_.GetStrTab()->GetLink();
+
+    // Get the layout of the hash section
+    hash_builder_.GetSection()->sh_offset =
+        NextOffset<Elf_Word, Elf_Shdr>(*hash_builder_.GetSection(),
+                                       *dynsym_builder_.GetStrTab()->GetSection());
+    hash_builder_.GetSection()->sh_addr = hash_builder_.GetSection()->sh_offset;
+    hash_builder_.GetSection()->sh_size = hash_.size() * sizeof(Elf_Word);
+    hash_builder_.GetSection()->sh_link = hash_builder_.GetLink();
+
+    // Get the layout of the rodata section.
+    rodata_builder_.GetSection()->sh_offset =
+        NextOffset<Elf_Word, Elf_Shdr>(*rodata_builder_.GetSection(),
+                                       *hash_builder_.GetSection());
+    rodata_builder_.GetSection()->sh_addr = rodata_builder_.GetSection()->sh_offset;
+    rodata_builder_.GetSection()->sh_size = rodata_builder_.GetSize();
+    rodata_builder_.GetSection()->sh_link = rodata_builder_.GetLink();
+
+    // Get the layout of the text section.
+    text_builder_.GetSection()->sh_offset =
+        NextOffset<Elf_Word, Elf_Shdr>(*text_builder_.GetSection(),
+                                       *rodata_builder_.GetSection());
+    text_builder_.GetSection()->sh_addr = text_builder_.GetSection()->sh_offset;
+    text_builder_.GetSection()->sh_size = text_builder_.GetSize();
+    text_builder_.GetSection()->sh_link = text_builder_.GetLink();
+    CHECK_ALIGNED(rodata_builder_.GetSection()->sh_offset +
+                  rodata_builder_.GetSection()->sh_size, kPageSize);
+
+    // Get the layout of the dynamic section.
+    dynamic_builder_.GetSection()->sh_offset =
+        NextOffset<Elf_Word, Elf_Shdr>(*dynamic_builder_.GetSection(), *text_builder_.GetSection());
+    dynamic_builder_.GetSection()->sh_addr = dynamic_builder_.GetSection()->sh_offset;
+    dynamic_builder_.GetSection()->sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
+    dynamic_builder_.GetSection()->sh_link = dynamic_builder_.GetLink();
+
+    if (debug_logging_) {
+      LOG(INFO) << "dynsym off=" << dynsym_builder_.GetSection()->sh_offset
+                << " dynsym size=" << dynsym_builder_.GetSection()->sh_size;
+      LOG(INFO) << "dynstr off=" << dynsym_builder_.GetStrTab()->GetSection()->sh_offset
+                << " dynstr size=" << dynsym_builder_.GetStrTab()->GetSection()->sh_size;
+      LOG(INFO) << "hash off=" << hash_builder_.GetSection()->sh_offset
+                << " hash size=" << hash_builder_.GetSection()->sh_size;
+      LOG(INFO) << "rodata off=" << rodata_builder_.GetSection()->sh_offset
+                << " rodata size=" << rodata_builder_.GetSection()->sh_size;
+      LOG(INFO) << "text off=" << text_builder_.GetSection()->sh_offset
+                << " text size=" << text_builder_.GetSection()->sh_size;
+      LOG(INFO) << "dynamic off=" << dynamic_builder_.GetSection()->sh_offset
+                << " dynamic size=" << dynamic_builder_.GetSection()->sh_size;
+    }
+
+    return true;
+  }
+
+  bool Write() {
+    std::vector<ElfFilePiece<Elf_Word>*> pieces;
+    Elf_Shdr* prev = dynamic_builder_.GetSection();
+    std::string strtab;
+
+    if (IncludingDebugSymbols()) {
+      // Setup .symtab
+      section_ptrs_.push_back(symtab_builder_.GetSection());
+      AssignSectionStr(&symtab_builder_, &shstrtab_);
+      symtab_builder_.SetSectionIndex(section_index_);
+      section_index_++;
+
+      // Setup .strtab
+      section_ptrs_.push_back(symtab_builder_.GetStrTab()->GetSection());
+      AssignSectionStr(symtab_builder_.GetStrTab(), &shstrtab_);
+      symtab_builder_.GetStrTab()->SetSectionIndex(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->GetSection());
+      AssignSectionStr(builder, &shstrtab_);
+      builder->SetSectionIndex(section_index_);
+      section_index_++;
+    }
+
+    // Setup shstrtab
+    section_ptrs_.push_back(shstrtab_builder_.GetSection());
+    AssignSectionStr(&shstrtab_builder_, &shstrtab_);
+    shstrtab_builder_.SetSectionIndex(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_.GetSection()->sh_offset =
+          NextOffset<Elf_Word, Elf_Shdr>(*symtab_builder_.GetSection(),
+                                         *dynamic_builder_.GetSection());
+      symtab_builder_.GetSection()->sh_addr = 0;
+      // Add to leave space for the null symbol.
+      symtab_builder_.GetSection()->sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
+      symtab_builder_.GetSection()->sh_link = symtab_builder_.GetLink();
+
+      // Get the layout of the dynstr section.
+      symtab_builder_.GetStrTab()->GetSection()->sh_offset =
+          NextOffset<Elf_Word, Elf_Shdr>(*symtab_builder_.GetStrTab()->GetSection(),
+                                         *symtab_builder_.GetSection());
+      symtab_builder_.GetStrTab()->GetSection()->sh_addr = 0;
+      symtab_builder_.GetStrTab()->GetSection()->sh_size = strtab.size();
+      symtab_builder_.GetStrTab()->GetSection()->sh_link = symtab_builder_.GetStrTab()->GetLink();
+
+      prev = symtab_builder_.GetStrTab()->GetSection();
+      if (debug_logging_) {
+        LOG(INFO) << "symtab off=" << symtab_builder_.GetSection()->sh_offset
+                  << " symtab size=" << symtab_builder_.GetSection()->sh_size;
+        LOG(INFO) << "strtab off=" << symtab_builder_.GetStrTab()->GetSection()->sh_offset
+                  << " strtab size=" << symtab_builder_.GetStrTab()->GetSection()->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->GetSection()->sh_offset = NextOffset<Elf_Word, Elf_Shdr>(*it->GetSection(), *prev);
+      it->GetSection()->sh_addr = 0;
+      it->GetSection()->sh_size = it->GetBuffer()->size();
+      it->GetSection()->sh_link = it->GetLink();
+
+      // We postpone adding an ElfFilePiece to keep the order in "pieces."
+
+      prev = it->GetSection();
+      if (debug_logging_) {
+        LOG(INFO) << it->GetName() << " off=" << it->GetSection()->sh_offset
+                  << " size=" << it->GetSection()->sh_size;
+      }
+    }
+
+    // Get the layout of the shstrtab section
+    shstrtab_builder_.GetSection()->sh_offset =
+        NextOffset<Elf_Word, Elf_Shdr>(*shstrtab_builder_.GetSection(), *prev);
+    shstrtab_builder_.GetSection()->sh_addr = 0;
+    shstrtab_builder_.GetSection()->sh_size = shstrtab_.size();
+    shstrtab_builder_.GetSection()->sh_link = shstrtab_builder_.GetLink();
+    if (debug_logging_) {
+        LOG(INFO) << "shstrtab off=" << shstrtab_builder_.GetSection()->sh_offset
+                  << " shstrtab size=" << shstrtab_builder_.GetSection()->sh_size;
+    }
+
+    // The section list comes after come after.
+    Elf_Word sections_offset = RoundUp(
+        shstrtab_builder_.GetSection()->sh_offset + shstrtab_builder_.GetSection()->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_.GetSection()->sh_size);
+    std::vector<Elf_Sym> symtab;
+    if (IncludingDebugSymbols()) {
+      symtab = symtab_builder_.GenerateSymtab();
+      CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.GetSection()->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_.GetSection()->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_.GetSection()->sh_offset + rodata_builder_.GetSection()->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_.GetSection()->sh_addralign;
+
+    Elf_Word load_rx_size = text_builder_.GetSection()->sh_size;
+    program_headers_[PH_LOAD_R_X].p_offset = text_builder_.GetSection()->sh_offset;
+    program_headers_[PH_LOAD_R_X].p_vaddr  = text_builder_.GetSection()->sh_offset;
+    program_headers_[PH_LOAD_R_X].p_paddr  = text_builder_.GetSection()->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_.GetSection()->sh_addralign;
+
+    program_headers_[PH_LOAD_RW_].p_offset = dynamic_builder_.GetSection()->sh_offset;
+    program_headers_[PH_LOAD_RW_].p_vaddr  = dynamic_builder_.GetSection()->sh_offset;
+    program_headers_[PH_LOAD_RW_].p_paddr  = dynamic_builder_.GetSection()->sh_offset;
+    program_headers_[PH_LOAD_RW_].p_filesz = dynamic_builder_.GetSection()->sh_size;
+    program_headers_[PH_LOAD_RW_].p_memsz  = dynamic_builder_.GetSection()->sh_size;
+    program_headers_[PH_LOAD_RW_].p_align  = dynamic_builder_.GetSection()->sh_addralign;
+
+    program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.GetSection()->sh_offset;
+    program_headers_[PH_DYNAMIC].p_vaddr  = dynamic_builder_.GetSection()->sh_offset;
+    program_headers_[PH_DYNAMIC].p_paddr  = dynamic_builder_.GetSection()->sh_offset;
+    program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.GetSection()->sh_size;
+    program_headers_[PH_DYNAMIC].p_memsz  = dynamic_builder_.GetSection()->sh_size;
+    program_headers_[PH_DYNAMIC].p_align  = dynamic_builder_.GetSection()->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_.GetSectionIndex();
+
+    // 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_.GetSection()->sh_offset,
+                                                      dynamic.data(),
+                                                      dynamic_builder_.GetSection()->sh_size));
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynsym", dynsym_builder_.GetSection()->sh_offset,
+                                                      dynsym.data(),
+                                                      dynsym.size() * sizeof(Elf_Sym)));
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynstr",
+                                                    dynsym_builder_.GetStrTab()->GetSection()->sh_offset,
+                                                    dynstr_.c_str(), dynstr_.size()));
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".hash", hash_builder_.GetSection()->sh_offset,
+                                                      hash_.data(),
+                                                      hash_.size() * sizeof(Elf_Word)));
+    pieces.push_back(new ElfFileRodataPiece<Elf_Word>(rodata_builder_.GetSection()->sh_offset,
+                                                      oat_writer_));
+    pieces.push_back(new ElfFileOatTextPiece<Elf_Word>(text_builder_.GetSection()->sh_offset,
+                                                       oat_writer_));
+    if (IncludingDebugSymbols()) {
+      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".symtab",
+                                                        symtab_builder_.GetSection()->sh_offset,
+                                                        symtab.data(),
+                                                        symtab.size() * sizeof(Elf_Sym)));
+      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".strtab",
+                                                    symtab_builder_.GetStrTab()->GetSection()->sh_offset,
+                                                    strtab.c_str(), strtab.size()));
+    }
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".shstrtab",
+                                                      shstrtab_builder_.GetSection()->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->GetName(), it->GetSection()->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:
+  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]      = (sizeof(Elf_Addr) == sizeof(Elf32_Addr))
+                                         ? ELFCLASS32 : ELFCLASS64;;
+    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->GetSection()->sh_name = strtab->size();
+    *strtab += builder->GetName();
+    *strtab += '\0';
+    if (debug_logging_) {
+      LOG(INFO) << "adding section name \"" << builder->GetName() << "\" "
+                << "to shstrtab at offset " << builder->GetSection()->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() const {
+    return add_symbols_ && symtab_builder_.GetSize() > 1;
+  }
+
+  CodeOutput* const oat_writer_;
+  File* const 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_;
+  // The index of the current section being built. The first being 1.
+  uint32_t section_index_;
+  std::string dynstr_;
+  uint32_t dynstr_soname_offset_;
+  std::vector<const Elf_Shdr*> section_ptrs_;
+  std::vector<Elf_Word> hash_;
+
+  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_;
+
+  DISALLOW_COPY_AND_ASSIGN(ElfBuilder);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_ELF_BUILDER_H_
diff --git a/compiler/elf_fixup.cc b/compiler/elf_fixup.cc
deleted file mode 100644
index 0155c82..0000000
--- a/compiler/elf_fixup.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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.
- */
-
-#include "elf_fixup.h"
-
-#include <inttypes.h>
-#include <memory>
-
-#include "base/logging.h"
-#include "base/stringprintf.h"
-#include "elf_file.h"
-#include "elf_writer.h"
-
-namespace art {
-
-static const bool DEBUG_FIXUP = false;
-
-bool ElfFixup::Fixup(File* file, uintptr_t oat_data_begin) {
-  std::string error_msg;
-  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, &error_msg));
-  CHECK(elf_file.get() != nullptr) << error_msg;
-
-  // Lookup "oatdata" symbol address.
-  Elf32_Addr oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get());
-  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;
-  }
-  if (!FixupSectionHeaders(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo 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;
-  }
-  if (!FixupSymbols(*elf_file.get(), base_address, true)) {
-      LOG(WARNING) << "Failed fo 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;
-  }
-  if (!FixupRelocations(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo fixup .rel.dyn in " << file->GetPath();
-      return false;
-  }
-  return true;
-}
-
-
-bool ElfFixup::FixupDynamic(ElfFile& elf_file, uintptr_t base_address) {
-  for (Elf32_Word i = 0; i < elf_file.GetDynamicNum(); i++) {
-    Elf32_Dyn& elf_dyn = elf_file.GetDynamic(i);
-    Elf32_Word d_tag = elf_dyn.d_tag;
-    if (IsDynamicSectionPointer(d_tag, elf_file.GetHeader().e_machine)) {
-      uint32_t d_ptr = elf_dyn.d_un.d_ptr;
-      if (DEBUG_FIXUP) {
-        LOG(INFO) << StringPrintf("In %s moving Elf32_Dyn[%d] from 0x%08x to 0x%08" PRIxPTR,
-                                  elf_file.GetFile().GetPath().c_str(), i,
-                                  d_ptr, d_ptr + base_address);
-      }
-      d_ptr += base_address;
-      elf_dyn.d_un.d_ptr = d_ptr;
-    }
-  }
-  return true;
-}
-
-bool ElfFixup::FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address) {
-  for (Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) {
-    Elf32_Shdr* sh = elf_file.GetSectionHeader(i);
-    CHECK(sh != nullptr);
-    // 0 implies that the section will not exist in the memory of the process
-    if (sh->sh_addr == 0) {
-      continue;
-    }
-    if (DEBUG_FIXUP) {
-      LOG(INFO) << StringPrintf("In %s moving Elf32_Shdr[%d] from 0x%08x to 0x%08" PRIxPTR,
-                                elf_file.GetFile().GetPath().c_str(), i,
-                                sh->sh_addr, sh->sh_addr + base_address);
-    }
-    sh->sh_addr += base_address;
-  }
-  return true;
-}
-
-bool ElfFixup::FixupProgramHeaders(ElfFile& elf_file, uintptr_t base_address) {
-  // TODO: ELFObjectFile doesn't have give to Elf32_Phdr, so we do that ourselves for now.
-  for (Elf32_Word i = 0; i < elf_file.GetProgramHeaderNum(); i++) {
-    Elf32_Phdr* ph = elf_file.GetProgramHeader(i);
-    CHECK(ph != nullptr);
-    CHECK_EQ(ph->p_vaddr, ph->p_paddr) << elf_file.GetFile().GetPath() << " i=" << i;
-    CHECK((ph->p_align == 0) || (0 == ((ph->p_vaddr - ph->p_offset) & (ph->p_align - 1))))
-            << elf_file.GetFile().GetPath() << " i=" << i;
-    if (DEBUG_FIXUP) {
-      LOG(INFO) << StringPrintf("In %s moving Elf32_Phdr[%d] from 0x%08x to 0x%08" PRIxPTR,
-                                elf_file.GetFile().GetPath().c_str(), i,
-                                ph->p_vaddr, ph->p_vaddr + base_address);
-    }
-    ph->p_vaddr += base_address;
-    ph->p_paddr += base_address;
-    CHECK((ph->p_align == 0) || (0 == ((ph->p_vaddr - ph->p_offset) & (ph->p_align - 1))))
-            << elf_file.GetFile().GetPath() << " i=" << i;
-  }
-  return true;
-}
-
-bool ElfFixup::FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic) {
-  Elf32_Word section_type = dynamic ? SHT_DYNSYM : SHT_SYMTAB;
-  // TODO: Unfortunate ELFObjectFile has protected symbol access, so use ElfFile
-  Elf32_Shdr* symbol_section = elf_file.FindSectionByType(section_type);
-  if (symbol_section == nullptr) {
-    // file is missing optional .symtab
-    CHECK(!dynamic) << elf_file.GetFile().GetPath();
-    return true;
-  }
-  for (uint32_t i = 0; i < elf_file.GetSymbolNum(*symbol_section); i++) {
-    Elf32_Sym* symbol = elf_file.GetSymbol(section_type, i);
-    CHECK(symbol != nullptr);
-    if (symbol->st_value != 0) {
-      if (DEBUG_FIXUP) {
-        LOG(INFO) << StringPrintf("In %s moving Elf32_Sym[%d] from 0x%08x to 0x%08" PRIxPTR,
-                                  elf_file.GetFile().GetPath().c_str(), i,
-                                  symbol->st_value, symbol->st_value + base_address);
-      }
-      symbol->st_value += base_address;
-    }
-  }
-  return true;
-}
-
-bool ElfFixup::FixupRelocations(ElfFile& elf_file, uintptr_t base_address) {
-  for (Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) {
-    Elf32_Shdr* sh = elf_file.GetSectionHeader(i);
-    CHECK(sh != nullptr);
-    if (sh->sh_type == SHT_REL) {
-      for (uint32_t i = 0; i < elf_file.GetRelNum(*sh); i++) {
-        Elf32_Rel& rel = elf_file.GetRel(*sh, i);
-        if (DEBUG_FIXUP) {
-          LOG(INFO) << StringPrintf("In %s moving Elf32_Rel[%d] from 0x%08x to 0x%08" PRIxPTR,
-                                    elf_file.GetFile().GetPath().c_str(), i,
-                                    rel.r_offset, rel.r_offset + base_address);
-        }
-        rel.r_offset += base_address;
-      }
-    } else if (sh->sh_type == SHT_RELA) {
-      for (uint32_t i = 0; i < elf_file.GetRelaNum(*sh); i++) {
-        Elf32_Rela& rela = elf_file.GetRela(*sh, i);
-        if (DEBUG_FIXUP) {
-          LOG(INFO) << StringPrintf("In %s moving Elf32_Rela[%d] from 0x%08x to 0x%08" PRIxPTR,
-                                    elf_file.GetFile().GetPath().c_str(), i,
-                                    rela.r_offset, rela.r_offset + base_address);
-        }
-        rela.r_offset += base_address;
-      }
-    }
-  }
-  return true;
-}
-
-}  // namespace art
diff --git a/compiler/elf_fixup.h b/compiler/elf_fixup.h
deleted file mode 100644
index 1abf06b..0000000
--- a/compiler/elf_fixup.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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_ELF_FIXUP_H_
-#define ART_COMPILER_ELF_FIXUP_H_
-
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "os.h"
-
-namespace art {
-
-class ElfFile;
-
-class ElfFixup {
- public:
-  // Fixup an ELF file so that that oat header will be loaded at oat_begin.
-  // Returns true on success, false on failure.
-  static bool Fixup(File* file, uintptr_t oat_data_begin);
-
- private:
-  // Fixup .dynamic d_ptr values for the expected base_address.
-  static bool FixupDynamic(ElfFile& elf_file, uintptr_t base_address);
-
-  // Fixup Elf32_Shdr p_vaddr to load at the desired address.
-  static bool FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address);
-
-  // Fixup Elf32_Phdr p_vaddr to load at the desired address.
-  static bool FixupProgramHeaders(ElfFile& elf_file, uintptr_t base_address);
-
-  // Fixup symbol table
-  static bool FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic);
-
-  // Fixup dynamic relocations
-  static bool FixupRelocations(ElfFile& elf_file, uintptr_t base_address);
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ElfFixup);
-};
-
-}  // namespace art
-
-#endif  // ART_COMPILER_ELF_FIXUP_H_
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
deleted file mode 100644
index e8ccd67..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, nullptr, nullptr, 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_stripper.cc b/compiler/elf_stripper.cc
deleted file mode 100644
index 457d8a0..0000000
--- a/compiler/elf_stripper.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * 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.
- */
-
-#include "elf_stripper.h"
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <memory>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/stringprintf.h"
-#include "elf_file.h"
-#include "elf_utils.h"
-#include "utils.h"
-
-namespace art {
-
-bool ElfStripper::Strip(File* file, std::string* error_msg) {
-  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg));
-  if (elf_file.get() == nullptr) {
-    return false;
-  }
-
-  // ELF files produced by MCLinker look roughly like this
-  //
-  // +------------+
-  // | Elf32_Ehdr | contains number of Elf32_Shdr and offset to first
-  // +------------+
-  // | Elf32_Phdr | program headers
-  // | Elf32_Phdr |
-  // | ...        |
-  // | Elf32_Phdr |
-  // +------------+
-  // | section    | mixture of needed and unneeded sections
-  // +------------+
-  // | section    |
-  // +------------+
-  // | ...        |
-  // +------------+
-  // | section    |
-  // +------------+
-  // | Elf32_Shdr | section headers
-  // | Elf32_Shdr |
-  // | ...        | contains offset to section start
-  // | Elf32_Shdr |
-  // +------------+
-  //
-  // To strip:
-  // - leave the Elf32_Ehdr and Elf32_Phdr values in place.
-  // - walk the sections making a new set of Elf32_Shdr section headers for what we want to keep
-  // - move the sections are keeping up to fill in gaps of sections we want to strip
-  // - write new Elf32_Shdr section headers to end of file, updating Elf32_Ehdr
-  // - truncate rest of file
-  //
-
-  std::vector<Elf32_Shdr> section_headers;
-  std::vector<Elf32_Word> section_headers_original_indexes;
-  section_headers.reserve(elf_file->GetSectionHeaderNum());
-
-
-  Elf32_Shdr* string_section = elf_file->GetSectionNameStringSection();
-  CHECK(string_section != nullptr);
-  for (Elf32_Word i = 0; i < elf_file->GetSectionHeaderNum(); i++) {
-    Elf32_Shdr* sh = elf_file->GetSectionHeader(i);
-    CHECK(sh != nullptr);
-    const char* name = elf_file->GetString(*string_section, sh->sh_name);
-    if (name == nullptr) {
-      CHECK_EQ(0U, i);
-      section_headers.push_back(*sh);
-      section_headers_original_indexes.push_back(0);
-      continue;
-    }
-    if (StartsWith(name, ".debug")
-        || (strcmp(name, ".strtab") == 0)
-        || (strcmp(name, ".symtab") == 0)) {
-      continue;
-    }
-    section_headers.push_back(*sh);
-    section_headers_original_indexes.push_back(i);
-  }
-  CHECK_NE(0U, section_headers.size());
-  CHECK_EQ(section_headers.size(), section_headers_original_indexes.size());
-
-  // section 0 is the NULL section, sections start at offset of first section
-  CHECK(elf_file->GetSectionHeader(1) != nullptr);
-  Elf32_Off offset = elf_file->GetSectionHeader(1)->sh_offset;
-  for (size_t i = 1; i < section_headers.size(); i++) {
-    Elf32_Shdr& new_sh = section_headers[i];
-    Elf32_Shdr* old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]);
-    CHECK(old_sh != nullptr);
-    CHECK_EQ(new_sh.sh_name, old_sh->sh_name);
-    if (old_sh->sh_addralign > 1) {
-      offset = RoundUp(offset, old_sh->sh_addralign);
-    }
-    if (old_sh->sh_offset == offset) {
-      // already in place
-      offset += old_sh->sh_size;
-      continue;
-    }
-    // shift section earlier
-    memmove(elf_file->Begin() + offset,
-            elf_file->Begin() + old_sh->sh_offset,
-            old_sh->sh_size);
-    new_sh.sh_offset = offset;
-    offset += old_sh->sh_size;
-  }
-
-  Elf32_Off shoff = offset;
-  size_t section_headers_size_in_bytes = section_headers.size() * sizeof(Elf32_Shdr);
-  memcpy(elf_file->Begin() + offset, &section_headers[0], section_headers_size_in_bytes);
-  offset += section_headers_size_in_bytes;
-
-  elf_file->GetHeader().e_shnum = section_headers.size();
-  elf_file->GetHeader().e_shoff = shoff;
-  int result = ftruncate(file->Fd(), offset);
-  if (result != 0) {
-    *error_msg = StringPrintf("Failed to truncate while stripping ELF file: '%s': %s",
-                              file->GetPath().c_str(), strerror(errno));
-    return false;
-  }
-  return true;
-}
-
-}  // namespace art
diff --git a/compiler/elf_stripper.h b/compiler/elf_stripper.h
deleted file mode 100644
index f1a1d46..0000000
--- a/compiler/elf_stripper.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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_ELF_STRIPPER_H_
-#define ART_COMPILER_ELF_STRIPPER_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "os.h"
-
-namespace art {
-
-class ElfStripper {
- public:
-  // Strip an ELF file of unneeded debugging information.
-  // Returns true on success, false on failure.
-  static bool Strip(File* file, std::string* error_msg);
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ElfStripper);
-};
-
-}  // namespace art
-
-#endif  // ART_COMPILER_ELF_STRIPPER_H_
diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc
index 55ee18e..47402f3 100644
--- a/compiler/elf_writer.cc
+++ b/compiler/elf_writer.cc
@@ -30,8 +30,8 @@
 
 namespace art {
 
-uint32_t ElfWriter::GetOatDataAddress(ElfFile* elf_file) {
-  Elf32_Addr oatdata_address = elf_file->FindSymbolAddress(SHT_DYNSYM,
+uintptr_t ElfWriter::GetOatDataAddress(ElfFile* elf_file) {
+  uintptr_t oatdata_address = elf_file->FindSymbolAddress(SHT_DYNSYM,
                                                            "oatdata",
                                                            false);
   CHECK_NE(0U, oatdata_address);
@@ -51,4 +51,16 @@
   CHECK_NE(0U, oat_data_offset);
 }
 
+bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) {
+  std::string error_msg;
+  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, &error_msg));
+  CHECK(elf_file.get() != nullptr) << error_msg;
+
+  // Lookup "oatdata" symbol address.
+  uintptr_t oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get());
+  uintptr_t base_address = oat_data_begin - oatdata_address;
+
+  return elf_file->Fixup(base_address);
+}
+
 }  // namespace art
diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h
index 03b965a..033c1f8 100644
--- a/compiler/elf_writer.h
+++ b/compiler/elf_writer.h
@@ -42,7 +42,9 @@
                                    size_t& oat_data_offset);
 
   // Returns runtime oat_data runtime address for an opened ElfFile.
-  static uint32_t GetOatDataAddress(ElfFile* elf_file);
+  static uintptr_t GetOatDataAddress(ElfFile* elf_file);
+
+  static bool Fixup(File* file, uintptr_t oat_data_begin);
 
  protected:
   ElfWriter(const CompilerDriver& driver, File* elf_file)
diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc
index 3dba426..7705b9c 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..25cf086 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,
-                           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();
+class OatWriterWrapper FINAL : 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* const 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 ATTRIBUTE_UNUSED,
+                           const std::string& android_root_unused ATTRIBUTE_UNUSED,
+                           bool is_host_unused ATTRIBUTE_UNUSED) {
+  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,291 @@
   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->GetTextBuilder().GetSection()->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->GetSymtabBuilder();
+  for (auto it = method_info.begin(); it != method_info.end(); ++it) {
+    symtab->AddSymbol(it->method_name_, &builder->GetTextBuilder(), it->low_pc_, true,
+                      it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
+
+    // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
+    // instructions, so that disassembler tools can correctly disassemble.
+    if (it->compiled_method_->GetInstructionSet() == kThumb2) {
+      symtab->AddSymbol("$t", &builder->GetTextBuilder(), it->low_pc_ & ~1, true,
+                        0, STB_LOCAL, STT_NOTYPE);
+    }
+
+    // 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/elf_writer_test.cc b/compiler/elf_writer_test.cc
index e479322..2ffbd10 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -17,6 +17,7 @@
 #include "elf_file.h"
 
 #include "base/stringprintf.h"
+#include "base/unix_file/fd_file.h"
 #include "common_compiler_test.h"
 #include "oat.h"
 #include "utils.h"
diff --git a/compiler/file_output_stream.h b/compiler/file_output_stream.h
index 76b00fe..9dfbd7f 100644
--- a/compiler/file_output_stream.h
+++ b/compiler/file_output_stream.h
@@ -23,7 +23,7 @@
 
 namespace art {
 
-class FileOutputStream : public OutputStream {
+class FileOutputStream FINAL : public OutputStream {
  public:
   explicit FileOutputStream(File* file);
 
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 0aef512..7e2be3e 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -21,8 +21,9 @@
 #include <vector>
 
 #include "base/unix_file/fd_file.h"
+#include "class_linker.h"
 #include "common_compiler_test.h"
-#include "elf_fixup.h"
+#include "elf_writer.h"
 #include "gc/space/image_space.h"
 #include "image_writer.h"
 #include "lock_word.h"
@@ -61,6 +62,10 @@
   oat_filename += "oat";
   ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str()));
 
+  const uintptr_t requested_image_base = ART_BASE_ADDRESS;
+  std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_, requested_image_base,
+                                                      /*compile_pic*/false));
+  // TODO: compile_pic should be a test argument.
   {
     {
       jobject class_loader = NULL;
@@ -78,15 +83,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.get(), &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,13 +99,11 @@
   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(), /*compile_pic*/false);
+    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());
+    bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer->GetOatDataBegin());
     ASSERT_TRUE(success_fixup);
   }
 
@@ -133,6 +136,7 @@
   // Remove the reservation of the memory for use to load the image.
   // Need to do this before we reset the runtime.
   UnreserveImageSpace();
+  writer.reset(nullptr);
 
   runtime_.reset();
   java_lang_dex_file_ = NULL;
@@ -165,8 +169,8 @@
 
   gc::space::ImageSpace* image_space = heap->GetImageSpace();
   image_space->VerifyImageAllocations();
-  byte* image_begin = image_space->Begin();
-  byte* image_end = image_space->End();
+  uint8_t* image_begin = image_space->Begin();
+  uint8_t* image_end = image_space->End();
   CHECK_EQ(requested_image_base, reinterpret_cast<uintptr_t>(image_begin));
   for (size_t i = 0; i < dex->NumClassDefs(); ++i) {
     const DexFile::ClassDef& class_def = dex->GetClassDef(i);
@@ -175,11 +179,11 @@
     EXPECT_TRUE(klass != nullptr) << descriptor;
     if (image_classes.find(descriptor) != image_classes.end()) {
       // Image classes should be located inside the image.
-      EXPECT_LT(image_begin, reinterpret_cast<byte*>(klass)) << descriptor;
-      EXPECT_LT(reinterpret_cast<byte*>(klass), image_end) << descriptor;
+      EXPECT_LT(image_begin, reinterpret_cast<uint8_t*>(klass)) << descriptor;
+      EXPECT_LT(reinterpret_cast<uint8_t*>(klass), image_end) << descriptor;
     } else {
-      EXPECT_TRUE(reinterpret_cast<byte*>(klass) >= image_end ||
-                  reinterpret_cast<byte*>(klass) < image_begin) << descriptor;
+      EXPECT_TRUE(reinterpret_cast<uint8_t*>(klass) >= image_end ||
+                  reinterpret_cast<uint8_t*>(klass) < image_begin) << descriptor;
     }
     EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false)));
   }
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 0b304eb..cf2cddb 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,28 +67,49 @@
 
 namespace art {
 
-bool ImageWriter::Write(const std::string& image_filename,
-                        uintptr_t image_begin,
-                        const std::string& oat_filename,
-                        const std::string& oat_location,
-                        bool compile_pic) {
-  CHECK(!image_filename.empty());
+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.
 
-  CHECK_NE(image_begin, 0U);
-  image_begin_ = reinterpret_cast<byte*>(image_begin);
-  compile_pic_ = compile_pic;
+  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,
+                        const std::string& oat_filename,
+                        const std::string& oat_location) {
+  CHECK(!image_filename.empty());
 
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
   std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
   if (oat_file.get() == NULL) {
-    LOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
+    PLOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
     return false;
   }
   std::string error_msg;
   oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, &error_msg);
   if (oat_file_ == nullptr) {
-    LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
+    PLOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
         << ": " << error_msg;
     return false;
   }
@@ -117,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) {
@@ -203,7 +206,7 @@
       break;
     default:
       LOG(FATAL) << "Unreachable.";
-      break;
+      UNREACHABLE();
   }
   object->SetLockWord(LockWord::FromForwardingAddress(offset), false);
   DCHECK(IsImageOffsetAssigned(object));
@@ -234,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;
@@ -262,7 +265,7 @@
   return true;
 }
 
-void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) {
+void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED) {
   if (!obj->GetClass()->IsStringClass()) {
     return;
   }
@@ -532,8 +535,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()));
@@ -548,55 +550,56 @@
   {
     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);
-  const byte* oat_file_end = oat_file_begin + oat_loaded_size;
+  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 uint8_t* oat_file_begin = GetOatFileBegin();
+  const uint8_t* 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();
+  const uint8_t* oat_data_end = oat_data_begin_ + oat_file_->Size();
 
   // Return to write header at start of image with future location of image_roots. At this point,
   // image_end_ is the size of the image (excluding bitmaps).
   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),
-                           compile_pic_);
-  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),
+                                    compile_pic_);
 }
 
+
 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) {
@@ -605,8 +608,8 @@
   ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
   // see GetLocalAddress for similar computation
   size_t offset = image_writer->GetImageOffset(obj);
-  byte* dst = image_writer->image_->Begin() + offset;
-  const byte* src = reinterpret_cast<const byte*>(obj);
+  uint8_t* dst = image_writer->image_->Begin() + offset;
+  const uint8_t* src = reinterpret_cast<const uint8_t*>(obj);
   size_t n = obj->SizeOf();
   DCHECK_LT(offset + n, image_writer->image_->Size());
   memcpy(dst, src, n);
@@ -659,7 +662,8 @@
     }
   }
 
-  void operator()(mirror::Class* /*klass*/, mirror::Reference* ref) const
+  void operator()(mirror::Class* klass ATTRIBUTE_UNUSED,
+                  mirror::Reference* ref ATTRIBUTE_UNUSED) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
     LOG(FATAL) << "Reference not expected here.";
@@ -689,7 +693,7 @@
   }
 }
 
-const byte* ImageWriter::GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted) {
+const uint8_t* ImageWriter::GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted) {
   DCHECK(!method->IsResolutionMethod() && !method->IsImtConflictMethod() &&
          !method->IsImtUnimplementedMethod() && !method->IsAbstract()) << PrettyMethod(method);
 
@@ -697,7 +701,7 @@
   // trampoline.
 
   // Quick entrypoint:
-  const byte* quick_code = GetOatAddress(method->GetQuickOatCodeOffset());
+  const uint8_t* quick_code = GetOatAddress(method->GetQuickOatCodeOffset());
   *quick_is_interpreted = false;
   if (quick_code != nullptr &&
       (!method->IsStatic() || method->IsConstructor() || method->GetDeclaringClass()->IsInitialized())) {
@@ -719,7 +723,7 @@
   return quick_code;
 }
 
-const byte* ImageWriter::GetQuickEntryPoint(mirror::ArtMethod* method) {
+const uint8_t* ImageWriter::GetQuickEntryPoint(mirror::ArtMethod* method) {
   // Calculate the quick entry point following the same logic as FixupMethod() below.
   // The resolution method has a special trampoline to call.
   Runtime* runtime = Runtime::Current();
@@ -748,36 +752,29 @@
   // The resolution method has a special trampoline to call.
   Runtime* runtime = Runtime::Current();
   if (UNLIKELY(orig == runtime->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->GetImtConflictMethod() ||
                       orig == runtime->GetImtUnimplementedMethod())) {
-#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_))));
+          (const_cast<uint8_t*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_))));
     } else {
       bool quick_is_interpreted;
-      const byte* quick_code = GetQuickCode(orig, &quick_is_interpreted);
+      const uint8_t* quick_code = GetQuickCode(orig, &quick_is_interpreted);
       copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code);
 
       // Portable entrypoint:
+      const uint8_t* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
       bool portable_is_interpreted = false;
-#if defined(ART_USE_PORTABLE_COMPILER)
-      const byte* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
       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.
@@ -797,7 +794,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.
@@ -806,7 +803,7 @@
       } else {
         // Normal (non-abstract non-native) methods have various tables to relocate.
         uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset();
-        const byte* native_gc_map = GetOatAddress(native_gc_map_offset);
+        const uint8_t* native_gc_map = GetOatAddress(native_gc_map_offset);
         copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map));
       }
 
@@ -817,32 +814,26 @@
           : interpreter_to_compiled_code_bridge_offset_;
       copy->SetEntryPointFromInterpreter<kVerifyNone>(
           reinterpret_cast<EntryPointFromInterpreter*>(
-              const_cast<byte*>(GetOatAddress(interpreter_code))));
+              const_cast<uint8_t*>(GetOatAddress(interpreter_code))));
     }
   }
 }
 
 static OatHeader* GetOatHeaderFromElf(ElfFile* elf) {
-  Elf32_Shdr* data_sec = elf->FindSectionByName(".rodata");
-  if (data_sec == nullptr) {
+  uint64_t data_sec_offset;
+  bool has_data_sec = elf->GetSectionOffsetAndSize(".rodata", &data_sec_offset, nullptr);
+  if (!has_data_sec) {
     return nullptr;
   }
-  return reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
+  return reinterpret_cast<OatHeader*>(elf->Begin() + data_sec_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 61365fe..b0cf2b2 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -38,21 +38,42 @@
 // Write a Space built during compilation for use during execution.
 class ImageWriter FINAL {
  public:
-  explicit ImageWriter(const CompilerDriver& compiler_driver)
-      : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(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),
+  ImageWriter(const CompilerDriver& compiler_driver, uintptr_t image_begin,
+              bool compile_pic)
+      : compiler_driver_(compiler_driver), image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
+        image_end_(0), image_roots_address_(0), oat_file_(nullptr),
+        oat_data_begin_(nullptr), interpreter_to_interpreter_bridge_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),
-        compile_pic_(false) {}
+        quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic) {
+    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 == nullptr) {
+      return nullptr;
+    }
+    return reinterpret_cast<mirror::Object*>(image_begin_ + GetImageOffset(object));
+  }
+
+  uint8_t* 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,
-             bool compile_pic)
+             const std::string& oat_location)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
   uintptr_t GetOatDataBegin() {
@@ -78,22 +99,14 @@
     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);
-    byte* dst = image_->Begin() + offset;
+    uint8_t* dst = image_->Begin() + offset;
     return reinterpret_cast<mirror::Object*>(dst);
   }
 
-  const byte* GetOatAddress(uint32_t offset) const {
+  const uint8_t* GetOatAddress(uint32_t offset) const {
 #if !defined(ART_USE_PORTABLE_COMPILER)
     // With Quick, code is within the OatFile, as there are all in one
     // .o ELF object. However with Portable, the code is always in
@@ -134,7 +147,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_);
@@ -158,35 +173,37 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Get quick code for non-resolution/imt_conflict/abstract method.
-  const byte* GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted)
+  const uint8_t* GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  const byte* GetQuickEntryPoint(mirror::ArtMethod* method)
+  const uint8_t* GetQuickEntryPoint(mirror::ArtMethod* method)
       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.
+  uint8_t* 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_;
 
   // Beginning target oat address for the pointers from the output image to its oat file.
-  const byte* oat_data_begin_;
+  const uint8_t* oat_data_begin_;
 
   // Image bitmap which lets us know where the objects inside of the image reside.
   std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_;
@@ -202,7 +219,7 @@
   uint32_t quick_imt_conflict_trampoline_offset_;
   uint32_t quick_resolution_trampoline_offset_;
   uint32_t quick_to_interpreter_bridge_offset_;
-  bool compile_pic_;
+  const bool compile_pic_;
 
   friend class FixupVisitor;
   friend class FixupClassVisitor;
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index ed1175b..2755442 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -73,17 +73,15 @@
     }
     ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
     if (check_generic_jni_) {
-      method->SetEntryPointFromQuickCompiledCode(class_linker_->GetQuickGenericJniTrampoline());
+      method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub());
     } else {
-      if (method->GetEntryPointFromQuickCompiledCode() == nullptr ||
-          method->GetEntryPointFromQuickCompiledCode() == class_linker_->GetQuickGenericJniTrampoline()) {
+      const void* code = method->GetEntryPointFromQuickCompiledCode();
+      if (code == nullptr || class_linker_->IsQuickGenericJniStub(code)) {
         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);
@@ -375,12 +365,12 @@
   EXPECT_EQ(0, gJava_MyClassNatives_fooDD_calls);
   jdouble result = env_->CallNonvirtualDoubleMethod(jobj_, jklass_, jmethod_,
                                                     99.0, 10.0);
-  EXPECT_EQ(99.0 - 10.0, result);
+  EXPECT_DOUBLE_EQ(99.0 - 10.0, result);
   EXPECT_EQ(1, gJava_MyClassNatives_fooDD_calls);
   jdouble a = 3.14159265358979323846;
   jdouble b = 0.69314718055994530942;
   result = env_->CallNonvirtualDoubleMethod(jobj_, jklass_, jmethod_, a, b);
-  EXPECT_EQ(a - b, result);
+  EXPECT_DOUBLE_EQ(a - b, result);
   EXPECT_EQ(2, gJava_MyClassNatives_fooDD_calls);
 
   gJava_MyClassNatives_fooDD_calls = 0;
@@ -523,13 +513,13 @@
 
   EXPECT_EQ(0, gJava_MyClassNatives_fooSDD_calls);
   jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_, 99.0, 10.0);
-  EXPECT_EQ(99.0 - 10.0, result);
+  EXPECT_DOUBLE_EQ(99.0 - 10.0, result);
   EXPECT_EQ(1, gJava_MyClassNatives_fooSDD_calls);
   jdouble a = 3.14159265358979323846;
   jdouble b = 0.69314718055994530942;
   result = env_->CallStaticDoubleMethod(jklass_, jmethod_, a, b);
-  EXPECT_EQ(a - b, result);
-  EXPECT_EQ(2, gJava_MyClassNatives_fooSDD_calls);
+  EXPECT_DOUBLE_EQ(a - b, result);
+  EXPECT_DOUBLE_EQ(2, gJava_MyClassNatives_fooSDD_calls);
 
   gJava_MyClassNatives_fooSDD_calls = 0;
 }
@@ -540,7 +530,7 @@
 // point return value would be in xmm0. We use log, to somehow ensure
 // the compiler will use the floating point stack.
 
-jdouble Java_MyClassNatives_logD(JNIEnv* env, jclass klass, jdouble x) {
+jdouble Java_MyClassNatives_logD(JNIEnv*, jclass, jdouble x) {
   return log(x);
 }
 
@@ -549,12 +539,12 @@
   SetUpForTest(true, "logD", "(D)D", reinterpret_cast<void*>(&Java_MyClassNatives_logD));
 
   jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_, 2.0);
-  EXPECT_EQ(log(2.0), result);
+  EXPECT_DOUBLE_EQ(log(2.0), result);
 }
 
 JNI_TEST(RunStaticLogDoubleMethod)
 
-jfloat Java_MyClassNatives_logF(JNIEnv* env, jclass klass, jfloat x) {
+jfloat Java_MyClassNatives_logF(JNIEnv*, jclass, jfloat x) {
   return logf(x);
 }
 
@@ -563,20 +553,20 @@
   SetUpForTest(true, "logF", "(F)F", reinterpret_cast<void*>(&Java_MyClassNatives_logF));
 
   jfloat result = env_->CallStaticFloatMethod(jklass_, jmethod_, 2.0);
-  EXPECT_EQ(logf(2.0), result);
+  EXPECT_FLOAT_EQ(logf(2.0), result);
 }
 
 JNI_TEST(RunStaticLogFloatMethod)
 
-jboolean Java_MyClassNatives_returnTrue(JNIEnv* env, jclass klass) {
+jboolean Java_MyClassNatives_returnTrue(JNIEnv*, jclass) {
   return JNI_TRUE;
 }
 
-jboolean Java_MyClassNatives_returnFalse(JNIEnv* env, jclass klass) {
+jboolean Java_MyClassNatives_returnFalse(JNIEnv*, jclass) {
   return JNI_FALSE;
 }
 
-jint Java_MyClassNatives_returnInt(JNIEnv* env, jclass klass) {
+jint Java_MyClassNatives_returnInt(JNIEnv*, jclass) {
   return 42;
 }
 
@@ -795,9 +785,9 @@
     EXPECT_EQ(11, trace_array->GetLength());
 
     // Check stack trace entries have expected values
-    for (int32_t i = 0; i < trace_array->GetLength(); ++i) {
-      EXPECT_EQ(-2, trace_array->Get(i)->GetLineNumber());
-      mirror::StackTraceElement* ste = trace_array->Get(i);
+    for (int32_t j = 0; j < trace_array->GetLength(); ++j) {
+      EXPECT_EQ(-2, trace_array->Get(j)->GetLineNumber());
+      mirror::StackTraceElement* ste = trace_array->Get(j);
       EXPECT_STREQ("MyClassNatives.java", ste->GetFileName()->ToModifiedUtf8().c_str());
       EXPECT_STREQ("MyClassNatives", ste->GetDeclaringClass()->ToModifiedUtf8().c_str());
       EXPECT_STREQ("fooI", ste->GetMethodName()->ToModifiedUtf8().c_str());
@@ -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)
@@ -1057,16 +1047,19 @@
 
   jfloat result = env_->CallNonvirtualFloatMethod(jobj_, jklass_, jmethod_,
                                                     99.0F, 10.0F);
-  EXPECT_EQ(99.0F - 10.0F, result);
+  EXPECT_FLOAT_EQ(99.0F - 10.0F, result);
   jfloat a = 3.14159F;
   jfloat b = 0.69314F;
   result = env_->CallNonvirtualFloatMethod(jobj_, jklass_, jmethod_, a, b);
-  EXPECT_EQ(a - b, result);
+  EXPECT_FLOAT_EQ(a - b, result);
 }
 
 JNI_TEST(CompileAndRunFloatFloatMethod)
 
-void Java_MyClassNatives_checkParameterAlign(JNIEnv* env, jobject thisObj, jint i1, jlong l1) {
+void Java_MyClassNatives_checkParameterAlign(JNIEnv* env ATTRIBUTE_UNUSED,
+                                             jobject thisObj ATTRIBUTE_UNUSED,
+                                             jint i1 ATTRIBUTE_UNUSED,
+                                             jlong l1 ATTRIBUTE_UNUSED) {
 //  EXPECT_EQ(kNative, Thread::Current()->GetState());
 //  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
 //  EXPECT_TRUE(thisObj != nullptr);
@@ -1530,7 +1523,7 @@
 
 JNI_TEST(WithoutImplementation)
 
-void Java_MyClassNatives_stackArgsIntsFirst(JNIEnv* env, jclass klass, jint i1, jint i2, jint i3,
+void Java_MyClassNatives_stackArgsIntsFirst(JNIEnv*, jclass, jint i1, jint i2, jint i3,
                                             jint i4, jint i5, jint i6, jint i7, jint i8, jint i9,
                                             jint i10, jfloat f1, jfloat f2, jfloat f3, jfloat f4,
                                             jfloat f5, jfloat f6, jfloat f7, jfloat f8, jfloat f9,
@@ -1601,7 +1594,7 @@
 
 JNI_TEST(StackArgsIntsFirst)
 
-void Java_MyClassNatives_stackArgsFloatsFirst(JNIEnv* env, jclass klass, jfloat f1, jfloat f2,
+void Java_MyClassNatives_stackArgsFloatsFirst(JNIEnv*, jclass, jfloat f1, jfloat f2,
                                               jfloat f3, jfloat f4, jfloat f5, jfloat f6, jfloat f7,
                                               jfloat f8, jfloat f9, jfloat f10, jint i1, jint i2,
                                               jint i3, jint i4, jint i5, jint i6, jint i7, jint i8,
@@ -1672,7 +1665,7 @@
 
 JNI_TEST(StackArgsFloatsFirst)
 
-void Java_MyClassNatives_stackArgsMixed(JNIEnv* env, jclass klass, jint i1, jfloat f1, jint i2,
+void Java_MyClassNatives_stackArgsMixed(JNIEnv*, jclass, jint i1, jfloat f1, jint i2,
                                         jfloat f2, jint i3, jfloat f3, jint i4, jfloat f4, jint i5,
                                         jfloat f5, jint i6, jfloat f6, jint i7, jfloat f7, jint i8,
                                         jfloat f8, jint i9, jfloat f9, jint i10, jfloat f10) {
diff --git a/compiler/jni/portable/jni_compiler.cc b/compiler/jni/portable/jni_compiler.cc
index d2f54f8..ff37d85 100644
--- a/compiler/jni/portable/jni_compiler.cc
+++ b/compiler/jni/portable/jni_compiler.cc
@@ -298,6 +298,7 @@
     case 'D': ret_type =  irb_.getJDoubleTy(); break;
     case 'L': ret_type =  irb_.getJObjectTy(); break;
     default: LOG(FATAL)  << "Unreachable: unexpected return type in shorty " << shorty;
+      UNREACHABLE();
   }
   // Get argument type
   std::vector< ::llvm::Type*> args_type;
diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc
index f0c0ed7..769cd4c 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.cc
+++ b/compiler/jni/quick/arm/calling_convention_arm.cc
@@ -21,6 +21,22 @@
 namespace art {
 namespace arm {
 
+// Used by hard float.
+static const Register kHFCoreArgumentRegisters[] = {
+  R0, R1, R2, R3
+};
+
+static const SRegister kHFSArgumentRegisters[] = {
+  S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15
+};
+
+static const DRegister kHFDArgumentRegisters[] = {
+  D0, D1, D2, D3, D4, D5, D6, D7
+};
+
+static_assert(arraysize(kHFDArgumentRegisters) * 2 == arraysize(kHFSArgumentRegisters),
+    "ks d argument registers mismatch");
+
 // Calling convention
 
 ManagedRegister ArmManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
@@ -31,26 +47,43 @@
   return ArmManagedRegister::FromCoreRegister(IP);  // R12
 }
 
-static ManagedRegister ReturnRegisterForShorty(const char* shorty) {
-  if (shorty[0] == 'F') {
-    return ArmManagedRegister::FromCoreRegister(R0);
-  } else if (shorty[0] == 'D') {
-    return ArmManagedRegister::FromRegisterPair(R0_R1);
-  } else if (shorty[0] == 'J') {
-    return ArmManagedRegister::FromRegisterPair(R0_R1);
-  } else if (shorty[0] == 'V') {
-    return ArmManagedRegister::NoRegister();
+ManagedRegister ArmManagedRuntimeCallingConvention::ReturnRegister() {
+  if (kArm32QuickCodeUseSoftFloat) {
+    switch (GetShorty()[0]) {
+    case 'V':
+      return ArmManagedRegister::NoRegister();
+    case 'D':
+    case 'J':
+      return ArmManagedRegister::FromRegisterPair(R0_R1);
+    default:
+      return ArmManagedRegister::FromCoreRegister(R0);
+    }
   } else {
-    return ArmManagedRegister::FromCoreRegister(R0);
+    switch (GetShorty()[0]) {
+    case 'V':
+      return ArmManagedRegister::NoRegister();
+    case 'D':
+      return ArmManagedRegister::FromDRegister(D0);
+    case 'F':
+      return ArmManagedRegister::FromSRegister(S0);
+    case 'J':
+      return ArmManagedRegister::FromRegisterPair(R0_R1);
+    default:
+      return ArmManagedRegister::FromCoreRegister(R0);
+    }
   }
 }
 
-ManagedRegister ArmManagedRuntimeCallingConvention::ReturnRegister() {
-  return ReturnRegisterForShorty(GetShorty());
-}
-
 ManagedRegister ArmJniCallingConvention::ReturnRegister() {
-  return ReturnRegisterForShorty(GetShorty());
+  switch (GetShorty()[0]) {
+  case 'V':
+    return ArmManagedRegister::NoRegister();
+  case 'D':
+  case 'J':
+    return ArmManagedRegister::FromRegisterPair(R0_R1);
+  default:
+    return ArmManagedRegister::FromCoreRegister(R0);
+  }
 }
 
 ManagedRegister ArmJniCallingConvention::IntReturnRegister() {
@@ -88,17 +121,70 @@
 const ManagedRegisterEntrySpills& ArmManagedRuntimeCallingConvention::EntrySpills() {
   // We spill the argument registers on ARM to free them up for scratch use, we then assume
   // all arguments are on the stack.
-  if (entry_spills_.size() == 0) {
-    size_t num_spills = NumArgs() + NumLongOrDoubleArgs();
-    if (num_spills > 0) {
-      entry_spills_.push_back(ArmManagedRegister::FromCoreRegister(R1));
-      if (num_spills > 1) {
-        entry_spills_.push_back(ArmManagedRegister::FromCoreRegister(R2));
-        if (num_spills > 2) {
-          entry_spills_.push_back(ArmManagedRegister::FromCoreRegister(R3));
+  if (kArm32QuickCodeUseSoftFloat) {
+    if (entry_spills_.size() == 0) {
+      size_t num_spills = NumArgs() + NumLongOrDoubleArgs();
+      if (num_spills > 0) {
+        entry_spills_.push_back(ArmManagedRegister::FromCoreRegister(R1));
+        if (num_spills > 1) {
+          entry_spills_.push_back(ArmManagedRegister::FromCoreRegister(R2));
+          if (num_spills > 2) {
+            entry_spills_.push_back(ArmManagedRegister::FromCoreRegister(R3));
+          }
         }
       }
     }
+  } else {
+    if ((entry_spills_.size() == 0) && (NumArgs() > 0)) {
+      uint32_t gpr_index = 1;  // R0 ~ R3. Reserve r0 for ArtMethod*.
+      uint32_t fpr_index = 0;  // S0 ~ S15.
+      uint32_t fpr_double_index = 0;  // D0 ~ D7.
+
+      ResetIterator(FrameOffset(0));
+      while (HasNext()) {
+        if (IsCurrentParamAFloatOrDouble()) {
+          if (IsCurrentParamADouble()) {  // Double.
+            // Double should not overlap with float.
+            fpr_double_index = (std::max(fpr_double_index * 2, RoundUp(fpr_index, 2))) / 2;
+            if (fpr_double_index < arraysize(kHFDArgumentRegisters)) {
+              entry_spills_.push_back(
+                  ArmManagedRegister::FromDRegister(kHFDArgumentRegisters[fpr_double_index++]));
+            } else {
+              entry_spills_.push_back(ManagedRegister::NoRegister(), 8);
+            }
+          } else {  // Float.
+            // Float should not overlap with double.
+            if (fpr_index % 2 == 0) {
+              fpr_index = std::max(fpr_double_index * 2, fpr_index);
+            }
+            if (fpr_index < arraysize(kHFSArgumentRegisters)) {
+              entry_spills_.push_back(
+                  ArmManagedRegister::FromSRegister(kHFSArgumentRegisters[fpr_index++]));
+            } else {
+              entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
+            }
+          }
+        } else {
+          // FIXME: Pointer this returns as both reference and long.
+          if (IsCurrentParamALong() && !IsCurrentParamAReference()) {  // Long.
+            if (gpr_index < arraysize(kHFCoreArgumentRegisters)) {
+              entry_spills_.push_back(
+                  ArmManagedRegister::FromCoreRegister(kHFCoreArgumentRegisters[gpr_index++]));
+            } else {
+              entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
+            }
+          }
+          // High part of long or 32-bit argument.
+          if (gpr_index < arraysize(kHFCoreArgumentRegisters)) {
+            entry_spills_.push_back(
+                ArmManagedRegister::FromCoreRegister(kHFCoreArgumentRegisters[gpr_index++]));
+          } else {
+            entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
+          }
+        }
+        Next();
+      }
+    }
   }
   return entry_spills_;
 }
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index b95dad2..29763a2 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -21,7 +21,7 @@
 namespace art {
 namespace arm64 {
 
-static const Register kCoreArgumentRegisters[] = {
+static const XRegister kXArgumentRegisters[] = {
   X0, X1, X2, X3, X4, X5, X6, X7
 };
 
@@ -39,11 +39,11 @@
 
 // Calling convention
 ManagedRegister Arm64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
-  return Arm64ManagedRegister::FromCoreRegister(X20);  // saved on entry restored on exit
+  return Arm64ManagedRegister::FromXRegister(X20);  // saved on entry restored on exit
 }
 
 ManagedRegister Arm64JniCallingConvention::InterproceduralScratchRegister() {
-  return Arm64ManagedRegister::FromCoreRegister(X20);  // saved on entry restored on exit
+  return Arm64ManagedRegister::FromXRegister(X20);  // saved on entry restored on exit
 }
 
 static ManagedRegister ReturnRegisterForShorty(const char* shorty) {
@@ -52,7 +52,7 @@
   } else if (shorty[0] == 'D') {
     return Arm64ManagedRegister::FromDRegister(D0);
   } else if (shorty[0] == 'J') {
-    return Arm64ManagedRegister::FromCoreRegister(X0);
+    return Arm64ManagedRegister::FromXRegister(X0);
   } else if (shorty[0] == 'V') {
     return Arm64ManagedRegister::NoRegister();
   } else {
@@ -75,7 +75,7 @@
 // Managed runtime calling convention
 
 ManagedRegister Arm64ManagedRuntimeCallingConvention::MethodRegister() {
-  return Arm64ManagedRegister::FromCoreRegister(X0);
+  return Arm64ManagedRegister::FromXRegister(X0);
 }
 
 bool Arm64ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
@@ -129,7 +129,7 @@
       } else {  // GP regs.
         if (gp_reg_index < 8) {
           if (IsCurrentParamALong() && (!IsCurrentParamAReference())) {
-            entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(kCoreArgumentRegisters[gp_reg_index]));
+            entry_spills_.push_back(Arm64ManagedRegister::FromXRegister(kXArgumentRegisters[gp_reg_index]));
           } else {
             entry_spills_.push_back(Arm64ManagedRegister::FromWRegister(kWArgumentRegisters[gp_reg_index]));
           }
@@ -154,17 +154,17 @@
     : JniCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) {
   // TODO: Ugly hard code...
   // Should generate these according to the spill mask automatically.
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X20));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X21));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X22));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X23));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X24));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X25));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X26));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X27));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X28));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X29));
-  callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X30));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X20));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X21));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X22));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X23));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X24));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X25));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X26));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X27));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X28));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X29));
+  callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X30));
 }
 
 uint32_t Arm64JniCallingConvention::CoreSpillMask() const {
@@ -232,7 +232,7 @@
     int gp_reg = itr_args_ - itr_float_and_doubles_;
     CHECK_LT(static_cast<unsigned int>(gp_reg), 8u);
     if (IsCurrentParamALong() || IsCurrentParamAReference() || IsCurrentParamJniEnv())  {
-      return Arm64ManagedRegister::FromCoreRegister(kCoreArgumentRegisters[gp_reg]);
+      return Arm64ManagedRegister::FromXRegister(kXArgumentRegisters[gp_reg]);
     } else {
       return Arm64ManagedRegister::FromWRegister(kWArgumentRegisters[gp_reg]);
     }
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index c38cfaf..3c3aa02 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
@@ -173,12 +176,8 @@
   // 4. Write out the end of the quick frames.
   if (is_64_bit_target) {
     __ StoreStackPointerToThread64(Thread::TopOfManagedStackOffset<8>());
-    __ StoreImmediateToThread64(Thread::TopOfManagedStackPcOffset<8>(), 0,
-                              mr_conv->InterproceduralScratchRegister());
   } else {
     __ StoreStackPointerToThread32(Thread::TopOfManagedStackOffset<4>());
-    __ StoreImmediateToThread32(Thread::TopOfManagedStackPcOffset<4>(), 0,
-                              mr_conv->InterproceduralScratchRegister());
   }
 
   // 5. Move frame down to allow space for out going args.
@@ -425,19 +424,17 @@
   // 17. Finalize code generation
   __ EmitSlowPaths();
   size_t cs = __ CodeSize();
-  if (instruction_set == kArm64) {
-    // Test that we do not exceed the buffer size.
-    CHECK(cs < arm64::kBufferSizeArm64);
-  }
   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 +540,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/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
index 525f05c..a100552 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
@@ -38,6 +38,7 @@
 }
 
 static ManagedRegister ReturnRegisterForShorty(const char* shorty, bool jni) {
+  UNUSED(jni);
   if (shorty[0] == 'F' || shorty[0] == 'D') {
     return X86_64ManagedRegister::FromXmmRegister(XMM0);
   } else if (shorty[0] == 'J') {
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..fa93e00
--- /dev/null
+++ b/compiler/llvm/llvm_compiler.cc
@@ -0,0 +1,163 @@
+/*
+ * 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"
+
+#include "base/macros.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
+  UNUSED(driver);
+  return nullptr;
+#endif
+}
+
+}  // namespace art
diff --git a/runtime/log_severity.h b/compiler/llvm/llvm_compiler.h
similarity index 63%
copy from runtime/log_severity.h
copy to compiler/llvm/llvm_compiler.h
index 31682df..da6d0e9 100644
--- a/runtime/log_severity.h
+++ b/compiler/llvm/llvm_compiler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_COMPILER_LLVM_LLVM_COMPILER_H_
+#define ART_COMPILER_LLVM_LLVM_COMPILER_H_
 
-typedef int LogSeverity;
+namespace art {
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+class Compiler;
+class CompilerDriver;
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+Compiler* CreateLLVMCompiler(CompilerDriver* driver);
+
+}
+
+#endif  // ART_COMPILER_LLVM_LLVM_COMPILER_H_
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 7c1f6c5..97b7cc9 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"
@@ -43,11 +44,11 @@
         compiler_driver_->GetCompiledMethod(MethodReference(dex_file,
                                                             method->GetDexMethodIndex()));
 
-    if (compiled_method == NULL) {
-      EXPECT_TRUE(oat_method.GetQuickCode() == NULL) << PrettyMethod(method) << " "
-                                                     << oat_method.GetQuickCode();
-      EXPECT_TRUE(oat_method.GetPortableCode() == NULL) << PrettyMethod(method) << " "
-                                                        << oat_method.GetPortableCode();
+    if (compiled_method == nullptr) {
+      EXPECT_TRUE(oat_method.GetQuickCode() == nullptr) << PrettyMethod(method) << " "
+                                                        << oat_method.GetQuickCode();
+      EXPECT_TRUE(oat_method.GetPortableCode() == nullptr) << PrettyMethod(method) << " "
+                                                           << oat_method.GetPortableCode();
       EXPECT_EQ(oat_method.GetFrameSizeInBytes(), 0U);
       EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U);
       EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
@@ -94,7 +95,10 @@
       : Compiler::kQuick;
   InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
 
-  InstructionSetFeatures insn_features;
+  std::string error_msg;
+  std::unique_ptr<const InstructionSetFeatures> insn_features(
+      InstructionSetFeatures::FromFeatureString(insn_set, "default", &error_msg));
+  ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
   compiler_options_.reset(new CompilerOptions);
   verification_results_.reset(new VerificationResults(compiler_options_.get()));
   method_inliner_map_.reset(new DexFileToMethodInlinerMap);
@@ -105,15 +109,14 @@
                                             verification_results_.get(),
                                             method_inliner_map_.get(),
                                             compiler_kind, insn_set,
-                                            insn_features, false, NULL, 2, true, true,
-                                            timer_.get()));
-  jobject class_loader = NULL;
+                                            insn_features.get(), false, nullptr, 2, true, true,
+                                            timer_.get(), ""));
+  jobject class_loader = nullptr;
   if (kCompile) {
-    TimingLogger timings("OatTest::WriteRead", false, false);
-    compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
+    TimingLogger timings2("OatTest::WriteRead", false, false);
+    compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
   }
 
-  ScopedObjectAccess soa(Thread::Current());
   ScratchFile tmp;
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
@@ -122,6 +125,7 @@
                        4096U,
                        0,
                        compiler_driver_.get(),
+                       nullptr,
                        &timings,
                        &key_value_store);
   bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(),
@@ -134,7 +138,6 @@
   if (kCompile) {  // OatWriter strips the code, regenerate to compare
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
   }
-  std::string error_msg;
   std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
                                                   nullptr, false, &error_msg));
   ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
@@ -151,11 +154,12 @@
                                                                     &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);
+    const uint8_t* class_data = dex_file->GetClassData(class_def);
     size_t num_virtual_methods = 0;
-    if (class_data != NULL) {
+    if (class_data != nullptr) {
       ClassDataItemIterator it(*dex_file, class_data);
       num_virtual_methods = it.NumVirtualMethods();
     }
@@ -170,12 +174,12 @@
              oat_class.GetType()) << descriptor;
 
     size_t method_index = 0;
-    for (size_t i = 0; i < klass->NumDirectMethods(); i++, method_index++) {
-      CheckMethod(klass->GetDirectMethod(i),
+    for (size_t j = 0; j < klass->NumDirectMethods(); j++, method_index++) {
+      CheckMethod(klass->GetDirectMethod(j),
                   oat_class.GetOatMethod(method_index), dex_file);
     }
-    for (size_t i = 0; i < num_virtual_methods; i++, method_index++) {
-      CheckMethod(klass->GetVirtualMethod(i),
+    for (size_t j = 0; j < num_virtual_methods; j++, method_index++) {
+      CheckMethod(klass->GetVirtualMethod(j),
                   oat_class.GetOatMethod(method_index), dex_file);
     }
   }
@@ -187,22 +191,25 @@
   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) {
-    InstructionSet instruction_set = kX86;
-    InstructionSetFeatures instruction_set_features;
+    InstructionSet insn_set = kX86;
+    std::string error_msg;
+    std::unique_ptr<const InstructionSetFeatures> insn_features(
+        InstructionSetFeatures::FromFeatureString(insn_set, "default", &error_msg));
+    ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
     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(insn_set,
+                                                            insn_features.get(),
+                                                            &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..659c332 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,349 @@
 #include "safe_map.h"
 #include "scoped_thread_state_change.h"
 #include "handle_scope-inl.h"
+#include "utils/arm/assembler_thumb2.h"
+#include "utils/arm64/assembler_arm64.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 ATTRIBUTE_UNUSED) OVERRIDE {
+    return offset;  // No space reserved; no patches expected.
+  }
+
+  uint32_t WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) OVERRIDE {
+    return offset;  // No thunks added; no patches expected.
+  }
+
+  void Patch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, uint32_t literal_offset ATTRIBUTE_UNUSED,
+             uint32_t patch_offset ATTRIBUTE_UNUSED,
+             uint32_t target_offset ATTRIBUTE_UNUSED) 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 ATTRIBUTE_UNUSED) OVERRIDE {
+    return offset;  // No space reserved; no limit on relative call distance.
+  }
+
+  uint32_t WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, 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::ArmBaseRelativeCallPatcher : public RelativeCallPatcher {
+ public:
+  ArmBaseRelativeCallPatcher(OatWriter* writer,
+                             InstructionSet instruction_set, std::vector<uint8_t> thunk_code,
+                             uint32_t max_positive_displacement, uint32_t max_negative_displacement)
+      : writer_(writer), instruction_set_(instruction_set), thunk_code_(thunk_code),
+        max_positive_displacement_(max_positive_displacement),
+        max_negative_displacement_(max_negative_displacement),
+        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, instruction_set_);
+      bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset);
+      if (needs_thunk) {
+        thunk_locations_.push_back(aligned_offset);
+        offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_);
+      }
+      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 > max_positive_displacement_) {
+      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(), instruction_set_);
+      }
+    }
+    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, instruction_set_);
+    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, instruction_set_);
+      aligned_code_delta = offset - thunk_end_offset;
+      if (aligned_code_delta != 0u && !writer_->WriteCodeAlignment(out, aligned_code_delta)) {
+        return 0u;
+      }
+    }
+    return offset;
+  }
+
+ protected:
+  uint32_t CalculateDisplacement(uint32_t patch_offset, uint32_t target_offset) {
+    // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
+    uint32_t displacement = target_offset - patch_offset;
+    // NOTE: With unsigned arithmetic we do mean to use && rather than || below.
+    if (displacement > max_positive_displacement_ && displacement < -max_negative_displacement_) {
+      // 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 < max_positive_displacement_) {
+        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 >= -max_negative_displacement_);
+      }
+    }
+    return displacement;
+  }
+
+ 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() > max_negative_displacement_) {
+          return next_aligned_offset - patch_offset > max_positive_displacement_;
+        }
+      } else if (it->second >= patch_offset) {
+        DCHECK_LE(it->second - patch_offset, max_positive_displacement_);
+      } 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 > max_negative_displacement_) {
+          return true;
+        }
+      }
+      unprocessed_patches_.pop_front();
+    }
+    return false;
+  }
+
+  OatWriter* const writer_;
+  const InstructionSet instruction_set_;
+  const std::vector<uint8_t> thunk_code_;
+  const uint32_t max_positive_displacement_;
+  const uint32_t max_negative_displacement_;
+  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(ArmBaseRelativeCallPatcher);
+};
+
+class OatWriter::Thumb2RelativeCallPatcher FINAL : public ArmBaseRelativeCallPatcher {
+ public:
+  explicit Thumb2RelativeCallPatcher(OatWriter* writer)
+      : ArmBaseRelativeCallPatcher(writer, kThumb2, CompileThunkCode(),
+                                   kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
+  }
+
+  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.
+    uint32_t displacement = CalculateDisplacement(patch_offset, target_offset & ~1u);
+    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:
+  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;
+
+  DISALLOW_COPY_AND_ASSIGN(Thumb2RelativeCallPatcher);
+};
+
+class OatWriter::Arm64RelativeCallPatcher FINAL : public ArmBaseRelativeCallPatcher {
+ public:
+  explicit Arm64RelativeCallPatcher(OatWriter* writer)
+      : ArmBaseRelativeCallPatcher(writer, kArm64, CompileThunkCode(),
+                                   kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
+  }
+
+  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 & 3u, 0u);
+    DCHECK_EQ(patch_offset & 3u, 0u);
+    DCHECK_EQ(target_offset & 3u, 0u);
+    uint32_t displacement = CalculateDisplacement(patch_offset, target_offset & ~1u);
+    DCHECK_EQ(displacement & 3u, 0u);
+    DCHECK((displacement >> 27) == 0u || (displacement >> 27) == 31u);  // 28-bit signed.
+    uint32_t value = (displacement & 0x0fffffffu) >> 2;
+    value |= 0x94000000;  // BL
+
+    uint8_t* addr = &(*code)[literal_offset];
+    // Check that we're just overwriting an existing BL.
+    DCHECK_EQ(addr[3] & 0xfc, 0x94);
+    // Write the new BL.
+    addr[0] = (value >> 0) & 0xff;
+    addr[1] = (value >> 8) & 0xff;
+    addr[2] = (value >> 16) & 0xff;
+    addr[3] = (value >> 24) & 0xff;
+  }
+
+ private:
+  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.
+    arm64::Arm64Assembler assembler;
+    Offset offset(mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+    assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0));
+    std::vector<uint8_t> thunk_code(assembler.CodeSize());
+    MemoryRegion code(thunk_code.data(), thunk_code.size());
+    assembler.FinalizeInstructions(code);
+    return thunk_code;
+  }
+
+  // Maximum positive and negative displacement measured from the patch location.
+  // (Signed 28 bit displacement with the last bit 0 has range [-2^27, 2^27-4] measured from
+  // the ARM64 PC pointing to the BL.)
+  static constexpr uint32_t kMaxPositiveDisplacement = (1u << 27) - 4u;
+  static constexpr uint32_t kMaxNegativeDisplacement = (1u << 27);
+
+  DISALLOW_COPY_AND_ASSIGN(Arm64RelativeCallPatcher);
+};
+
 #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 +393,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 +425,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 +437,28 @@
     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:
+      relative_call_patcher_.reset(new Arm64RelativeCallPatcher(this));
+      break;
+    default:
+      relative_call_patcher_.reset(new NoRelativeCallPatcher);
+      break;
+  }
+
   size_t offset;
   {
     TimingLogger::ScopedTiming split("InitOatHeader", timings);
@@ -126,6 +491,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 +505,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 {
@@ -285,7 +651,7 @@
     return true;
   }
 
-  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+  bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, const ClassDataItemIterator& it) {
     // Fill in the compiled_methods_ array for methods that have a
     // CompiledMethod. We track the number of non-null entries in
     // num_non_null_compiled_methods_ since we only want to allocate
@@ -315,6 +681,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 +695,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 +726,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 +735,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 +746,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 +781,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 +830,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());
@@ -488,7 +863,7 @@
     : OatDexMethodVisitor(writer, offset) {
   }
 
-  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
+  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it ATTRIBUTE_UNUSED)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
     CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
@@ -498,7 +873,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 +925,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 +944,48 @@
 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),
+      soa_(Thread::Current()),
+      no_thread_suspension_(soa_.Self(), "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);
+    }
   }
 
-  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+  ~WriteCodeMethodVisitor() UNLOCK_FUNCTION(Locks::mutator_lock_) {
+  }
+
+  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 +996,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 +1022,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 +1032,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;
@@ -630,12 +1074,82 @@
 
  private:
   OutputStream* const out_;
-  size_t const file_offset_;
+  const size_t file_offset_;
+  const ScopedObjectAccess soa_;
+  const ScopedAssertNoThreadSuspension no_thread_suspension_;
+  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 +1175,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_);
@@ -697,7 +1211,7 @@
         return false;
       }
       const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
-      const byte* class_data = dex_file->GetClassData(class_def);
+      const uint8_t* class_data = dex_file->GetClassData(class_def);
       if (class_data != NULL) {  // ie not an empty class, such as a marker interface
         ClassDataItemIterator it(*dex_file, class_data);
         while (it.HasNextStaticField()) {
@@ -780,9 +1294,9 @@
   // Update oat_dex_files_.
   auto oat_class_it = oat_classes_.begin();
   for (OatDexFile* oat_dex_file : oat_dex_files_) {
-    for (uint32_t& offset : oat_dex_file->methods_offsets_) {
+    for (uint32_t& method_offset : oat_dex_file->methods_offsets_) {
       DCHECK(oat_class_it != oat_classes_.end());
-      offset = (*oat_class_it)->offset_;
+      method_offset = (*oat_class_it)->offset_;
       ++oat_class_it;
     }
     oat_dex_file->UpdateChecksum(oat_header_);
@@ -877,11 +1391,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 +1412,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 +1436,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 +1467,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 +1483,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 +1632,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..5b61f21 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);
@@ -214,10 +231,10 @@
 
     // data to write
 
-    COMPILE_ASSERT(mirror::Class::Status::kStatusMax < (2 ^ 16), class_status_wont_fit_in_16bits);
+    static_assert(mirror::Class::Status::kStatusMax < (2 ^ 16), "class status won't fit in 16bits");
     int16_t status_;
 
-    COMPILE_ASSERT(OatClassType::kOatClassMax < (2 ^ 16), oat_class_type_wont_fit_in_16bits);
+    static_assert(OatClassType::kOatClassMax < (2 ^ 16), "oat_class type won't fit in 16bits");
     uint16_t type_;
 
     uint32_t method_bitmap_size_;
@@ -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,20 @@
   uint32_t size_oat_class_method_bitmaps_;
   uint32_t size_oat_class_method_offsets_;
 
+  class RelativeCallPatcher;
+  class NoRelativeCallPatcher;
+  class X86RelativeCallPatcher;
+  class ArmBaseRelativeCallPatcher;
+  class Thumb2RelativeCallPatcher;
+  class Arm64RelativeCallPatcher;
+
+  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 +353,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..8418ab0 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -1,5 +1,4 @@
 /*
- *
  * Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -65,10 +64,6 @@
   size_t index_;
 };
 
-static bool IsTypeSupported(Primitive::Type type) {
-  return type != Primitive::kPrimFloat && type != Primitive::kPrimDouble;
-}
-
 void HGraphBuilder::InitializeLocals(uint16_t count) {
   graph_->SetNumberOfVRegs(count);
   locals_.SetSize(count);
@@ -79,10 +74,10 @@
   }
 }
 
-bool HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) {
+void HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) {
   // dex_compilation_unit_ is null only when unit testing.
   if (dex_compilation_unit_ == nullptr) {
-    return true;
+    return;
   }
 
   graph_->SetNumberOfInVRegs(number_of_parameters);
@@ -102,49 +97,34 @@
 
   uint32_t pos = 1;
   for (int i = 0; i < number_of_parameters; i++) {
-    switch (shorty[pos++]) {
-      case 'F':
-      case 'D': {
-        return false;
-      }
-
-      default: {
-        // integer and reference parameters.
-        HParameterValue* parameter =
-            new (arena_) HParameterValue(parameter_index++, Primitive::GetType(shorty[pos - 1]));
-        entry_block_->AddInstruction(parameter);
-        HLocal* local = GetLocalAt(locals_index++);
-        // Store the parameter value in the local that the dex code will use
-        // to reference that parameter.
-        entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter));
-        if (parameter->GetType() == Primitive::kPrimLong) {
-          i++;
-          locals_index++;
-          parameter_index++;
-        }
-        break;
-      }
+    HParameterValue* parameter =
+        new (arena_) HParameterValue(parameter_index++, Primitive::GetType(shorty[pos++]));
+    entry_block_->AddInstruction(parameter);
+    HLocal* local = GetLocalAt(locals_index++);
+    // Store the parameter value in the local that the dex code will use
+    // to reference that parameter.
+    entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter));
+    bool is_wide = (parameter->GetType() == Primitive::kPrimLong)
+        || (parameter->GetType() == Primitive::kPrimDouble);
+    if (is_wide) {
+      i++;
+      locals_index++;
+      parameter_index++;
     }
   }
-  return true;
-}
-
-static bool CanHandleCodeItem(const DexFile::CodeItem& code_item) {
-  if (code_item.tries_size_ > 0) {
-    return false;
-  }
-  return true;
 }
 
 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 +135,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());
@@ -170,18 +152,15 @@
 }
 
 HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) {
-  if (!CanHandleCodeItem(code_item)) {
-    return nullptr;
-  }
-
   const uint16_t* code_ptr = code_item.insns_;
   const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
+  code_start_ = code_ptr;
 
   // 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_);
 
@@ -192,10 +171,27 @@
   // start a new block, and create these blocks.
   ComputeBranchTargets(code_ptr, code_end);
 
-  if (!InitializeParameters(code_item.ins_size_)) {
-    return nullptr;
+  // Also create blocks for catch handlers.
+  if (code_item.tries_size_ != 0) {
+    const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(code_item, 0);
+    uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+    for (uint32_t idx = 0; idx < handlers_size; ++idx) {
+      CatchHandlerIterator iterator(handlers_ptr);
+      for (; iterator.HasNext(); iterator.Next()) {
+        uint32_t address = iterator.GetHandlerAddress();
+        HBasicBlock* block = FindBlockStartingAt(address);
+        if (block == nullptr) {
+          block = new (arena_) HBasicBlock(graph_, address);
+          branch_targets_.Put(address, block);
+        }
+        block->SetIsCatchBlock();
+      }
+      handlers_ptr = iterator.EndDataPointer();
+    }
   }
 
+  InitializeParameters(code_item.ins_size_);
+
   size_t dex_offset = 0;
   while (code_ptr < code_end) {
     // Update the current block if dex_offset starts a new block.
@@ -209,6 +205,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 +233,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 +246,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 {
@@ -270,6 +268,21 @@
 }
 
 template<typename T>
+void HGraphBuilder::Unop_12x(const Instruction& instruction, Primitive::Type type) {
+  HInstruction* first = LoadLocal(instruction.VRegB(), type);
+  current_block_->AddInstruction(new (arena_) T(type, first));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+void HGraphBuilder::Conversion_12x(const Instruction& instruction,
+                                   Primitive::Type input_type,
+                                   Primitive::Type result_type) {
+  HInstruction* first = LoadLocal(instruction.VRegB(), input_type);
+  current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
 void HGraphBuilder::Binop_23x(const Instruction& instruction, Primitive::Type type) {
   HInstruction* first = LoadLocal(instruction.VRegB(), type);
   HInstruction* second = LoadLocal(instruction.VRegC(), type);
@@ -325,18 +338,69 @@
                                 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 || invoke_type == kInterface) {
+    MethodReference target_method(dex_file_, method_idx);
+    uintptr_t direct_code;
+    uintptr_t direct_method;
+    int table_index;
+    InvokeType optimized_invoke_type = invoke_type;
+    // TODO: Add devirtualization support.
+    compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_offset, true, true,
+                                        &optimized_invoke_type, &target_method, &table_index,
+                                        &direct_code, &direct_method);
+    if (table_index == -1) {
+      return false;
+    }
+
+    if (invoke_type == kVirtual) {
+      invoke = new (arena_) HInvokeVirtual(
+          arena_, number_of_arguments, return_type, dex_offset, table_index);
+    } else {
+      DCHECK_EQ(invoke_type, kInterface);
+      invoke = new (arena_) HInvokeInterface(
+          arena_, number_of_arguments, return_type, dex_offset, method_idx, table_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);
@@ -353,10 +417,8 @@
   uint32_t argument_index = start_index;
   for (size_t i = start_index; i < number_of_vreg_arguments; i++, argument_index++) {
     Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]);
-    if (!IsTypeSupported(type)) {
-      return false;
-    }
-    if (!is_range && type == Primitive::kPrimLong && args[i] + 1 != args[i + 1]) {
+    bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble);
+    if (!is_range && is_wide && args[i] + 1 != args[i + 1]) {
       LOG(WARNING) << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol()
                    << " at " << dex_offset;
       // We do not implement non sequential register pair.
@@ -364,23 +426,20 @@
     }
     HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
     invoke->SetArgumentAt(argument_index, arg);
-    if (type == Primitive::kPrimLong) {
+    if (is_wide) {
       i++;
     }
   }
 
-  if (!IsTypeSupported(return_type)) {
-    return false;
-  }
-
   DCHECK_EQ(argument_index, number_of_arguments);
   current_block_->AddInstruction(invoke);
+  latest_result_ = invoke;
   return true;
 }
 
-bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction,
-                                     uint32_t dex_offset,
-                                     bool is_put) {
+bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
+                                             uint32_t dex_offset,
+                                             bool is_put) {
   uint32_t source_or_dest_reg = instruction.VRegA_22c();
   uint32_t obj_reg = instruction.VRegB_22c();
   uint16_t field_index = instruction.VRegC_22c();
@@ -398,9 +457,6 @@
   }
 
   Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
-  if (!IsTypeSupported(field_type)) {
-    return false;
-  }
 
   HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot);
   current_block_->AddInstruction(new (arena_) HNullCheck(object, dex_offset));
@@ -413,6 +469,7 @@
     current_block_->AddInstruction(new (arena_) HInstanceFieldSet(
         null_check,
         value,
+        field_type,
         resolved_field->GetOffset()));
   } else {
     current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
@@ -425,6 +482,84 @@
   return true;
 }
 
+
+
+bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
+                                           uint32_t dex_offset,
+                                           bool is_put) {
+  uint32_t source_or_dest_reg = instruction.VRegA_21c();
+  uint16_t field_index = instruction.VRegB_21c();
+
+  uint32_t storage_index;
+  bool is_referrers_class;
+  bool is_initialized;
+  bool is_volatile;
+  MemberOffset field_offset(0u);
+  Primitive::Type field_type;
+
+  bool fast_path = compiler_driver_->ComputeStaticFieldInfo(field_index,
+                                                            dex_compilation_unit_,
+                                                            is_put,
+                                                            &field_offset,
+                                                            &storage_index,
+                                                            &is_referrers_class,
+                                                            &is_volatile,
+                                                            &is_initialized,
+                                                            &field_type);
+  if (!fast_path) {
+    return false;
+  }
+
+  if (is_volatile) {
+    return false;
+  }
+
+  HLoadClass* constant = new (arena_) HLoadClass(
+      storage_index, is_referrers_class, dex_offset);
+  current_block_->AddInstruction(constant);
+
+  HInstruction* cls = constant;
+  if (!is_initialized) {
+    cls = new (arena_) HClinitCheck(constant, dex_offset);
+    current_block_->AddInstruction(cls);
+  }
+
+  if (is_put) {
+    // We need to keep the class alive before loading the value.
+    Temporaries temps(graph_, 1);
+    temps.Add(cls);
+    HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
+    DCHECK_EQ(value->GetType(), field_type);
+    current_block_->AddInstruction(
+        new (arena_) HStaticFieldSet(cls, value, field_type, field_offset));
+  } else {
+    current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls, field_type, field_offset));
+    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+  }
+  return true;
+}
+
+void HGraphBuilder::BuildCheckedDiv(uint16_t out_reg,
+                                    uint16_t first_reg,
+                                    int32_t second_reg,
+                                    uint32_t dex_offset,
+                                    Primitive::Type type,
+                                    bool second_is_lit) {
+  DCHECK(type == Primitive::kPrimInt);
+
+  HInstruction* first = LoadLocal(first_reg, type);
+  HInstruction* second = second_is_lit ? GetIntConstant(second_reg) : LoadLocal(second_reg, type);
+  if (!second->IsIntConstant() || (second->AsIntConstant()->GetValue() == 0)) {
+    second = new (arena_) HDivZeroCheck(second, dex_offset);
+    Temporaries temps(graph_, 1);
+    current_block_->AddInstruction(second);
+    temps.Add(current_block_->GetLastInstruction());
+  }
+
+  current_block_->AddInstruction(new (arena_) HDiv(type, first, second));
+  UpdateLocal(out_reg, current_block_->GetLastInstruction());
+}
+
 void HGraphBuilder::BuildArrayAccess(const Instruction& instruction,
                                      uint32_t dex_offset,
                                      bool is_put,
@@ -433,8 +568,6 @@
   uint8_t array_reg = instruction.VRegB_23x();
   uint8_t index_reg = instruction.VRegC_23x();
 
-  DCHECK(IsTypeSupported(anticipated_type));
-
   // We need one temporary for the null check, one for the index, and one for the length.
   Temporaries temps(graph_, 3);
 
@@ -453,14 +586,133 @@
   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::BuildFilledNewArray(uint32_t dex_offset,
+                                        uint32_t type_index,
+                                        uint32_t number_of_vreg_arguments,
+                                        bool is_range,
+                                        uint32_t* args,
+                                        uint32_t register_index) {
+  HInstruction* length = GetIntConstant(number_of_vreg_arguments);
+  HInstruction* object = new (arena_) HNewArray(length, dex_offset, type_index);
+  current_block_->AddInstruction(object);
+
+  const char* descriptor = dex_file_->StringByTypeIdx(type_index);
+  DCHECK_EQ(descriptor[0], '[') << descriptor;
+  char primitive = descriptor[1];
+  DCHECK(primitive == 'I'
+      || primitive == 'L'
+      || primitive == '[') << descriptor;
+  bool is_reference_array = (primitive == 'L') || (primitive == '[');
+  Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt;
+
+  Temporaries temps(graph_, 1);
+  temps.Add(object);
+  for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
+    HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type);
+    HInstruction* index = GetIntConstant(i);
+    current_block_->AddInstruction(
+        new (arena_) HArraySet(object, index, value, type, dex_offset));
+  }
+  latest_result_ = object;
+}
+
+template <typename T>
+void HGraphBuilder::BuildFillArrayData(HInstruction* object,
+                                       const T* data,
+                                       uint32_t element_count,
+                                       Primitive::Type anticipated_type,
+                                       uint32_t dex_offset) {
+  for (uint32_t i = 0; i < element_count; ++i) {
+    HInstruction* index = GetIntConstant(i);
+    HInstruction* value = GetIntConstant(data[i]);
+    current_block_->AddInstruction(new (arena_) HArraySet(
+      object, index, value, anticipated_type, dex_offset));
+  }
+}
+
+void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_offset) {
+  Temporaries temps(graph_, 1);
+  HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot);
+  HNullCheck* null_check = new (arena_) HNullCheck(array, dex_offset);
+  current_block_->AddInstruction(null_check);
+  temps.Add(null_check);
+
+  HInstruction* length = new (arena_) HArrayLength(null_check);
+  current_block_->AddInstruction(length);
+
+  int32_t payload_offset = instruction.VRegB_31t() + dex_offset;
+  const Instruction::ArrayDataPayload* payload =
+      reinterpret_cast<const Instruction::ArrayDataPayload*>(code_start_ + payload_offset);
+  const uint8_t* data = payload->data;
+  uint32_t element_count = payload->element_count;
+
+  // Implementation of this DEX instruction seems to be that the bounds check is
+  // done before doing any stores.
+  HInstruction* last_index = GetIntConstant(payload->element_count - 1);
+  current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_offset));
+
+  switch (payload->element_width) {
+    case 1:
+      BuildFillArrayData(null_check,
+                         reinterpret_cast<const int8_t*>(data),
+                         element_count,
+                         Primitive::kPrimByte,
+                         dex_offset);
+      break;
+    case 2:
+      BuildFillArrayData(null_check,
+                         reinterpret_cast<const int16_t*>(data),
+                         element_count,
+                         Primitive::kPrimShort,
+                         dex_offset);
+      break;
+    case 4:
+      BuildFillArrayData(null_check,
+                         reinterpret_cast<const int32_t*>(data),
+                         element_count,
+                         Primitive::kPrimInt,
+                         dex_offset);
+      break;
+    case 8:
+      BuildFillWideArrayData(null_check,
+                             reinterpret_cast<const int64_t*>(data),
+                             element_count,
+                             dex_offset);
+      break;
+    default:
+      LOG(FATAL) << "Unknown element width for " << payload->element_width;
+  }
+}
+
+void HGraphBuilder::BuildFillWideArrayData(HInstruction* object,
+                                           const int64_t* data,
+                                           uint32_t element_count,
+                                           uint32_t dex_offset) {
+  for (uint32_t i = 0; i < element_count; ++i) {
+    HInstruction* index = GetIntConstant(i);
+    HInstruction* value = GetLongConstant(data[i]);
+    current_block_->AddInstruction(new (arena_) HArraySet(
+      object, index, value, Primitive::kPrimLong, 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
   }
@@ -531,8 +783,7 @@
       break;
     }
 
-    // TODO: these instructions are also used to move floating point values, so what is
-    // the type (int or float)?
+    // Note that the SSA building will refine the types.
     case Instruction::MOVE:
     case Instruction::MOVE_FROM16:
     case Instruction::MOVE_16: {
@@ -541,8 +792,7 @@
       break;
     }
 
-    // TODO: these instructions are also used to move floating point values, so what is
-    // the type (long or double)?
+    // Note that the SSA building will refine the types.
     case Instruction::MOVE_WIDE:
     case Instruction::MOVE_WIDE_FROM16:
     case Instruction::MOVE_WIDE_16: {
@@ -578,7 +828,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);
@@ -587,34 +839,44 @@
     }
 
     case Instruction::RETURN: {
-      BuildReturn(instruction, Primitive::kPrimInt);
+      DCHECK_NE(return_type_, Primitive::kPrimNot);
+      DCHECK_NE(return_type_, Primitive::kPrimLong);
+      DCHECK_NE(return_type_, Primitive::kPrimDouble);
+      BuildReturn(instruction, return_type_);
       break;
     }
 
     case Instruction::RETURN_OBJECT: {
-      BuildReturn(instruction, Primitive::kPrimNot);
+      DCHECK(return_type_ == Primitive::kPrimNot);
+      BuildReturn(instruction, return_type_);
       break;
     }
 
     case Instruction::RETURN_WIDE: {
-      BuildReturn(instruction, Primitive::kPrimLong);
+      DCHECK(return_type_ == Primitive::kPrimDouble || return_type_ == Primitive::kPrimLong);
+      BuildReturn(instruction, return_type_);
       break;
     }
 
     case Instruction::INVOKE_STATIC:
-    case Instruction::INVOKE_DIRECT: {
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_VIRTUAL:
+    case Instruction::INVOKE_INTERFACE: {
       uint32_t method_idx = instruction.VRegB_35c();
       uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
       uint32_t args[5];
       instruction.GetVarArgs(args);
-      if (!BuildInvoke(instruction, dex_offset, method_idx, number_of_vreg_arguments, false, args, -1)) {
+      if (!BuildInvoke(instruction, dex_offset, method_idx,
+                       number_of_vreg_arguments, false, args, -1)) {
         return false;
       }
       break;
     }
 
     case Instruction::INVOKE_STATIC_RANGE:
-    case Instruction::INVOKE_DIRECT_RANGE: {
+    case Instruction::INVOKE_DIRECT_RANGE:
+    case Instruction::INVOKE_VIRTUAL_RANGE:
+    case Instruction::INVOKE_INTERFACE_RANGE: {
       uint32_t method_idx = instruction.VRegB_3rc();
       uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
       uint32_t register_index = instruction.VRegC();
@@ -625,6 +887,41 @@
       break;
     }
 
+    case Instruction::NEG_INT: {
+      Unop_12x<HNeg>(instruction, Primitive::kPrimInt);
+      break;
+    }
+
+    case Instruction::NEG_LONG: {
+      Unop_12x<HNeg>(instruction, Primitive::kPrimLong);
+      break;
+    }
+
+    case Instruction::NEG_FLOAT: {
+      Unop_12x<HNeg>(instruction, Primitive::kPrimFloat);
+      break;
+    }
+
+    case Instruction::NEG_DOUBLE: {
+      Unop_12x<HNeg>(instruction, Primitive::kPrimDouble);
+      break;
+    }
+
+    case Instruction::NOT_INT: {
+      Unop_12x<HNot>(instruction, Primitive::kPrimInt);
+      break;
+    }
+
+    case Instruction::NOT_LONG: {
+      Unop_12x<HNot>(instruction, Primitive::kPrimLong);
+      break;
+    }
+
+    case Instruction::INT_TO_LONG: {
+      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong);
+      break;
+    }
+
     case Instruction::ADD_INT: {
       Binop_23x<HAdd>(instruction, Primitive::kPrimInt);
       break;
@@ -635,6 +932,16 @@
       break;
     }
 
+    case Instruction::ADD_DOUBLE: {
+      Binop_23x<HAdd>(instruction, Primitive::kPrimDouble);
+      break;
+    }
+
+    case Instruction::ADD_FLOAT: {
+      Binop_23x<HAdd>(instruction, Primitive::kPrimFloat);
+      break;
+    }
+
     case Instruction::SUB_INT: {
       Binop_23x<HSub>(instruction, Primitive::kPrimInt);
       break;
@@ -645,16 +952,72 @@
       break;
     }
 
+    case Instruction::SUB_FLOAT: {
+      Binop_23x<HSub>(instruction, Primitive::kPrimFloat);
+      break;
+    }
+
+    case Instruction::SUB_DOUBLE: {
+      Binop_23x<HSub>(instruction, Primitive::kPrimDouble);
+      break;
+    }
+
     case Instruction::ADD_INT_2ADDR: {
       Binop_12x<HAdd>(instruction, Primitive::kPrimInt);
       break;
     }
 
+    case Instruction::MUL_INT: {
+      Binop_23x<HMul>(instruction, Primitive::kPrimInt);
+      break;
+    }
+
+    case Instruction::MUL_LONG: {
+      Binop_23x<HMul>(instruction, Primitive::kPrimLong);
+      break;
+    }
+
+    case Instruction::MUL_FLOAT: {
+      Binop_23x<HMul>(instruction, Primitive::kPrimFloat);
+      break;
+    }
+
+    case Instruction::MUL_DOUBLE: {
+      Binop_23x<HMul>(instruction, Primitive::kPrimDouble);
+      break;
+    }
+
+    case Instruction::DIV_INT: {
+      BuildCheckedDiv(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+                      dex_offset, Primitive::kPrimInt, false);
+      break;
+    }
+
+    case Instruction::DIV_FLOAT: {
+      Binop_23x<HDiv>(instruction, Primitive::kPrimFloat);
+      break;
+    }
+
+    case Instruction::DIV_DOUBLE: {
+      Binop_23x<HDiv>(instruction, Primitive::kPrimDouble);
+      break;
+    }
+
     case Instruction::ADD_LONG_2ADDR: {
       Binop_12x<HAdd>(instruction, Primitive::kPrimLong);
       break;
     }
 
+    case Instruction::ADD_DOUBLE_2ADDR: {
+      Binop_12x<HAdd>(instruction, Primitive::kPrimDouble);
+      break;
+    }
+
+    case Instruction::ADD_FLOAT_2ADDR: {
+      Binop_12x<HAdd>(instruction, Primitive::kPrimFloat);
+      break;
+    }
+
     case Instruction::SUB_INT_2ADDR: {
       Binop_12x<HSub>(instruction, Primitive::kPrimInt);
       break;
@@ -665,6 +1028,52 @@
       break;
     }
 
+    case Instruction::SUB_FLOAT_2ADDR: {
+      Binop_12x<HSub>(instruction, Primitive::kPrimFloat);
+      break;
+    }
+
+    case Instruction::SUB_DOUBLE_2ADDR: {
+      Binop_12x<HSub>(instruction, Primitive::kPrimDouble);
+      break;
+    }
+
+    case Instruction::MUL_INT_2ADDR: {
+      Binop_12x<HMul>(instruction, Primitive::kPrimInt);
+      break;
+    }
+
+    case Instruction::MUL_LONG_2ADDR: {
+      Binop_12x<HMul>(instruction, Primitive::kPrimLong);
+      break;
+    }
+
+    case Instruction::MUL_FLOAT_2ADDR: {
+      Binop_12x<HMul>(instruction, Primitive::kPrimFloat);
+      break;
+    }
+
+    case Instruction::MUL_DOUBLE_2ADDR: {
+      Binop_12x<HMul>(instruction, Primitive::kPrimDouble);
+      break;
+    }
+
+    case Instruction::DIV_INT_2ADDR: {
+      BuildCheckedDiv(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+                      dex_offset, Primitive::kPrimInt, false);
+      break;
+    }
+
+    case Instruction::DIV_FLOAT_2ADDR: {
+      Binop_12x<HDiv>(instruction, Primitive::kPrimFloat);
+      break;
+    }
+
+    case Instruction::DIV_DOUBLE_2ADDR: {
+      Binop_12x<HDiv>(instruction, Primitive::kPrimDouble);
+      break;
+    }
+
     case Instruction::ADD_INT_LIT16: {
       Binop_22s<HAdd>(instruction, false);
       break;
@@ -675,6 +1084,11 @@
       break;
     }
 
+    case Instruction::MUL_INT_LIT16: {
+      Binop_22s<HMul>(instruction, false);
+      break;
+    }
+
     case Instruction::ADD_INT_LIT8: {
       Binop_22b<HAdd>(instruction, false);
       break;
@@ -685,6 +1099,18 @@
       break;
     }
 
+    case Instruction::MUL_INT_LIT8: {
+      Binop_22b<HMul>(instruction, false);
+      break;
+    }
+
+    case Instruction::DIV_INT_LIT16:
+    case Instruction::DIV_INT_LIT8: {
+      BuildCheckedDiv(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+                      dex_offset, Primitive::kPrimInt, true);
+      break;
+    }
+
     case Instruction::NEW_INSTANCE: {
       current_block_->AddInstruction(
           new (arena_) HNewInstance(dex_offset, instruction.VRegB_21c()));
@@ -692,10 +1118,42 @@
       break;
     }
 
+    case Instruction::NEW_ARRAY: {
+      HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
+      current_block_->AddInstruction(
+          new (arena_) HNewArray(length, dex_offset, instruction.VRegC_22c()));
+      UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
+      break;
+    }
+
+    case Instruction::FILLED_NEW_ARRAY: {
+      uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
+      uint32_t type_index = instruction.VRegB_35c();
+      uint32_t args[5];
+      instruction.GetVarArgs(args);
+      BuildFilledNewArray(dex_offset, type_index, number_of_vreg_arguments, false, args, 0);
+      break;
+    }
+
+    case Instruction::FILLED_NEW_ARRAY_RANGE: {
+      uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
+      uint32_t type_index = instruction.VRegB_3rc();
+      uint32_t register_index = instruction.VRegC_3rc();
+      BuildFilledNewArray(
+          dex_offset, type_index, number_of_vreg_arguments, true, nullptr, register_index);
+      break;
+    }
+
+    case Instruction::FILL_ARRAY_DATA: {
+      BuildFillArrayData(instruction, dex_offset);
+      break;
+    }
+
     case Instruction::MOVE_RESULT:
     case Instruction::MOVE_RESULT_WIDE:
     case Instruction::MOVE_RESULT_OBJECT:
-      UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+      UpdateLocal(instruction.VRegA(), latest_result_);
+      latest_result_ = nullptr;
       break;
 
     case Instruction::CMP_LONG: {
@@ -713,7 +1171,7 @@
     case Instruction::IGET_BYTE:
     case Instruction::IGET_CHAR:
     case Instruction::IGET_SHORT: {
-      if (!BuildFieldAccess(instruction, dex_offset, false)) {
+      if (!BuildInstanceFieldAccess(instruction, dex_offset, false)) {
         return false;
       }
       break;
@@ -726,7 +1184,33 @@
     case Instruction::IPUT_BYTE:
     case Instruction::IPUT_CHAR:
     case Instruction::IPUT_SHORT: {
-      if (!BuildFieldAccess(instruction, dex_offset, true)) {
+      if (!BuildInstanceFieldAccess(instruction, dex_offset, true)) {
+        return false;
+      }
+      break;
+    }
+
+    case Instruction::SGET:
+    case Instruction::SGET_WIDE:
+    case Instruction::SGET_OBJECT:
+    case Instruction::SGET_BOOLEAN:
+    case Instruction::SGET_BYTE:
+    case Instruction::SGET_CHAR:
+    case Instruction::SGET_SHORT: {
+      if (!BuildStaticFieldAccess(instruction, dex_offset, false)) {
+        return false;
+      }
+      break;
+    }
+
+    case Instruction::SPUT:
+    case Instruction::SPUT_WIDE:
+    case Instruction::SPUT_OBJECT:
+    case Instruction::SPUT_BOOLEAN:
+    case Instruction::SPUT_BYTE:
+    case Instruction::SPUT_CHAR:
+    case Instruction::SPUT_SHORT: {
+      if (!BuildStaticFieldAccess(instruction, dex_offset, true)) {
         return false;
       }
       break;
@@ -750,11 +1234,91 @@
     ARRAY_XX(_CHAR, Primitive::kPrimChar);
     ARRAY_XX(_SHORT, Primitive::kPrimShort);
 
+    case Instruction::ARRAY_LENGTH: {
+      HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
+      // No need for a temporary for the null check, it is the only input of the following
+      // instruction.
+      object = new (arena_) HNullCheck(object, dex_offset);
+      current_block_->AddInstruction(object);
+      current_block_->AddInstruction(new (arena_) HArrayLength(object));
+      UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
+      break;
+    }
+
+    case Instruction::CONST_STRING: {
+      current_block_->AddInstruction(new (arena_) HLoadString(instruction.VRegB_21c(), dex_offset));
+      UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+      break;
+    }
+
+    case Instruction::CONST_STRING_JUMBO: {
+      current_block_->AddInstruction(new (arena_) HLoadString(instruction.VRegB_31c(), dex_offset));
+      UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction());
+      break;
+    }
+
+    case Instruction::CONST_CLASS: {
+      uint16_t type_index = instruction.VRegB_21c();
+      bool type_known_final;
+      bool type_known_abstract;
+      bool is_referrers_class;
+      bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
+          dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
+          &type_known_final, &type_known_abstract, &is_referrers_class);
+      if (!can_access) {
+        return false;
+      }
+      current_block_->AddInstruction(
+          new (arena_) HLoadClass(type_index, is_referrers_class, dex_offset));
+      UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+      break;
+    }
+
+    case Instruction::MOVE_EXCEPTION: {
+      current_block_->AddInstruction(new (arena_) HLoadException());
+      UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction());
+      break;
+    }
+
+    case Instruction::THROW: {
+      HInstruction* exception = LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot);
+      current_block_->AddInstruction(new (arena_) HThrow(exception, dex_offset));
+      // A throw instruction must branch to the exit block.
+      current_block_->AddSuccessor(exit_block_);
+      // We finished building this block. Set the current block to null to avoid
+      // adding dead instructions to it.
+      current_block_ = nullptr;
+      break;
+    }
+
+    case Instruction::INSTANCE_OF: {
+      uint16_t type_index = instruction.VRegC_22c();
+      bool type_known_final;
+      bool type_known_abstract;
+      bool is_referrers_class;
+      bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
+          dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
+          &type_known_final, &type_known_abstract, &is_referrers_class);
+      if (!can_access) {
+        return false;
+      }
+      HInstruction* object = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimNot);
+      HLoadClass* cls = new (arena_) HLoadClass(type_index, is_referrers_class, dex_offset);
+      current_block_->AddInstruction(cls);
+      // The class needs a temporary before being used by the type check.
+      Temporaries temps(graph_, 1);
+      temps.Add(cls);
+      current_block_->AddInstruction(
+          new (arena_) HTypeCheck(object, cls, type_known_final, dex_offset));
+      UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
+      break;
+    }
+
     default:
       return false;
   }
   return true;
-}
+}  // NOLINT(readability/fn_size)
 
 HIntConstant* HGraphBuilder::GetIntConstant0() {
   if (constant0_ != nullptr) {
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 170c427..09c9a51 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -18,10 +18,11 @@
 #define ART_COMPILER_OPTIMIZING_BUILDER_H_
 
 #include "dex_file.h"
+#include "dex_file-inl.h"
 #include "driver/compiler_driver.h"
 #include "driver/dex_compilation_unit.h"
 #include "primitive.h"
-#include "utils/allocation.h"
+#include "utils/arena_object.h"
 #include "utils/growable_array.h"
 #include "nodes.h"
 
@@ -32,9 +33,9 @@
 class HGraphBuilder : public ValueObject {
  public:
   HGraphBuilder(ArenaAllocator* arena,
-                DexCompilationUnit* dex_compilation_unit = nullptr,
-                const DexFile* dex_file = nullptr,
-                CompilerDriver* driver = nullptr)
+                DexCompilationUnit* dex_compilation_unit,
+                const DexFile* dex_file,
+                CompilerDriver* driver)
       : arena_(arena),
         branch_targets_(arena, 0),
         locals_(arena, 0),
@@ -46,7 +47,28 @@
         constant1_(nullptr),
         dex_file_(dex_file),
         dex_compilation_unit_(dex_compilation_unit),
-        compiler_driver_(driver) {}
+        compiler_driver_(driver),
+        return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])),
+        code_start_(nullptr),
+        latest_result_(nullptr) {}
+
+  // Only for unit testing.
+  HGraphBuilder(ArenaAllocator* arena, Primitive::Type return_type = Primitive::kPrimInt)
+      : arena_(arena),
+        branch_targets_(arena, 0),
+        locals_(arena, 0),
+        entry_block_(nullptr),
+        exit_block_(nullptr),
+        current_block_(nullptr),
+        graph_(nullptr),
+        constant0_(nullptr),
+        constant1_(nullptr),
+        dex_file_(nullptr),
+        dex_compilation_unit_(nullptr),
+        compiler_driver_(nullptr),
+        return_type_(return_type),
+        code_start_(nullptr),
+        latest_result_(nullptr) {}
 
   HGraph* BuildGraph(const DexFile::CodeItem& code);
 
@@ -54,7 +76,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,10 +92,11 @@
   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);
+  void InitializeParameters(uint16_t number_of_parameters);
 
-  // Temporarily returns whether the compiler supports the parameters
-  // of the method.
-  bool InitializeParameters(uint16_t number_of_parameters);
+  template<typename T>
+  void Unop_12x(const Instruction& instruction, Primitive::Type type);
 
   template<typename T>
   void Binop_23x(const Instruction& instruction, Primitive::Type type);
@@ -90,9 +113,25 @@
   template<typename T> void If_21t(const Instruction& instruction, uint32_t dex_offset);
   template<typename T> void If_22t(const Instruction& instruction, uint32_t dex_offset);
 
+  void Conversion_12x(const Instruction& instruction,
+                      Primitive::Type input_type,
+                      Primitive::Type result_type);
+
+  void BuildCheckedDiv(uint16_t out_reg,
+                       uint16_t first_reg,
+                       int32_t second_reg,  // can be a constant
+                       uint32_t dex_offset,
+                       Primitive::Type type,
+                       bool second_is_lit);
+
   void BuildReturn(const Instruction& instruction, Primitive::Type type);
 
-  bool BuildFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_get);
+  // Builds an instance field access node and returns whether the instruction is supported.
+  bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_put);
+
+  // Builds a static field access node and returns whether the instruction is supported.
+  bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_put);
+
   void BuildArrayAccess(const Instruction& instruction,
                         uint32_t dex_offset,
                         bool is_get,
@@ -107,6 +146,33 @@
                    uint32_t* args,
                    uint32_t register_index);
 
+  // Builds a new array node and the instructions that fill it.
+  void BuildFilledNewArray(uint32_t dex_offset,
+                           uint32_t type_index,
+                           uint32_t number_of_vreg_arguments,
+                           bool is_range,
+                           uint32_t* args,
+                           uint32_t register_index);
+
+  void BuildFillArrayData(const Instruction& instruction, uint32_t dex_offset);
+
+  // Fills the given object with data as specified in the fill-array-data
+  // instruction. Currently only used for non-reference and non-floating point
+  // arrays.
+  template <typename T>
+  void BuildFillArrayData(HInstruction* object,
+                          const T* data,
+                          uint32_t element_count,
+                          Primitive::Type anticipated_type,
+                          uint32_t dex_offset);
+
+  // Fills the given object with data as specified in the fill-array-data
+  // instruction. The data must be for long and double arrays.
+  void BuildFillWideArrayData(HInstruction* object,
+                              const int64_t* data,
+                              uint32_t element_count,
+                              uint32_t dex_offset);
+
   ArenaAllocator* const arena_;
 
   // A list of the size of the dex code holding block information for
@@ -127,6 +193,15 @@
   const DexFile* const dex_file_;
   DexCompilationUnit* const dex_compilation_unit_;
   CompilerDriver* const compiler_driver_;
+  const Primitive::Type return_type_;
+
+  // The pointer in the dex file where the instructions of the code item
+  // being currently compiled start.
+  const uint16_t* code_start_;
+
+  // The last invoke or fill-new-array being built. Only to be
+  // used by move-result instructions.
+  HInstruction* latest_result_;
 
   DISALLOW_COPY_AND_ASSIGN(HGraphBuilder);
 };
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index bd8c27e..9d17263 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -17,40 +17,52 @@
 #include "code_generator.h"
 
 #include "code_generator_arm.h"
+#include "code_generator_arm64.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 "mirror/array-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/object_reference.h"
+#include "ssa_liveness_analysis.h"
 #include "utils/assembler.h"
 #include "verifier/dex_gc_map.h"
 #include "vmap_table.h"
 
 namespace art {
 
+size_t CodeGenerator::GetCacheOffset(uint32_t index) {
+  return mirror::ObjectArray<mirror::Object>::OffsetOfElement(index).SizeValue();
+}
+
 void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
   const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
   DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
   DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
-  block_labels_.SetSize(blocks.Size());
+  Initialize();
 
   DCHECK_EQ(frame_size_, kUninitializedFrameSize);
   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();
+    Bind(block);
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* current = it.Current();
       current->Accept(location_builder);
@@ -72,13 +84,13 @@
   const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
   DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
   DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
-  block_labels_.SetSize(blocks.Size());
+  Initialize();
 
   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();
+    Bind(block);
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* current = it.Current();
       current->Accept(instruction_visitor);
@@ -98,21 +110,40 @@
   }
 }
 
-size_t CodeGenerator::AllocateFreeRegisterInternal(
-    bool* blocked_registers, size_t number_of_registers) const {
-  for (size_t regno = 0; regno < number_of_registers; regno++) {
-    if (!blocked_registers[regno]) {
-      blocked_registers[regno] = true;
-      return regno;
+size_t CodeGenerator::FindFreeEntry(bool* array, size_t length) {
+  for (size_t i = 0; i < length; ++i) {
+    if (!array[i]) {
+      array[i] = true;
+      return i;
     }
   }
+  LOG(FATAL) << "Could not find a register in baseline register allocator";
+  UNREACHABLE();
   return -1;
 }
 
-void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots) {
+size_t CodeGenerator::FindTwoFreeConsecutiveAlignedEntries(bool* array, size_t length) {
+  for (size_t i = 0; i < length - 1; i += 2) {
+    if (!array[i] && !array[i + 1]) {
+      array[i] = true;
+      array[i + 1] = true;
+      return i;
+    }
+  }
+  LOG(FATAL) << "Could not find a register in baseline register allocator";
+  UNREACHABLE();
+  return -1;
+}
+
+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));
 }
@@ -147,47 +178,74 @@
   LocationSummary* locations = instruction->GetLocations();
   if (locations == nullptr) return;
 
-  for (size_t i = 0, e = GetNumberOfRegisters(); i < e; ++i) {
-    blocked_registers_[i] = false;
+  for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
+    blocked_core_registers_[i] = false;
+  }
+
+  for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
+    blocked_fpu_registers_[i] = false;
+  }
+
+  for (size_t i = 0, e = number_of_register_pairs_; i < e; ++i) {
+    blocked_register_pairs_[i] = false;
   }
 
   // Mark all fixed input, temp and output registers as used.
   for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
     Location loc = locations->InAt(i);
+    // The DCHECKS below check that a register is not specified twice in
+    // the summary.
     if (loc.IsRegister()) {
-      // Check that a register is not specified twice in the summary.
-      DCHECK(!blocked_registers_[loc.GetEncoding()]);
-      blocked_registers_[loc.GetEncoding()] = true;
+      DCHECK(!blocked_core_registers_[loc.reg()]);
+      blocked_core_registers_[loc.reg()] = true;
+    } else if (loc.IsFpuRegister()) {
+      DCHECK(!blocked_fpu_registers_[loc.reg()]);
+      blocked_fpu_registers_[loc.reg()] = true;
+    } else if (loc.IsFpuRegisterPair()) {
+      DCHECK(!blocked_fpu_registers_[loc.AsFpuRegisterPairLow<int>()]);
+      blocked_fpu_registers_[loc.AsFpuRegisterPairLow<int>()] = true;
+      DCHECK(!blocked_fpu_registers_[loc.AsFpuRegisterPairHigh<int>()]);
+      blocked_fpu_registers_[loc.AsFpuRegisterPairHigh<int>()] = true;
+    } else if (loc.IsRegisterPair()) {
+      DCHECK(!blocked_core_registers_[loc.AsRegisterPairLow<int>()]);
+      blocked_core_registers_[loc.AsRegisterPairLow<int>()] = true;
+      DCHECK(!blocked_core_registers_[loc.AsRegisterPairHigh<int>()]);
+      blocked_core_registers_[loc.AsRegisterPairHigh<int>()] = true;
     }
   }
 
   for (size_t i = 0, e = locations->GetTempCount(); i < e; ++i) {
     Location loc = locations->GetTemp(i);
+    // The DCHECKS below check that a register is not specified twice in
+    // the summary.
     if (loc.IsRegister()) {
-      // Check that a register is not specified twice in the summary.
-      DCHECK(!blocked_registers_[loc.GetEncoding()]);
-      blocked_registers_[loc.GetEncoding()] = true;
+      DCHECK(!blocked_core_registers_[loc.reg()]);
+      blocked_core_registers_[loc.reg()] = true;
+    } else if (loc.IsFpuRegister()) {
+      DCHECK(!blocked_fpu_registers_[loc.reg()]);
+      blocked_fpu_registers_[loc.reg()] = true;
+    } else {
+      DCHECK_EQ(loc.GetPolicy(), Location::kRequiresRegister);
     }
   }
 
-  SetupBlockedRegisters(blocked_registers_);
+  SetupBlockedRegisters();
 
   // Allocate all unallocated input locations.
   for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
     Location loc = locations->InAt(i);
     HInstruction* input = instruction->InputAt(i);
     if (loc.IsUnallocated()) {
-      if (loc.GetPolicy() == Location::kRequiresRegister) {
-        loc = Location::RegisterLocation(
-            AllocateFreeRegister(input->GetType(), blocked_registers_));
+      if ((loc.GetPolicy() == Location::kRequiresRegister)
+          || (loc.GetPolicy() == Location::kRequiresFpuRegister)) {
+        loc = AllocateFreeRegister(input->GetType());
       } else {
         DCHECK_EQ(loc.GetPolicy(), Location::kAny);
         HLoadLocal* load = input->AsLoadLocal();
         if (load != nullptr) {
           loc = GetStackLocation(load);
         } else {
-          loc = Location::RegisterLocation(
-              AllocateFreeRegister(input->GetType(), blocked_registers_));
+          loc = AllocateFreeRegister(input->GetType());
         }
       }
       locations->SetInAt(i, loc);
@@ -201,8 +259,7 @@
       DCHECK_EQ(loc.GetPolicy(), Location::kRequiresRegister);
       // TODO: Adjust handling of temps. We currently consider temps to use
       // core registers. They may also use floating point registers at some point.
-      loc = Location::RegisterLocation(static_cast<ManagedRegister>(
-          AllocateFreeRegister(Primitive::kPrimInt, blocked_registers_)));
+      loc = AllocateFreeRegister(Primitive::kPrimInt);
       locations->SetTempAt(i, loc);
     }
   }
@@ -211,8 +268,8 @@
     switch (result_location.GetPolicy()) {
       case Location::kAny:
       case Location::kRequiresRegister:
-        result_location = Location::RegisterLocation(
-            AllocateFreeRegister(instruction->GetType(), blocked_registers_));
+      case Location::kRequiresFpuRegister:
+        result_location = AllocateFreeRegister(instruction->GetType());
         break;
       case Location::kSameAsFirstInput:
         result_location = locations->InAt(0);
@@ -228,16 +285,22 @@
       HInstruction* previous = instruction->GetPrevious();
       Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
       Move(previous, temp_location, instruction);
-      previous->GetLocations()->SetOut(temp_location);
     }
     return;
   }
   AllocateRegistersLocally(instruction);
   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
     Location location = instruction->GetLocations()->InAt(i);
+    HInstruction* input = instruction->InputAt(i);
     if (location.IsValid()) {
       // Move the input to the desired location.
-      Move(instruction->InputAt(i), location, instruction);
+      if (input->GetNext()->IsTemporary()) {
+        // If the input was stored in a temporary, use that temporary to
+        // perform the move.
+        Move(input->GetNext(), location, instruction);
+      } else {
+        Move(input, location, instruction);
+      }
     }
   }
 }
@@ -247,10 +310,6 @@
   return current->GetBlockId() + 1 == next->GetBlockId();
 }
 
-Label* CodeGenerator::GetLabelOf(HBasicBlock* block) const {
-  return block_labels_.GetRawStorage() + block->GetBlockId();
-}
-
 CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator,
                                      HGraph* graph,
                                      InstructionSet instruction_set) {
@@ -259,6 +318,9 @@
     case kThumb2: {
       return new (allocator) arm::CodeGeneratorARM(graph);
     }
+    case kArm64: {
+      return new (allocator) arm64::CodeGeneratorARM64(graph);
+    }
     case kMips:
       return nullptr;
     case kX86: {
@@ -297,21 +359,42 @@
   }
 }
 
-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;
   int32_t pc2dex_dalvik_offset = 0;
   uint32_t dex2pc_data_size = 0u;
   uint32_t dex2pc_entries = 0u;
+  uint32_t dex2pc_offset = 0u;
+  int32_t dex2pc_dalvik_offset = 0;
 
-  // We currently only have pc2dex entries.
+  if (src_map != nullptr) {
+    src_map->reserve(pc2dex_entries);
+  }
+
   for (size_t i = 0; i < pc2dex_entries; i++) {
     struct PcInfo pc_info = pc_infos_.Get(i);
     pc2dex_data_size += UnsignedLeb128Size(pc_info.native_pc - pc2dex_offset);
     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}));
+    }
+  }
+
+  // Walk over the blocks and find which ones correspond to catch block entries.
+  for (size_t i = 0; i < graph_->GetBlocks().Size(); ++i) {
+    HBasicBlock* block = graph_->GetBlocks().Get(i);
+    if (block->IsCatchBlock()) {
+      intptr_t native_pc = GetAddressOf(block);
+      ++dex2pc_entries;
+      dex2pc_data_size += UnsignedLeb128Size(native_pc - dex2pc_offset);
+      dex2pc_data_size += SignedLeb128Size(block->GetDexPc() - dex2pc_dalvik_offset);
+      dex2pc_offset = native_pc;
+      dex2pc_dalvik_offset = block->GetDexPc();
+    }
   }
 
   uint32_t total_entries = pc2dex_entries + dex2pc_entries;
@@ -321,6 +404,7 @@
 
   uint8_t* data_ptr = &(*data)[0];
   uint8_t* write_pos = data_ptr;
+
   write_pos = EncodeUnsignedLeb128(write_pos, total_entries);
   write_pos = EncodeUnsignedLeb128(write_pos, pc2dex_entries);
   DCHECK_EQ(static_cast<size_t>(write_pos - data_ptr), hdr_data_size);
@@ -328,6 +412,9 @@
 
   pc2dex_offset = 0u;
   pc2dex_dalvik_offset = 0u;
+  dex2pc_offset = 0u;
+  dex2pc_dalvik_offset = 0u;
+
   for (size_t i = 0; i < pc2dex_entries; i++) {
     struct PcInfo pc_info = pc_infos_.Get(i);
     DCHECK(pc2dex_offset <= pc_info.native_pc);
@@ -336,6 +423,19 @@
     pc2dex_offset = pc_info.native_pc;
     pc2dex_dalvik_offset = pc_info.dex_pc;
   }
+
+  for (size_t i = 0; i < graph_->GetBlocks().Size(); ++i) {
+    HBasicBlock* block = graph_->GetBlocks().Get(i);
+    if (block->IsCatchBlock()) {
+      intptr_t native_pc = GetAddressOf(block);
+      write_pos2 = EncodeUnsignedLeb128(write_pos2, native_pc - dex2pc_offset);
+      write_pos2 = EncodeSignedLeb128(write_pos2, block->GetDexPc() - dex2pc_dalvik_offset);
+      dex2pc_offset = native_pc;
+      dex2pc_dalvik_offset = block->GetDexPc();
+    }
+  }
+
+
   DCHECK_EQ(static_cast<size_t>(write_pos - data_ptr), hdr_data_size + pc2dex_data_size);
   DCHECK_EQ(static_cast<size_t>(write_pos2 - data_ptr), data_size);
 
@@ -352,6 +452,14 @@
       CHECK_EQ(pc_info.dex_pc, it.DexPc());
       ++it;
     }
+    for (size_t i = 0; i < graph_->GetBlocks().Size(); ++i) {
+      HBasicBlock* block = graph_->GetBlocks().Get(i);
+      if (block->IsCatchBlock()) {
+        CHECK_EQ(GetAddressOf(block), it2.NativePcOffset());
+        CHECK_EQ(block->GetDexPc(), it2.DexPc());
+        ++it2;
+      }
+    }
     CHECK(it == table.PcToDexEnd());
     CHECK(it2 == table.DexToPcEnd());
   }
@@ -368,4 +476,160 @@
   *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();
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+        if (current->GetType() == Primitive::kPrimLong) {
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+          ++i;
+          DCHECK_LT(i, environment_size);
+        }
+        break;
+      }
+
+      case Location::kFpuRegister : {
+        int id = location.reg();
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, id);
+        if (current->GetType() == Primitive::kPrimDouble) {
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, id);
+          ++i;
+          DCHECK_LT(i, environment_size);
+        }
+        break;
+      }
+
+      default:
+        LOG(FATAL) << "Unexpected kind " << location.GetKind();
+    }
+  }
+}
+
+void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) {
+  RegisterSet* register_set = locations->GetLiveRegisters();
+  size_t stack_offset = first_register_slot_in_slow_path_;
+  for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
+    if (register_set->ContainsCoreRegister(i)) {
+      // If the register holds an object, update the stack mask.
+      if (locations->RegisterContainsObject(i)) {
+        locations->SetStackBit(stack_offset / kVRegSize);
+      }
+      stack_offset += SaveCoreRegister(stack_offset, i);
+    }
+  }
+
+  for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
+    if (register_set->ContainsFloatingPointRegister(i)) {
+      stack_offset += SaveFloatingPointRegister(stack_offset, i);
+    }
+  }
+}
+
+void CodeGenerator::RestoreLiveRegisters(LocationSummary* locations) {
+  RegisterSet* register_set = locations->GetLiveRegisters();
+  size_t stack_offset = first_register_slot_in_slow_path_;
+  for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
+    if (register_set->ContainsCoreRegister(i)) {
+      stack_offset += RestoreCoreRegister(stack_offset, i);
+    }
+  }
+
+  for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
+    if (register_set->ContainsFloatingPointRegister(i)) {
+      stack_offset += RestoreFloatingPointRegister(stack_offset, i);
+    }
+  }
+}
+
+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..fc4ea4b 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -23,15 +23,17 @@
 #include "locations.h"
 #include "memory_region.h"
 #include "nodes.h"
-#include "utils/assembler.h"
+#include "stack_map_stream.h"
 
 namespace art {
 
 static size_t constexpr kVRegSize = 4;
 static size_t constexpr kUninitializedFrameSize = 0;
 
+class Assembler;
 class CodeGenerator;
 class DexCompilationUnit;
+class SrcMap;
 
 class CodeAllocator {
  public:
@@ -49,24 +51,18 @@
   uintptr_t native_pc;
 };
 
-class SlowPathCode : public ArenaObject {
+class SlowPathCode : public ArenaObject<kArenaAllocSlowPaths> {
  public:
-  SlowPathCode() : entry_label_(), exit_label_() {}
+  SlowPathCode() {}
   virtual ~SlowPathCode() {}
 
-  Label* GetEntryLabel() { return &entry_label_; }
-  Label* GetExitLabel() { return &exit_label_; }
-
   virtual void EmitNativeCode(CodeGenerator* codegen) = 0;
 
  private:
-  Label entry_label_;
-  Label exit_label_;
-
   DISALLOW_COPY_AND_ASSIGN(SlowPathCode);
 };
 
-class CodeGenerator : public ArenaObject {
+class CodeGenerator : public ArenaObject<kArenaAllocMisc> {
  public:
   // Compiles the graph to executable instructions. Returns whether the compilation
   // succeeded.
@@ -78,7 +74,6 @@
 
   HGraph* GetGraph() const { return graph_; }
 
-  Label* GetLabelOf(HBasicBlock* block) const;
   bool GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const;
 
   size_t GetStackSlotOfParameter(HParameterValue* parameter) const {
@@ -88,15 +83,19 @@
         + parameter->GetIndex() * kVRegSize;
   }
 
+  virtual void Initialize() = 0;
   virtual void GenerateFrameEntry() = 0;
   virtual void GenerateFrameExit() = 0;
-  virtual void Bind(Label* label) = 0;
+  virtual void Bind(HBasicBlock* block) = 0;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0;
   virtual HGraphVisitor* GetLocationBuilder() = 0;
   virtual HGraphVisitor* GetInstructionVisitor() = 0;
   virtual Assembler* GetAssembler() = 0;
   virtual size_t GetWordSize() const = 0;
-  void ComputeFrameSize(size_t number_of_spill_slots);
+  virtual uintptr_t GetAddressOf(HBasicBlock* block) const = 0;
+  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;
@@ -105,20 +104,29 @@
   void SetFrameSize(uint32_t size) { frame_size_ = size; }
   uint32_t GetCoreSpillMask() const { return core_spill_mask_; }
 
-  virtual size_t GetNumberOfCoreRegisters() const = 0;
-  virtual size_t GetNumberOfFloatingPointRegisters() const = 0;
-  virtual size_t GetNumberOfRegisters() const = 0;
-  virtual void SetupBlockedRegisters(bool* blocked_registers) const = 0;
+  size_t GetNumberOfCoreRegisters() const { return number_of_core_registers_; }
+  size_t GetNumberOfFloatingPointRegisters() const { return number_of_fpu_registers_; }
+  virtual void SetupBlockedRegisters() const = 0;
+
   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;
-
-  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);
+  // Saves the register in the stack. Returns the size taken on stack.
+  virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) = 0;
+  // Restores the register from the stack. Returns the size taken on stack.
+  virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) = 0;
+  virtual size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+    UNUSED(stack_index, reg_id);
+    UNIMPLEMENTED(FATAL);
+    UNREACHABLE();
   }
+  virtual size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+    UNUSED(stack_index, reg_id);
+    UNIMPLEMENTED(FATAL);
+    UNREACHABLE();
+  }
+
+  void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc);
 
   void AddSlowPath(SlowPathCode* slow_path) {
     slow_paths_.Add(slow_path);
@@ -126,10 +134,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,74 +150,120 @@
     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;
+
+  bool* GetBlockedCoreRegisters() const { return blocked_core_registers_; }
+  bool* GetBlockedFloatingPointRegisters() const { return blocked_fpu_registers_; }
+
+  // Helper that returns the pointer offset of an index in an object array.
+  // Note: this method assumes we always have the same pointer size, regardless
+  // of the architecture.
+  static size_t GetCacheOffset(uint32_t index);
+
  protected:
-  CodeGenerator(HGraph* graph, size_t number_of_registers)
+  CodeGenerator(HGraph* graph,
+                size_t number_of_core_registers,
+                size_t number_of_fpu_registers,
+                size_t number_of_register_pairs)
       : frame_size_(kUninitializedFrameSize),
+        core_spill_mask_(0),
+        first_register_slot_in_slow_path_(0),
+        blocked_core_registers_(graph->GetArena()->AllocArray<bool>(number_of_core_registers)),
+        blocked_fpu_registers_(graph->GetArena()->AllocArray<bool>(number_of_fpu_registers)),
+        blocked_register_pairs_(graph->GetArena()->AllocArray<bool>(number_of_register_pairs)),
+        number_of_core_registers_(number_of_core_registers),
+        number_of_fpu_registers_(number_of_fpu_registers),
+        number_of_register_pairs_(number_of_register_pairs),
         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.
   void AllocateRegistersLocally(HInstruction* instruction) const;
 
   // Backend specific implementation for allocating a register.
-  virtual ManagedRegister AllocateFreeRegister(Primitive::Type type,
-                                               bool* blocked_registers) const = 0;
+  virtual Location AllocateFreeRegister(Primitive::Type type) const = 0;
 
-  // Raw implementation of allocating a register: loops over blocked_registers to find
-  // the first available register.
-  size_t AllocateFreeRegisterInternal(bool* blocked_registers, size_t number_of_registers) const;
+  static size_t FindFreeEntry(bool* array, size_t length);
+  static size_t FindTwoFreeConsecutiveAlignedEntries(bool* array, size_t length);
 
   virtual Location GetStackLocation(HLoadLocal* load) const = 0;
 
   // Frame size required for this method.
   uint32_t frame_size_;
   uint32_t core_spill_mask_;
+  uint32_t first_register_slot_in_slow_path_;
+
+  // Arrays used when doing register allocation to know which
+  // registers we can allocate. `SetupBlockedRegisters` updates the
+  // arrays.
+  bool* const blocked_core_registers_;
+  bool* const blocked_fpu_registers_;
+  bool* const blocked_register_pairs_;
+  size_t number_of_core_registers_;
+  size_t number_of_fpu_registers_;
+  size_t number_of_register_pairs_;
 
  private:
   void InitLocations(HInstruction* instruction);
+  size_t GetStackOffsetOfSavedRegister(size_t index);
 
   HGraph* const graph_;
 
-  // Labels for each block that will be compiled.
-  GrowableArray<Label> block_labels_;
   GrowableArray<PcInfo> pc_infos_;
   GrowableArray<SlowPathCode*> slow_paths_;
 
-  // Temporary data structure used when doing register allocation.
-  bool* const blocked_registers_;
-
   bool is_leaf_;
 
+  StackMapStream stack_map_stream_;
+
   DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
 };
 
-template <typename T>
+template <typename C, typename F>
 class CallingConvention {
  public:
-  CallingConvention(const T* registers, int number_of_registers)
-      : registers_(registers), number_of_registers_(number_of_registers) {}
+  CallingConvention(const C* registers,
+                    size_t number_of_registers,
+                    const F* fpu_registers,
+                    size_t number_of_fpu_registers)
+      : registers_(registers),
+        number_of_registers_(number_of_registers),
+        fpu_registers_(fpu_registers),
+        number_of_fpu_registers_(number_of_fpu_registers) {}
 
   size_t GetNumberOfRegisters() const { return number_of_registers_; }
+  size_t GetNumberOfFpuRegisters() const { return number_of_fpu_registers_; }
 
-  T GetRegisterAt(size_t index) const {
+  C GetRegisterAt(size_t index) const {
     DCHECK_LT(index, number_of_registers_);
     return registers_[index];
   }
 
-  uint8_t GetStackOffsetOf(size_t index) const {
+  F GetFpuRegisterAt(size_t index) const {
+    DCHECK_LT(index, number_of_fpu_registers_);
+    return fpu_registers_[index];
+  }
+
+  size_t GetStackOffsetOf(size_t index) const {
     // We still reserve the space for parameters passed by registers.
     // Add one for the method pointer.
     return (index + 1) * kVRegSize;
   }
 
  private:
-  const T* registers_;
+  const C* registers_;
   const size_t number_of_registers_;
+  const F* fpu_registers_;
+  const size_t number_of_fpu_registers_;
 
   DISALLOW_COPY_AND_ASSIGN(CallingConvention);
 };
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2c954a0..467c2a6 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -18,8 +18,9 @@
 
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/accounting/card_table.h"
-#include "mirror/array.h"
+#include "mirror/array-inl.h"
 #include "mirror/art_method.h"
+#include "mirror/class.h"
 #include "thread.h"
 #include "utils/assembler.h"
 #include "utils/arm/assembler_arm.h"
@@ -28,55 +29,86 @@
 
 namespace art {
 
-arm::ArmManagedRegister Location::AsArm() const {
-  return reg().AsArm();
-}
-
 namespace arm {
 
+static DRegister FromLowSToD(SRegister reg) {
+  DCHECK_EQ(reg % 2, 0);
+  return static_cast<DRegister>(reg / 2);
+}
+
 static constexpr bool kExplicitStackOverflowCheck = false;
 
 static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2;  // LR, R6, R7
 static constexpr int kCurrentMethodStackOffset = 0;
 
-static Location ArmCoreLocation(Register reg) {
-  return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg));
-}
-
 static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2 };
 static constexpr size_t kRuntimeParameterCoreRegistersLength =
     arraysize(kRuntimeParameterCoreRegisters);
+static constexpr SRegister kRuntimeParameterFpuRegisters[] = { };
+static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
 
-class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
+class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> {
  public:
   InvokeRuntimeCallingConvention()
       : CallingConvention(kRuntimeParameterCoreRegisters,
-                          kRuntimeParameterCoreRegistersLength) {}
+                          kRuntimeParameterCoreRegistersLength,
+                          kRuntimeParameterFpuRegisters,
+                          kRuntimeParameterFpuRegistersLength) {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
 };
 
 #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
+#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value()
 
-class NullCheckSlowPathARM : public SlowPathCode {
+class SlowPathCodeARM : public SlowPathCode {
  public:
-  explicit NullCheckSlowPathARM(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+  SlowPathCodeARM() : entry_label_(), exit_label_() {}
+
+  Label* GetEntryLabel() { return &entry_label_; }
+  Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  Label entry_label_;
+  Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM);
+};
+
+class NullCheckSlowPathARM : public SlowPathCodeARM {
+ public:
+  explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
     __ Bind(GetEntryLabel());
-    int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value();
-    __ ldr(LR, Address(TR, offset));
-    __ blx(LR);
-    codegen->RecordPcInfo(dex_pc_);
+    arm_codegen->InvokeRuntime(
+        QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HNullCheck* const instruction_;
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
 };
 
-class StackOverflowCheckSlowPathARM : public SlowPathCode {
+class DivZeroCheckSlowPathARM : public SlowPathCodeARM {
+ public:
+  explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
+    __ Bind(GetEntryLabel());
+    arm_codegen->InvokeRuntime(
+        QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc());
+  }
+
+ private:
+  HDivZeroCheck* const instruction_;
+  DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM);
+};
+
+class StackOverflowCheckSlowPathARM : public SlowPathCodeARM {
  public:
   StackOverflowCheckSlowPathARM() {}
 
@@ -90,33 +122,196 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
 };
 
-class BoundsCheckSlowPathARM : public SlowPathCode {
+class SuspendCheckSlowPathARM : public SlowPathCodeARM {
  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) {}
+  explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
+      : instruction_(instruction), successor_(successor) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen);
+    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
     __ Bind(GetEntryLabel());
-    InvokeRuntimeCallingConvention calling_convention;
-    arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(0)), index_location_);
-    arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(1)), length_location_);
-    int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value();
-    __ ldr(LR, Address(TR, offset));
-    __ blx(LR);
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->SaveLiveRegisters(instruction_->GetLocations());
+    arm_codegen->InvokeRuntime(
+        QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc());
+    codegen->RestoreLiveRegisters(instruction_->GetLocations());
+    if (successor_ == nullptr) {
+      __ b(GetReturnLabel());
+    } else {
+      __ b(arm_codegen->GetLabelOf(successor_));
+    }
+  }
+
+  Label* GetReturnLabel() {
+    DCHECK(successor_ == nullptr);
+    return &return_label_;
   }
 
  private:
-  const uint32_t dex_pc_;
+  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 SlowPathCodeARM {
+ public:
+  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 = down_cast<CodeGeneratorARM*>(codegen);
+    __ Bind(GetEntryLabel());
+    InvokeRuntimeCallingConvention calling_convention;
+    arm_codegen->Move32(
+        Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
+    arm_codegen->Move32(
+        Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_);
+    arm_codegen->InvokeRuntime(
+        QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc());
+  }
+
+ private:
+  HBoundsCheck* const instruction_;
   const Location index_location_;
   const Location length_location_;
 
   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM);
 };
 
+class LoadClassSlowPathARM : public SlowPathCodeARM {
+ public:
+  LoadClassSlowPathARM(HLoadClass* cls,
+                       HInstruction* at,
+                       uint32_t dex_pc,
+                       bool do_clinit)
+      : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+    DCHECK(at->IsLoadClass() || at->IsClinitCheck());
+  }
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = at_->GetLocations();
+
+    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(locations);
+
+    InvokeRuntimeCallingConvention calling_convention;
+    __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex());
+    arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+    int32_t entry_point_offset = do_clinit_
+        ? QUICK_ENTRY_POINT(pInitializeStaticStorage)
+        : QUICK_ENTRY_POINT(pInitializeType);
+    arm_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_);
+
+    // Move the class to the desired location.
+    Location out = locations->Out();
+    if (out.IsValid()) {
+      DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+      arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
+    }
+    codegen->RestoreLiveRegisters(locations);
+    __ b(GetExitLabel());
+  }
+
+ private:
+  // The class this slow path will load.
+  HLoadClass* const cls_;
+
+  // The instruction where this slow path is happening.
+  // (Might be the load class or an initialization check).
+  HInstruction* const at_;
+
+  // The dex PC of `at_`.
+  const uint32_t dex_pc_;
+
+  // Whether to initialize the class.
+  const bool do_clinit_;
+
+  DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM);
+};
+
+class LoadStringSlowPathARM : public SlowPathCodeARM {
+ public:
+  explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = instruction_->GetLocations();
+    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+
+    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(locations);
+
+    InvokeRuntimeCallingConvention calling_convention;
+    arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0));
+    __ LoadImmediate(calling_convention.GetRegisterAt(1), instruction_->GetStringIndex());
+    arm_codegen->InvokeRuntime(
+        QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc());
+    arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
+
+    codegen->RestoreLiveRegisters(locations);
+    __ b(GetExitLabel());
+  }
+
+ private:
+  HLoadString* const instruction_;
+
+  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM);
+};
+
+class TypeCheckSlowPathARM : public SlowPathCodeARM {
+ public:
+  explicit TypeCheckSlowPathARM(HTypeCheck* instruction, Location object_class)
+      : instruction_(instruction),
+        object_class_(object_class) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = instruction_->GetLocations();
+    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+
+    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(locations);
+
+    // We're moving two locations to locations that could overlap, so we need a parallel
+    // move resolver.
+    InvokeRuntimeCallingConvention calling_convention;
+    MoveOperands move1(locations->InAt(1),
+                       Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+                       nullptr);
+    MoveOperands move2(object_class_,
+                       Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
+                       nullptr);
+    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
+    parallel_move.AddMove(&move1);
+    parallel_move.AddMove(&move2);
+    arm_codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
+
+    arm_codegen->InvokeRuntime(
+        QUICK_ENTRY_POINT(pInstanceofNonTrivial), instruction_, instruction_->GetDexPc());
+    arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
+
+    codegen->RestoreLiveRegisters(locations);
+    __ b(GetExitLabel());
+  }
+
+ private:
+  HTypeCheck* const instruction_;
+  const Location object_class_;
+
+  DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM);
+};
+
+#undef __
+
 #undef __
 #define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
 
@@ -153,11 +348,22 @@
 }
 
 void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
-  stream << ArmManagedRegister::FromDRegister(DRegister(reg));
+  stream << ArmManagedRegister::FromSRegister(SRegister(reg));
+}
+
+size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
+  __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_index);
+  return kArmWordSize;
+}
+
+size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
+  __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_index);
+  return kArmWordSize;
 }
 
 CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
-    : CodeGenerator(graph, kNumberOfRegIds),
+    : CodeGenerator(graph, kNumberOfCoreRegisters, kNumberOfSRegisters, kNumberOfRegisterPairs),
+      block_labels_(graph->GetArena(), 0),
       location_builder_(graph, this),
       instruction_visitor_(graph, this),
       move_resolver_(graph->GetArena(), this),
@@ -167,32 +373,19 @@
   return kNumberOfPushedRegistersAtEntry * kArmWordSize;
 }
 
-static bool* GetBlockedRegisterPairs(bool* blocked_registers) {
-  return blocked_registers + kNumberOfAllocIds;
-}
-
-ManagedRegister CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type,
-                                                       bool* blocked_registers) const {
+Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const {
   switch (type) {
     case Primitive::kPrimLong: {
-      bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
-      size_t reg = AllocateFreeRegisterInternal(blocked_register_pairs, kNumberOfRegisterPairs);
+      size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs);
       ArmManagedRegister pair =
           ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg));
-      blocked_registers[pair.AsRegisterPairLow()] = true;
-      blocked_registers[pair.AsRegisterPairHigh()] = true;
-       // Block all other register pairs that share a register with `pair`.
-      for (int i = 0; i < kNumberOfRegisterPairs; i++) {
-        ArmManagedRegister current =
-            ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
-        if (current.AsRegisterPairLow() == pair.AsRegisterPairLow()
-            || current.AsRegisterPairLow() == pair.AsRegisterPairHigh()
-            || current.AsRegisterPairHigh() == pair.AsRegisterPairLow()
-            || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) {
-          blocked_register_pairs[i] = true;
-        }
-      }
-      return pair;
+      DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]);
+      DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]);
+
+      blocked_core_registers_[pair.AsRegisterPairLow()] = true;
+      blocked_core_registers_[pair.AsRegisterPairHigh()] = true;
+      UpdateBlockedPairRegisters();
+      return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh());
     }
 
     case Primitive::kPrimByte:
@@ -201,62 +394,89 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      int reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCoreRegisters);
+      int reg = FindFreeEntry(blocked_core_registers_, kNumberOfCoreRegisters);
       // Block all register pairs that contain `reg`.
-      bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
       for (int i = 0; i < kNumberOfRegisterPairs; i++) {
         ArmManagedRegister current =
             ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
         if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) {
-          blocked_register_pairs[i] = true;
+          blocked_register_pairs_[i] = true;
         }
       }
-      return ArmManagedRegister::FromCoreRegister(static_cast<Register>(reg));
+      return Location::RegisterLocation(reg);
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << type;
+    case Primitive::kPrimFloat: {
+      int reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfSRegisters);
+      return Location::FpuRegisterLocation(reg);
+    }
+
+    case Primitive::kPrimDouble: {
+      int reg = FindTwoFreeConsecutiveAlignedEntries(blocked_fpu_registers_, kNumberOfSRegisters);
+      DCHECK_EQ(reg % 2, 0);
+      return Location::FpuRegisterPairLocation(reg, reg + 1);
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << type;
   }
 
-  return ManagedRegister::NoRegister();
+  return Location();
 }
 
-void CodeGeneratorARM::SetupBlockedRegisters(bool* blocked_registers) const {
-  bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
-
+void CodeGeneratorARM::SetupBlockedRegisters() const {
   // Don't allocate the dalvik style register pair passing.
-  blocked_register_pairs[R1_R2] = true;
+  blocked_register_pairs_[R1_R2] = true;
 
   // Stack register, LR and PC are always reserved.
-  blocked_registers[SP] = true;
-  blocked_registers[LR] = true;
-  blocked_registers[PC] = true;
-
-  // Reserve R4 for suspend check.
-  blocked_registers[R4] = true;
-  blocked_register_pairs[R4_R5] = true;
+  blocked_core_registers_[SP] = true;
+  blocked_core_registers_[LR] = true;
+  blocked_core_registers_[PC] = true;
 
   // Reserve thread register.
-  blocked_registers[TR] = true;
+  blocked_core_registers_[TR] = true;
 
   // Reserve temp register.
-  blocked_registers[IP] = true;
+  blocked_core_registers_[IP] = true;
 
   // TODO: We currently don't use Quick's callee saved registers.
   // We always save and restore R6 and R7 to make sure we can use three
   // register pairs for long operations.
-  blocked_registers[R5] = true;
-  blocked_registers[R8] = true;
-  blocked_registers[R10] = true;
-  blocked_registers[R11] = true;
+  blocked_core_registers_[R4] = true;
+  blocked_core_registers_[R5] = true;
+  blocked_core_registers_[R8] = true;
+  blocked_core_registers_[R10] = true;
+  blocked_core_registers_[R11] = true;
+
+  blocked_fpu_registers_[S16] = true;
+  blocked_fpu_registers_[S17] = true;
+  blocked_fpu_registers_[S18] = true;
+  blocked_fpu_registers_[S19] = true;
+  blocked_fpu_registers_[S20] = true;
+  blocked_fpu_registers_[S21] = true;
+  blocked_fpu_registers_[S22] = true;
+  blocked_fpu_registers_[S23] = true;
+  blocked_fpu_registers_[S24] = true;
+  blocked_fpu_registers_[S25] = true;
+  blocked_fpu_registers_[S26] = true;
+  blocked_fpu_registers_[S27] = true;
+  blocked_fpu_registers_[S28] = true;
+  blocked_fpu_registers_[S29] = true;
+  blocked_fpu_registers_[S30] = true;
+  blocked_fpu_registers_[S31] = true;
+
+  UpdateBlockedPairRegisters();
 }
 
-size_t CodeGeneratorARM::GetNumberOfRegisters() const {
-  return kNumberOfRegIds;
+void CodeGeneratorARM::UpdateBlockedPairRegisters() const {
+  for (int i = 0; i < kNumberOfRegisterPairs; i++) {
+    ArmManagedRegister current =
+        ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
+    if (blocked_core_registers_[current.AsRegisterPairLow()]
+        || blocked_core_registers_[current.AsRegisterPairHigh()]) {
+      blocked_register_pairs_[i] = true;
+    }
+  }
 }
 
 InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen)
@@ -268,7 +488,7 @@
   bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
   if (!skip_overflow_check) {
     if (kExplicitStackOverflowCheck) {
-      SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
+      SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
       AddSlowPath(slow_path);
 
       __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value());
@@ -276,8 +496,8 @@
       __ b(slow_path->GetEntryLabel(), CC);
     } else {
       __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
-      __ ldr(IP, Address(IP, 0));
-      RecordPcInfo(0);
+      __ LoadFromOffset(kLoadWord, IP, IP, 0);
+      RecordPcInfo(nullptr, 0);
     }
   }
 
@@ -286,7 +506,7 @@
 
   // The return PC has already been pushed on the stack.
   __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize));
-  __ str(R0, Address(SP, 0));
+  __ StoreToOffset(kStoreWord, R0, SP, 0);
 }
 
 void CodeGeneratorARM::GenerateFrameExit() {
@@ -294,23 +514,21 @@
   __ PopList(1 << PC | 1 << R6 | 1 << R7);
 }
 
-void CodeGeneratorARM::Bind(Label* label) {
-  __ Bind(label);
+void CodeGeneratorARM::Bind(HBasicBlock* block) {
+  __ Bind(GetLabelOf(block));
 }
 
 Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
   switch (load->GetType()) {
     case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
       return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
       break;
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
-      return Location::StackSlot(GetStackSlot(load->GetLocal()));
-
     case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented type " << load->GetType();
+      return Location::StackSlot(GetStackSlot(load->GetLocal()));
 
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -333,30 +551,56 @@
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
       uint32_t index = gp_index_++;
+      uint32_t stack_index = stack_index_++;
       if (index < calling_convention.GetNumberOfRegisters()) {
-        return ArmCoreLocation(calling_convention.GetRegisterAt(index));
+        return Location::RegisterLocation(calling_convention.GetRegisterAt(index));
       } else {
-        return Location::StackSlot(calling_convention.GetStackOffsetOf(index));
+        return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
       }
     }
 
     case Primitive::kPrimLong: {
       uint32_t index = gp_index_;
+      uint32_t stack_index = stack_index_;
       gp_index_ += 2;
+      stack_index_ += 2;
       if (index + 1 < calling_convention.GetNumberOfRegisters()) {
-        return Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(
-            calling_convention.GetRegisterPairAt(index)));
+        ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair(
+            calling_convention.GetRegisterPairAt(index));
+        return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh());
       } else if (index + 1 == calling_convention.GetNumberOfRegisters()) {
-        return Location::QuickParameter(index);
+        return Location::QuickParameter(index, stack_index);
       } else {
-        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(index));
+        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
       }
     }
 
-    case Primitive::kPrimDouble:
-    case Primitive::kPrimFloat:
-      LOG(FATAL) << "Unimplemented parameter type " << type;
-      break;
+    case Primitive::kPrimFloat: {
+      uint32_t stack_index = stack_index_++;
+      if (float_index_ % 2 == 0) {
+        float_index_ = std::max(double_index_, float_index_);
+      }
+      if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
+        return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(float_index_++));
+      } else {
+        return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
+      }
+    }
+
+    case Primitive::kPrimDouble: {
+      double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
+      uint32_t stack_index = stack_index_;
+      stack_index_ += 2;
+      if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
+        uint32_t index = double_index_;
+        double_index_ += 2;
+        return Location::FpuRegisterPairLocation(
+          calling_convention.GetFpuRegisterAt(index),
+          calling_convention.GetFpuRegisterAt(index + 1));
+      } else {
+        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
+      }
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unexpected parameter type " << type;
@@ -365,23 +609,66 @@
   return Location();
 }
 
+Location InvokeDexCallingConventionVisitor::GetReturnLocation(Primitive::Type type) {
+  switch (type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      return Location::RegisterLocation(R0);
+    }
+
+    case Primitive::kPrimFloat: {
+      return Location::FpuRegisterLocation(S0);
+    }
+
+    case Primitive::kPrimLong: {
+      return Location::RegisterPairLocation(R0, R1);
+    }
+
+    case Primitive::kPrimDouble: {
+      return Location::FpuRegisterPairLocation(S0, S1);
+    }
+
+    case Primitive::kPrimVoid:
+      return Location();
+  }
+  UNREACHABLE();
+  return Location();
+}
+
 void CodeGeneratorARM::Move32(Location destination, Location source) {
   if (source.Equals(destination)) {
     return;
   }
   if (destination.IsRegister()) {
     if (source.IsRegister()) {
-      __ Mov(destination.AsArm().AsCoreRegister(), source.AsArm().AsCoreRegister());
+      __ Mov(destination.As<Register>(), source.As<Register>());
+    } else if (source.IsFpuRegister()) {
+      __ vmovrs(destination.As<Register>(), source.As<SRegister>());
     } else {
-      __ ldr(destination.AsArm().AsCoreRegister(), Address(SP, source.GetStackIndex()));
+      __ LoadFromOffset(kLoadWord, destination.As<Register>(), SP, source.GetStackIndex());
+    }
+  } else if (destination.IsFpuRegister()) {
+    if (source.IsRegister()) {
+      __ vmovsr(destination.As<SRegister>(), source.As<Register>());
+    } else if (source.IsFpuRegister()) {
+      __ vmovs(destination.As<SRegister>(), source.As<SRegister>());
+    } else {
+      __ LoadSFromOffset(destination.As<SRegister>(), SP, source.GetStackIndex());
     }
   } else {
     DCHECK(destination.IsStackSlot());
     if (source.IsRegister()) {
-      __ str(source.AsArm().AsCoreRegister(), Address(SP, destination.GetStackIndex()));
+      __ StoreToOffset(kStoreWord, source.As<Register>(), SP, destination.GetStackIndex());
+    } else if (source.IsFpuRegister()) {
+      __ StoreSToOffset(source.As<SRegister>(), SP, destination.GetStackIndex());
     } else {
-      __ ldr(IP, Address(SP, source.GetStackIndex()));
-      __ str(IP, Address(SP, destination.GetStackIndex()));
+      DCHECK(source.IsStackSlot());
+      __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
+      __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
     }
   }
 }
@@ -390,64 +677,87 @@
   if (source.Equals(destination)) {
     return;
   }
-  if (destination.IsRegister()) {
-    if (source.IsRegister()) {
-      __ Mov(destination.AsArm().AsRegisterPairLow(), source.AsArm().AsRegisterPairLow());
-      __ Mov(destination.AsArm().AsRegisterPairHigh(), source.AsArm().AsRegisterPairHigh());
+  if (destination.IsRegisterPair()) {
+    if (source.IsRegisterPair()) {
+      __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
+      __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
+    } else if (source.IsFpuRegister()) {
+      UNIMPLEMENTED(FATAL);
     } else if (source.IsQuickParameter()) {
-      uint32_t argument_index = source.GetQuickParameterIndex();
+      uint16_t register_index = source.GetQuickParameterRegisterIndex();
+      uint16_t stack_index = source.GetQuickParameterStackIndex();
       InvokeDexCallingConvention calling_convention;
-      __ Mov(destination.AsArm().AsRegisterPairLow(),
-             calling_convention.GetRegisterAt(argument_index));
-      __ ldr(destination.AsArm().AsRegisterPairHigh(),
-             Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
+      __ Mov(destination.AsRegisterPairLow<Register>(),
+             calling_convention.GetRegisterAt(register_index));
+      __ LoadFromOffset(kLoadWord, destination.AsRegisterPairHigh<Register>(),
+             SP, calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize());
     } else {
       DCHECK(source.IsDoubleStackSlot());
-      if (destination.AsArm().AsRegisterPair() == R1_R2) {
-        __ ldr(R1, Address(SP, source.GetStackIndex()));
-        __ ldr(R2, Address(SP, source.GetHighStackIndex(kArmWordSize)));
+      if (destination.AsRegisterPairLow<Register>() == R1) {
+        DCHECK_EQ(destination.AsRegisterPairHigh<Register>(), R2);
+        __ LoadFromOffset(kLoadWord, R1, SP, source.GetStackIndex());
+        __ LoadFromOffset(kLoadWord, R2, SP, source.GetHighStackIndex(kArmWordSize));
       } else {
-        __ LoadFromOffset(kLoadWordPair, destination.AsArm().AsRegisterPairLow(),
+        __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(),
                           SP, source.GetStackIndex());
       }
     }
+  } else if (destination.IsFpuRegisterPair()) {
+    if (source.IsDoubleStackSlot()) {
+      __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
+                         SP,
+                         source.GetStackIndex());
+    } else {
+      UNIMPLEMENTED(FATAL);
+    }
   } else if (destination.IsQuickParameter()) {
     InvokeDexCallingConvention calling_convention;
-    uint32_t argument_index = destination.GetQuickParameterIndex();
-    if (source.IsRegister()) {
-      __ Mov(calling_convention.GetRegisterAt(argument_index), source.AsArm().AsRegisterPairLow());
-      __ str(source.AsArm().AsRegisterPairHigh(),
-             Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1)));
+    uint16_t register_index = destination.GetQuickParameterRegisterIndex();
+    uint16_t stack_index = destination.GetQuickParameterStackIndex();
+    if (source.IsRegisterPair()) {
+      __ Mov(calling_convention.GetRegisterAt(register_index),
+             source.AsRegisterPairLow<Register>());
+      __ StoreToOffset(kStoreWord, source.AsRegisterPairHigh<Register>(),
+             SP, calling_convention.GetStackOffsetOf(stack_index + 1));
+    } else if (source.IsFpuRegister()) {
+      UNIMPLEMENTED(FATAL);
     } else {
       DCHECK(source.IsDoubleStackSlot());
-      __ ldr(calling_convention.GetRegisterAt(argument_index), Address(SP, source.GetStackIndex()));
-      __ ldr(R0, Address(SP, source.GetHighStackIndex(kArmWordSize)));
-      __ str(R0, Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1)));
+      __ LoadFromOffset(
+          kLoadWord, calling_convention.GetRegisterAt(register_index), SP, source.GetStackIndex());
+      __ LoadFromOffset(kLoadWord, R0, SP, source.GetHighStackIndex(kArmWordSize));
+      __ StoreToOffset(kStoreWord, R0, SP, calling_convention.GetStackOffsetOf(stack_index + 1));
     }
   } else {
     DCHECK(destination.IsDoubleStackSlot());
-    if (source.IsRegister()) {
-      if (source.AsArm().AsRegisterPair() == R1_R2) {
-        __ str(R1, Address(SP, destination.GetStackIndex()));
-        __ str(R2, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
+    if (source.IsRegisterPair()) {
+      if (source.AsRegisterPairLow<Register>() == R1) {
+        DCHECK_EQ(source.AsRegisterPairHigh<Register>(), R2);
+        __ StoreToOffset(kStoreWord, R1, SP, destination.GetStackIndex());
+        __ StoreToOffset(kStoreWord, R2, SP, destination.GetHighStackIndex(kArmWordSize));
       } else {
-        __ StoreToOffset(kStoreWordPair, source.AsArm().AsRegisterPairLow(),
+        __ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow<Register>(),
                          SP, destination.GetStackIndex());
       }
     } else if (source.IsQuickParameter()) {
       InvokeDexCallingConvention calling_convention;
-      uint32_t argument_index = source.GetQuickParameterIndex();
-      __ str(calling_convention.GetRegisterAt(argument_index),
-             Address(SP, destination.GetStackIndex()));
-      __ ldr(R0,
-             Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
-      __ str(R0, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
+      uint16_t register_index = source.GetQuickParameterRegisterIndex();
+      uint16_t stack_index = source.GetQuickParameterStackIndex();
+      __ StoreToOffset(kStoreWord, calling_convention.GetRegisterAt(register_index),
+             SP, destination.GetStackIndex());
+      __ LoadFromOffset(kLoadWord, R0,
+             SP, calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize());
+      __ StoreToOffset(kStoreWord, R0, SP, destination.GetHighStackIndex(kArmWordSize));
+    } else if (source.IsFpuRegisterPair()) {
+      __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
+                        SP,
+                        destination.GetStackIndex());
     } else {
       DCHECK(source.IsDoubleStackSlot());
-      __ ldr(IP, Address(SP, source.GetStackIndex()));
-      __ str(IP, Address(SP, destination.GetStackIndex()));
-      __ ldr(IP, Address(SP, source.GetHighStackIndex(kArmWordSize)));
-      __ str(IP, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
+      __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
+      __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+      __ LoadFromOffset(kLoadWord, IP, SP, source.GetHighStackIndex(kArmWordSize));
+      __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
     }
   }
 }
@@ -458,28 +768,28 @@
     return;
   }
 
-  if (instruction->AsIntConstant() != nullptr) {
+  if (instruction->IsIntConstant()) {
     int32_t value = instruction->AsIntConstant()->GetValue();
     if (location.IsRegister()) {
-      __ LoadImmediate(location.AsArm().AsCoreRegister(), value);
+      __ LoadImmediate(location.As<Register>(), value);
     } else {
       DCHECK(location.IsStackSlot());
       __ LoadImmediate(IP, value);
-      __ str(IP, Address(SP, location.GetStackIndex()));
+      __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex());
     }
-  } else if (instruction->AsLongConstant() != nullptr) {
+  } else if (instruction->IsLongConstant()) {
     int64_t value = instruction->AsLongConstant()->GetValue();
-    if (location.IsRegister()) {
-      __ LoadImmediate(location.AsArm().AsRegisterPairLow(), Low32Bits(value));
-      __ LoadImmediate(location.AsArm().AsRegisterPairHigh(), High32Bits(value));
+    if (location.IsRegisterPair()) {
+      __ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value));
+      __ LoadImmediate(location.AsRegisterPairHigh<Register>(), High32Bits(value));
     } else {
       DCHECK(location.IsDoubleStackSlot());
       __ LoadImmediate(IP, Low32Bits(value));
-      __ str(IP, Address(SP, location.GetStackIndex()));
+      __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex());
       __ LoadImmediate(IP, High32Bits(value));
-      __ str(IP, Address(SP, location.GetHighStackIndex(kArmWordSize)));
+      __ StoreToOffset(kStoreWord, IP, SP, location.GetHighStackIndex(kArmWordSize));
     }
-  } else if (instruction->AsLoadLocal() != nullptr) {
+  } else if (instruction->IsLoadLocal()) {
     uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
     switch (instruction->GetType()) {
       case Primitive::kPrimBoolean:
@@ -488,16 +798,21 @@
       case Primitive::kPrimShort:
       case Primitive::kPrimInt:
       case Primitive::kPrimNot:
+      case Primitive::kPrimFloat:
         Move32(location, Location::StackSlot(stack_slot));
         break;
 
       case Primitive::kPrimLong:
+      case Primitive::kPrimDouble:
         Move64(location, Location::DoubleStackSlot(stack_slot));
         break;
 
       default:
-        LOG(FATAL) << "Unimplemented type " << instruction->GetType();
+        LOG(FATAL) << "Unexpected type " << instruction->GetType();
     }
+  } else if (instruction->IsTemporary()) {
+    Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
+    Move32(location, temp_location);
   } else {
     DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
     switch (instruction->GetType()) {
@@ -507,28 +822,56 @@
       case Primitive::kPrimShort:
       case Primitive::kPrimNot:
       case Primitive::kPrimInt:
+      case Primitive::kPrimFloat:
         Move32(location, locations->Out());
         break;
 
       case Primitive::kPrimLong:
+      case Primitive::kPrimDouble:
         Move64(location, locations->Out());
         break;
 
       default:
-        LOG(FATAL) << "Unimplemented type " << instruction->GetType();
+        LOG(FATAL) << "Unexpected type " << instruction->GetType();
     }
   }
 }
 
+void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset,
+                                     HInstruction* instruction,
+                                     uint32_t dex_pc) {
+  __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset);
+  __ blx(LR);
+  RecordPcInfo(instruction, dex_pc);
+  DCHECK(instruction->IsSuspendCheck()
+      || instruction->IsBoundsCheck()
+      || instruction->IsNullCheck()
+      || instruction->IsDivZeroCheck()
+      || !IsLeafMethod());
+}
+
 void LocationsBuilderARM::VisitGoto(HGoto* got) {
   got->SetLocations(nullptr);
 }
 
 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));
   }
 }
@@ -538,6 +881,7 @@
 }
 
 void InstructionCodeGeneratorARM::VisitExit(HExit* exit) {
+  UNUSED(exit);
   if (kIsDebugBuild) {
     __ Comment("Unreachable");
     __ bkpt(0);
@@ -545,63 +889,74 @@
 }
 
 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());
+  if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
+    locations->SetInAt(0, Location::RequiresRegister());
   }
-  if_instr->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
   HInstruction* cond = if_instr->InputAt(0);
-  DCHECK(cond->IsCondition());
-  HCondition* condition = cond->AsCondition();
-  if (condition->NeedsMaterialization()) {
-    // Condition has been materialized, compare the output to 0
-    DCHECK(if_instr->GetLocations()->InAt(0).IsRegister());
-    __ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(),
-           ShifterOperand(0));
-    __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), EQ);
-  } else {
-    // Condition has not been materialized, use its inputs as the comparison and its
-    // condition as the branch condition.
-    LocationSummary* locations = condition->GetLocations();
-    if (locations->InAt(1).IsRegister()) {
-      __ cmp(locations->InAt(0).AsArm().AsCoreRegister(),
-             ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
-    } else {
-      DCHECK(locations->InAt(1).IsConstant());
-      int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
-      ShifterOperand operand;
-      if (ShifterOperand::CanHoldArm(value, &operand)) {
-        __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(value));
-      } else {
-        Register temp = IP;
-        __ LoadImmediate(temp, value);
-        __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp));
+  if (cond->IsIntConstant()) {
+    // Constant condition, statically compared against 1.
+    int32_t cond_value = cond->AsIntConstant()->GetValue();
+    if (cond_value == 1) {
+      if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                     if_instr->IfTrueSuccessor())) {
+        __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
       }
+      return;
+    } else {
+      DCHECK_EQ(cond_value, 0);
     }
-    __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()),
-         ARMCondition(condition->GetCondition()));
+  } else {
+    if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
+      // Condition has been materialized, compare the output to 0
+      DCHECK(if_instr->GetLocations()->InAt(0).IsRegister());
+      __ cmp(if_instr->GetLocations()->InAt(0).As<Register>(),
+             ShifterOperand(0));
+      __ 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.
+      LocationSummary* locations = cond->GetLocations();
+      if (locations->InAt(1).IsRegister()) {
+        __ cmp(locations->InAt(0).As<Register>(),
+               ShifterOperand(locations->InAt(1).As<Register>()));
+      } else {
+        DCHECK(locations->InAt(1).IsConstant());
+        int32_t value =
+            locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+        ShifterOperand operand;
+        if (ShifterOperand::CanHoldArm(value, &operand)) {
+          __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(value));
+        } else {
+          Register temp = IP;
+          __ LoadImmediate(temp, value);
+          __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(temp));
+        }
+      }
+      __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()),
+           ARMCondition(cond->AsCondition()->GetCondition()));
+    }
   }
-
-  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
+  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                 if_instr->IfFalseSuccessor())) {
     __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
   }
 }
 
 
 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());
+    locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
   }
-  comp->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
@@ -609,24 +964,24 @@
 
   LocationSummary* locations = comp->GetLocations();
   if (locations->InAt(1).IsRegister()) {
-    __ cmp(locations->InAt(0).AsArm().AsCoreRegister(),
-           ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
+    __ cmp(locations->InAt(0).As<Register>(),
+           ShifterOperand(locations->InAt(1).As<Register>()));
   } else {
     DCHECK(locations->InAt(1).IsConstant());
     int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
     ShifterOperand operand;
     if (ShifterOperand::CanHoldArm(value, &operand)) {
-      __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(value));
+      __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(value));
     } else {
       Register temp = IP;
       __ LoadImmediate(temp, value);
-      __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp));
+      __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(temp));
     }
   }
   __ it(ARMCondition(comp->GetCondition()), kItElse);
-  __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1),
+  __ mov(locations->Out().As<Register>(), ShifterOperand(1),
          ARMCondition(comp->GetCondition()));
-  __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0),
+  __ mov(locations->Out().As<Register>(), ShifterOperand(0),
          ARMOppositeCondition(comp->GetCondition()));
 }
 
@@ -692,10 +1047,12 @@
 
 void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load) {
   // Nothing to do, this is driven by the code generator.
+  UNUSED(load);
 }
 
 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:
@@ -703,39 +1060,66 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
+    case Primitive::kPrimFloat:
       locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
       break;
 
     case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
       locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
       break;
 
     default:
-      LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
+      LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType();
   }
-  store->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) {
+  UNUSED(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) {
+  // Will be generated at use site.
+  UNUSED(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) {
   // Will be generated at use site.
+  UNUSED(constant);
+}
+
+void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant) {
+  // Will be generated at use site.
+  UNUSED(constant);
+}
+
+void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant) {
+  // Will be generated at use site.
+  UNUSED(constant);
 }
 
 void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) {
@@ -743,102 +1127,31 @@
 }
 
 void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret) {
+  UNUSED(ret);
   codegen_->GenerateFrameExit();
 }
 
 void LocationsBuilderARM::VisitReturn(HReturn* ret) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
-  switch (ret->InputAt(0)->GetType()) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot:
-      locations->SetInAt(0, ArmCoreLocation(R0));
-      break;
-
-    case Primitive::kPrimLong:
-      locations->SetInAt(
-          0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
-      break;
-
-    default:
-      LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
-  }
-
-  ret->SetLocations(locations);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
+  locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
 }
 
 void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
-  if (kIsDebugBuild) {
-    switch (ret->InputAt(0)->GetType()) {
-      case Primitive::kPrimBoolean:
-      case Primitive::kPrimByte:
-      case Primitive::kPrimChar:
-      case Primitive::kPrimShort:
-      case Primitive::kPrimInt:
-      case Primitive::kPrimNot:
-        DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsCoreRegister(), R0);
-        break;
-
-      case Primitive::kPrimLong:
-        DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsRegisterPair(), R0_R1);
-        break;
-
-      default:
-        LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
-    }
-  }
+  UNUSED(ret);
   codegen_->GenerateFrameExit();
 }
 
 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) {
-  __ ldr(reg, Address(SP, kCurrentMethodStackOffset));
+void CodeGeneratorARM::LoadCurrentMethod(Register reg) {
+  __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset);
 }
 
 void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
-  Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister();
-  uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
-  size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
-      invoke->GetIndexInDexCache() * kArmWordSize;
+  Register temp = invoke->GetLocations()->GetTemp(0).As<Register>();
 
   // TODO: Implement all kinds of calls:
   // 1) boot -> boot
@@ -848,167 +1161,600 @@
   // Currently we implement the app -> app logic, which looks up in the resolve cache.
 
   // temp = method;
-  LoadCurrentMethod(temp);
+  codegen_->LoadCurrentMethod(temp);
   // temp = temp->dex_cache_resolved_methods_;
-  __ ldr(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+  __ LoadFromOffset(
+      kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
   // temp = temp[index_in_cache]
-  __ ldr(temp, Address(temp, index_in_cache));
+  __ LoadFromOffset(
+      kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache()));
   // LR = temp[offset_of_quick_compiled_code]
-  __ ldr(LR, Address(temp,
-                     mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+  __ LoadFromOffset(kLoadWord, LR, temp,
+                     mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
   // LR()
   __ blx(LR);
 
-  codegen_->RecordPcInfo(invoke->GetDexPc());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
 }
 
-void LocationsBuilderARM::VisitAdd(HAdd* add) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
-  switch (add->GetResultType()) {
+void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
+  locations->AddTemp(Location::RegisterLocation(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()));
+  }
+
+  locations->SetOut(calling_convention_visitor.GetReturnLocation(invoke->GetType()));
+}
+
+void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  Register temp = invoke->GetLocations()->GetTemp(0).As<Register>();
+  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()) {
+    __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex());
+    __ LoadFromOffset(kLoadWord, temp, temp, class_offset);
+  } else {
+    __ LoadFromOffset(kLoadWord, temp, receiver.As<Register>(), class_offset);
+  }
+  // temp = temp->GetMethodAt(method_offset);
+  uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value();
+  __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
+  // LR = temp->GetEntryPoint();
+  __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
+  // LR();
+  __ blx(LR);
+  DCHECK(!codegen_->IsLeafMethod());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderARM::VisitInvokeInterface(HInvokeInterface* invoke) {
+  HandleInvoke(invoke);
+  // Add the hidden argument.
+  invoke->GetLocations()->AddTemp(Location::RegisterLocation(R12));
+}
+
+void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) {
+  // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
+  Register temp = invoke->GetLocations()->GetTemp(0).As<Register>();
+  uint32_t method_offset = mirror::Class::EmbeddedImTableOffset().Uint32Value() +
+          (invoke->GetImtIndex() % mirror::Class::kImtSize) * sizeof(mirror::Class::ImTableEntry);
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+
+  // Set the hidden argument.
+  __ LoadImmediate(invoke->GetLocations()->GetTemp(1).As<Register>(), invoke->GetDexMethodIndex());
+
+  // temp = object->GetClass();
+  if (receiver.IsStackSlot()) {
+    __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex());
+    __ LoadFromOffset(kLoadWord, temp, temp, class_offset);
+  } else {
+    __ LoadFromOffset(kLoadWord, temp, receiver.As<Register>(), class_offset);
+  }
+  // temp = temp->GetImtEntryAt(method_offset);
+  uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value();
+  __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
+  // LR = temp->GetEntryPoint();
+  __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
+  // LR();
+  __ blx(LR);
+  DCHECK(!codegen_->IsLeafMethod());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderARM::VisitNeg(HNeg* neg) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
+  switch (neg->GetResultType()) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
+      bool output_overlaps = (neg->GetResultType() == Primitive::kPrimLong);
       locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
-      locations->SetOut(Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), output_overlaps);
       break;
     }
 
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
       break;
 
     default:
-      LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
+      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
   }
-  add->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) {
+  LocationSummary* locations = neg->GetLocations();
+  Location out = locations->Out();
+  Location in = locations->InAt(0);
+  switch (neg->GetResultType()) {
+    case Primitive::kPrimInt:
+      DCHECK(in.IsRegister());
+      __ rsb(out.As<Register>(), in.As<Register>(), ShifterOperand(0));
+      break;
+
+    case Primitive::kPrimLong:
+      DCHECK(in.IsRegisterPair());
+      // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
+      __ rsbs(out.AsRegisterPairLow<Register>(),
+              in.AsRegisterPairLow<Register>(),
+              ShifterOperand(0));
+      // We cannot emit an RSC (Reverse Subtract with Carry)
+      // instruction here, as it does not exist in the Thumb-2
+      // instruction set.  We use the following approach
+      // using SBC and SUB instead.
+      //
+      // out.hi = -C
+      __ sbc(out.AsRegisterPairHigh<Register>(),
+             out.AsRegisterPairHigh<Register>(),
+             ShifterOperand(out.AsRegisterPairHigh<Register>()));
+      // out.hi = out.hi - in.hi
+      __ sub(out.AsRegisterPairHigh<Register>(),
+             out.AsRegisterPairHigh<Register>(),
+             ShifterOperand(in.AsRegisterPairHigh<Register>()));
+      break;
+
+    case Primitive::kPrimFloat:
+      DCHECK(in.IsFpuRegister());
+      __ vnegs(out.As<SRegister>(), in.As<SRegister>());
+      break;
+
+    case Primitive::kPrimDouble:
+      DCHECK(in.IsFpuRegisterPair());
+      __ vnegd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
+  }
+}
+
+void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall);
+  Primitive::Type result_type = conversion->GetResultType();
+  Primitive::Type input_type = conversion->GetInputType();
+  switch (result_type) {
+    case Primitive::kPrimLong:
+      switch (input_type) {
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // int-to-long conversion.
+          locations->SetInAt(0, Location::RequiresRegister());
+          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+          break;
+
+        case Primitive::kPrimFloat:
+        case Primitive::kPrimDouble:
+          LOG(FATAL) << "Type conversion from " << input_type << " to "
+                     << result_type << " not yet implemented";
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Type conversion from " << input_type
+                 << " to " << result_type << " not yet implemented";
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected type conversion from " << input_type
+                 << " to " << result_type;
+  }
+}
+
+void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversion) {
+  LocationSummary* locations = conversion->GetLocations();
+  Location out = locations->Out();
+  Location in = locations->InAt(0);
+  Primitive::Type result_type = conversion->GetResultType();
+  Primitive::Type input_type = conversion->GetInputType();
+  switch (result_type) {
+    case Primitive::kPrimLong:
+      switch (input_type) {
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // int-to-long conversion.
+          DCHECK(out.IsRegisterPair());
+          DCHECK(in.IsRegister());
+          __ Mov(out.AsRegisterPairLow<Register>(), in.As<Register>());
+          // Sign extension.
+          __ Asr(out.AsRegisterPairHigh<Register>(),
+                 out.AsRegisterPairLow<Register>(),
+                 31);
+          break;
+
+        case Primitive::kPrimFloat:
+        case Primitive::kPrimDouble:
+          LOG(FATAL) << "Type conversion from " << input_type << " to "
+                     << result_type << " not yet implemented";
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Type conversion from " << input_type
+                 << " to " << result_type << " not yet implemented";
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected type conversion from " << input_type
+                 << " to " << result_type;
+  }
+}
+
+void LocationsBuilderARM::VisitAdd(HAdd* add) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
+  switch (add->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong: {
+      bool output_overlaps = (add->GetResultType() == Primitive::kPrimLong);
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
+      locations->SetOut(Location::RequiresRegister(), output_overlaps);
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+  }
 }
 
 void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
   LocationSummary* locations = add->GetLocations();
+  Location out = locations->Out();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
   switch (add->GetResultType()) {
     case Primitive::kPrimInt:
-      if (locations->InAt(1).IsRegister()) {
-        __ add(locations->Out().AsArm().AsCoreRegister(),
-               locations->InAt(0).AsArm().AsCoreRegister(),
-               ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
+      if (second.IsRegister()) {
+        __ add(out.As<Register>(), first.As<Register>(), ShifterOperand(second.As<Register>()));
       } else {
-        __ AddConstant(locations->Out().AsArm().AsCoreRegister(),
-                       locations->InAt(0).AsArm().AsCoreRegister(),
-                       locations->InAt(1).GetConstant()->AsIntConstant()->GetValue());
+        __ AddConstant(out.As<Register>(),
+                       first.As<Register>(),
+                       second.GetConstant()->AsIntConstant()->GetValue());
       }
       break;
 
     case Primitive::kPrimLong:
-      __ adds(locations->Out().AsArm().AsRegisterPairLow(),
-              locations->InAt(0).AsArm().AsRegisterPairLow(),
-              ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow()));
-      __ adc(locations->Out().AsArm().AsRegisterPairHigh(),
-             locations->InAt(0).AsArm().AsRegisterPairHigh(),
-             ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh()));
+      __ adds(out.AsRegisterPairLow<Register>(),
+              first.AsRegisterPairLow<Register>(),
+              ShifterOperand(second.AsRegisterPairLow<Register>()));
+      __ adc(out.AsRegisterPairHigh<Register>(),
+             first.AsRegisterPairHigh<Register>(),
+             ShifterOperand(second.AsRegisterPairHigh<Register>()));
       break;
 
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+    case Primitive::kPrimFloat:
+      __ vadds(out.As<SRegister>(), first.As<SRegister>(), second.As<SRegister>());
+      break;
+
+    case Primitive::kPrimDouble:
+      __ vaddd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
       break;
 
     default:
-      LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
+      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
   }
 }
 
 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: {
+      bool output_overlaps = (sub->GetResultType() == Primitive::kPrimLong);
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
-      locations->SetOut(Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), output_overlaps);
       break;
     }
-
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
       break;
-
+    }
     default:
-      LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
+      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
   }
-  sub->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
   LocationSummary* locations = sub->GetLocations();
+  Location out = locations->Out();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
   switch (sub->GetResultType()) {
     case Primitive::kPrimInt: {
-      if (locations->InAt(1).IsRegister()) {
-        __ sub(locations->Out().AsArm().AsCoreRegister(),
-               locations->InAt(0).AsArm().AsCoreRegister(),
-               ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
+      if (second.IsRegister()) {
+        __ sub(out.As<Register>(), first.As<Register>(), ShifterOperand(second.As<Register>()));
       } else {
-        __ AddConstant(locations->Out().AsArm().AsCoreRegister(),
-                       locations->InAt(0).AsArm().AsCoreRegister(),
-                       -locations->InAt(1).GetConstant()->AsIntConstant()->GetValue());
+        __ AddConstant(out.As<Register>(),
+                       first.As<Register>(),
+                       -second.GetConstant()->AsIntConstant()->GetValue());
       }
       break;
     }
 
-    case Primitive::kPrimLong:
-      __ subs(locations->Out().AsArm().AsRegisterPairLow(),
-              locations->InAt(0).AsArm().AsRegisterPairLow(),
-              ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow()));
-      __ sbc(locations->Out().AsArm().AsRegisterPairHigh(),
-             locations->InAt(0).AsArm().AsRegisterPairHigh(),
-             ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh()));
+    case Primitive::kPrimLong: {
+      __ subs(out.AsRegisterPairLow<Register>(),
+              first.AsRegisterPairLow<Register>(),
+              ShifterOperand(second.AsRegisterPairLow<Register>()));
+      __ sbc(out.AsRegisterPairHigh<Register>(),
+             first.AsRegisterPairHigh<Register>(),
+             ShifterOperand(second.AsRegisterPairHigh<Register>()));
       break;
+    }
 
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
+    case Primitive::kPrimFloat: {
+      __ vsubs(out.As<SRegister>(), first.As<SRegister>(), second.As<SRegister>());
       break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ vsubd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
+      break;
+    }
+
 
     default:
-      LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
+      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
   }
 }
 
+void LocationsBuilderARM::VisitMul(HMul* mul) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:  {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARM::VisitMul(HMul* mul) {
+  LocationSummary* locations = mul->GetLocations();
+  Location out = locations->Out();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt: {
+      __ mul(out.As<Register>(), first.As<Register>(), second.As<Register>());
+      break;
+    }
+    case Primitive::kPrimLong: {
+      Register out_hi = out.AsRegisterPairHigh<Register>();
+      Register out_lo = out.AsRegisterPairLow<Register>();
+      Register in1_hi = first.AsRegisterPairHigh<Register>();
+      Register in1_lo = first.AsRegisterPairLow<Register>();
+      Register in2_hi = second.AsRegisterPairHigh<Register>();
+      Register in2_lo = second.AsRegisterPairLow<Register>();
+
+      // Extra checks to protect caused by the existence of R1_R2.
+      // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
+      // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
+      DCHECK_NE(out_hi, in1_lo);
+      DCHECK_NE(out_hi, in2_lo);
+
+      // input: in1 - 64 bits, in2 - 64 bits
+      // output: out
+      // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
+      // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
+      // parts: out.lo = (in1.lo * in2.lo)[31:0]
+
+      // IP <- in1.lo * in2.hi
+      __ mul(IP, in1_lo, in2_hi);
+      // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
+      __ mla(out_hi, in1_hi, in2_lo, IP);
+      // out.lo <- (in1.lo * in2.lo)[31:0];
+      __ umull(out_lo, IP, in1_lo, in2_lo);
+      // out.hi <- in2.hi * in1.lo +  in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
+      __ add(out_hi, out_hi, ShifterOperand(IP));
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ vmuls(out.As<SRegister>(), first.As<SRegister>(), second.As<SRegister>());
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ vmuld(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void LocationsBuilderARM::VisitDiv(HDiv* div) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall);
+  switch (div->GetResultType()) {
+    case Primitive::kPrimInt: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+    case Primitive::kPrimLong: {
+      LOG(FATAL) << "Not implemented div type" << div->GetResultType();
+      break;
+    }
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) {
+  LocationSummary* locations = div->GetLocations();
+  Location out = locations->Out();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+
+  switch (div->GetResultType()) {
+    case Primitive::kPrimInt: {
+      __ sdiv(out.As<Register>(), first.As<Register>(), second.As<Register>());
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      LOG(FATAL) << "Not implemented div type" << div->GetResultType();
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ vdivs(out.As<SRegister>(), first.As<SRegister>(), second.As<SRegister>());
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ vdivd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
+  }
+}
+
+void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
+}
+
+void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM(instruction);
+  codegen_->AddSlowPath(slow_path);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location value = locations->InAt(0);
+
+  DCHECK(value.IsRegister()) << value;
+  __ cmp(value.As<Register>(), ShifterOperand(0));
+  __ b(slow_path->GetEntryLabel(), EQ);
+}
+
 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);
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetOut(Location::RegisterLocation(R0));
 }
 
 void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
   InvokeRuntimeCallingConvention calling_convention;
-  LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
   __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
+  codegen_->InvokeRuntime(
+      QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
+}
 
-  int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocObjectWithAccessCheck).Int32Value();
-  __ ldr(LR, Address(TR, offset));
-  __ blx(LR);
+void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetOut(Location::RegisterLocation(R0));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+}
 
-  codegen_->RecordPcInfo(instruction->GetDexPc());
-  DCHECK(!codegen_->IsLeafMethod());
+void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
+  InvokeRuntimeCallingConvention calling_convention;
+  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+  __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
+  codegen_->InvokeRuntime(
+      QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
 }
 
 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,52 +1762,70 @@
     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   }
   locations->SetOut(location);
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
   // Nothing to do, the parameter is already at its location.
+  UNUSED(instruction);
 }
 
-void LocationsBuilderARM::VisitNot(HNot* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+void LocationsBuilderARM::VisitNot(HNot* not_) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
-void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  __ eor(locations->Out().AsArm().AsCoreRegister(),
-         locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(1));
+void InstructionCodeGeneratorARM::VisitNot(HNot* not_) {
+  LocationSummary* locations = not_->GetLocations();
+  Location out = locations->Out();
+  Location in = locations->InAt(0);
+  switch (not_->InputAt(0)->GetType()) {
+    case Primitive::kPrimBoolean:
+      __ eor(out.As<Register>(), in.As<Register>(), ShifterOperand(1));
+      break;
+
+    case Primitive::kPrimInt:
+      __ mvn(out.As<Register>(), ShifterOperand(in.As<Register>()));
+      break;
+
+    case Primitive::kPrimLong:
+      __ mvn(out.AsRegisterPairLow<Register>(),
+             ShifterOperand(in.AsRegisterPairLow<Register>()));
+      __ mvn(out.AsRegisterPairHigh<Register>(),
+             ShifterOperand(in.AsRegisterPairHigh<Register>()));
+      break;
+
+    default:
+      LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
+  }
 }
 
 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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
-  Label greater, done;
   LocationSummary* locations = compare->GetLocations();
   switch (compare->InputAt(0)->GetType()) {
     case Primitive::kPrimLong: {
-      Register output = locations->Out().AsArm().AsCoreRegister();
-      ArmManagedRegister left = locations->InAt(0).AsArm();
-      ArmManagedRegister right = locations->InAt(1).AsArm();
+      Register output = locations->Out().As<Register>();
+      Location left = locations->InAt(0);
+      Location right = locations->InAt(1);
       Label less, greater, done;
-      __ cmp(left.AsRegisterPairHigh(),
-             ShifterOperand(right.AsRegisterPairHigh()));  // Signed compare.
+      __ cmp(left.AsRegisterPairHigh<Register>(),
+             ShifterOperand(right.AsRegisterPairHigh<Register>()));  // Signed compare.
       __ b(&less, LT);
       __ b(&greater, GT);
       // Do LoadImmediate before any `cmp`, as LoadImmediate might affect
       // the status flags.
       __ LoadImmediate(output, 0);
-      __ cmp(left.AsRegisterPairLow(),
-             ShifterOperand(right.AsRegisterPairLow()));  // Unsigned compare.
+      __ cmp(left.AsRegisterPairLow<Register>(),
+             ShifterOperand(right.AsRegisterPairLow<Register>()));  // Unsigned compare.
       __ b(&done, EQ);
       __ b(&less, CC);
 
@@ -1081,183 +1845,206 @@
 }
 
 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) {
+  UNUSED(instruction);
   LOG(FATAL) << "Unreachable";
 }
 
 void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot;
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // Temporary registers for the write barrier.
-  if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+  if (is_object_type) {
     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();
+  Register obj = locations->InAt(0).As<Register>();
   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:
     case Primitive::kPrimByte: {
-      Register value = locations->InAt(1).AsArm().AsCoreRegister();
+      Register value = locations->InAt(1).As<Register>();
       __ StoreToOffset(kStoreByte, value, obj, offset);
       break;
     }
 
     case Primitive::kPrimShort:
     case Primitive::kPrimChar: {
-      Register value = locations->InAt(1).AsArm().AsCoreRegister();
+      Register value = locations->InAt(1).As<Register>();
       __ StoreToOffset(kStoreHalfword, value, obj, offset);
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      Register value = locations->InAt(1).AsArm().AsCoreRegister();
+      Register value = locations->InAt(1).As<Register>();
       __ StoreToOffset(kStoreWord, value, obj, offset);
       if (field_type == Primitive::kPrimNot) {
-        Register temp = locations->GetTemp(0).AsArm().AsCoreRegister();
-        Register card = locations->GetTemp(1).AsArm().AsCoreRegister();
+        Register temp = locations->GetTemp(0).As<Register>();
+        Register card = locations->GetTemp(1).As<Register>();
         codegen_->MarkGCCard(temp, card, obj, value);
       }
       break;
     }
 
     case Primitive::kPrimLong: {
-      ArmManagedRegister value = locations->InAt(1).AsArm();
-      __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset);
+      Location value = locations->InAt(1);
+      __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), obj, offset);
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << field_type;
+    case Primitive::kPrimFloat: {
+      SRegister value = locations->InAt(1).As<SRegister>();
+      __ StoreSToOffset(value, obj, offset);
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      DRegister value = FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>());
+      __ StoreDToOffset(value, obj, offset);
+      break;
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << field_type;
+      UNREACHABLE();
   }
 }
 
 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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsArm().AsCoreRegister();
+  Register obj = locations->InAt(0).As<Register>();
   uint32_t offset = instruction->GetFieldOffset().Uint32Value();
 
   switch (instruction->GetType()) {
     case Primitive::kPrimBoolean: {
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
       break;
     }
 
     case Primitive::kPrimByte: {
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
       break;
     }
 
     case Primitive::kPrimShort: {
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
       break;
     }
 
     case Primitive::kPrimChar: {
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       __ LoadFromOffset(kLoadWord, out, obj, offset);
       break;
     }
 
     case Primitive::kPrimLong: {
       // TODO: support volatile.
-      ArmManagedRegister out = locations->Out().AsArm();
-      __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset);
+      Location out = locations->Out();
+      __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), obj, offset);
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+    case Primitive::kPrimFloat: {
+      SRegister out = locations->Out().As<SRegister>();
+      __ LoadSFromOffset(out, obj, offset);
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      DRegister out = FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>());
+      __ LoadDFromOffset(out, obj, offset);
+      break;
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
   }
 }
 
 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);
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
 }
 
 void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction->GetDexPc());
+  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
   Location obj = locations->InAt(0);
-  DCHECK(obj.Equals(locations->Out()));
 
   if (obj.IsRegister()) {
-    __ cmp(obj.AsArm().AsCoreRegister(), ShifterOperand(0));
+    __ cmp(obj.As<Register>(), ShifterOperand(0));
+    __ b(slow_path->GetEntryLabel(), EQ);
+  } else {
+    DCHECK(obj.IsConstant()) << obj;
+    DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
+    __ b(slow_path->GetEntryLabel());
   }
-  __ b(slow_path->GetEntryLabel(), EQ);
 }
 
 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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsArm().AsCoreRegister();
+  Register obj = locations->InAt(0).As<Register>();
   Location index = locations->InAt(1);
 
   switch (instruction->GetType()) {
     case Primitive::kPrimBoolean: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
         __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
+        __ add(IP, obj, ShifterOperand(index.As<Register>()));
         __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset);
       }
       break;
@@ -1265,12 +2052,12 @@
 
     case Primitive::kPrimByte: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
         __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
+        __ add(IP, obj, ShifterOperand(index.As<Register>()));
         __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset);
       }
       break;
@@ -1278,12 +2065,12 @@
 
     case Primitive::kPrimShort: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
         __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
+        __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_2));
         __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset);
       }
       break;
@@ -1291,12 +2078,12 @@
 
     case Primitive::kPrimChar: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
         __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
+        __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_2));
         __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset);
       }
       break;
@@ -1306,12 +2093,12 @@
     case Primitive::kPrimNot: {
       DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
-      Register out = locations->Out().AsArm().AsCoreRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
         __ LoadFromOffset(kLoadWord, out, obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4));
+        __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_4));
         __ LoadFromOffset(kLoadWord, out, IP, data_offset);
       }
       break;
@@ -1319,13 +2106,13 @@
 
     case Primitive::kPrimLong: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
-      ArmManagedRegister out = locations->Out().AsArm();
+      Location out = locations->Out();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset);
+        __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8));
-        __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), IP, data_offset);
+        __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_8));
+        __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), IP, data_offset);
       }
       break;
     }
@@ -1333,45 +2120,46 @@
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
       LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
-
+      UNREACHABLE();
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
   }
 }
 
 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();
+    locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+    locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
   } 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();
+  Register obj = locations->InAt(0).As<Register>();
   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:
     case Primitive::kPrimByte: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
-      Register value = locations->InAt(2).AsArm().AsCoreRegister();
+      Register value = locations->InAt(2).As<Register>();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
         __ StoreToOffset(kStoreByte, value, obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
+        __ add(IP, obj, ShifterOperand(index.As<Register>()));
         __ StoreToOffset(kStoreByte, value, IP, data_offset);
       }
       break;
@@ -1380,12 +2168,12 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimChar: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
-      Register value = locations->InAt(2).AsArm().AsCoreRegister();
+      Register value = locations->InAt(2).As<Register>();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
         __ StoreToOffset(kStoreHalfword, value, obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
+        __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_2));
         __ StoreToOffset(kStoreHalfword, value, IP, data_offset);
       }
       break;
@@ -1393,35 +2181,31 @@
 
     case Primitive::kPrimInt: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
-      Register value = locations->InAt(2).AsArm().AsCoreRegister();
+      Register value = locations->InAt(2).As<Register>();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
         __ StoreToOffset(kStoreWord, value, obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4));
+        __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_4));
         __ StoreToOffset(kStoreWord, value, IP, data_offset);
       }
       break;
     }
 
     case Primitive::kPrimNot: {
-      int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value();
-      __ ldr(LR, Address(TR, offset));
-      __ blx(LR);
-      codegen_->RecordPcInfo(instruction->GetDexPc());
-      DCHECK(!codegen_->IsLeafMethod());
+      codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc());
       break;
     }
 
     case Primitive::kPrimLong: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
-      ArmManagedRegister value = locations->InAt(2).AsArm();
+      Location value = locations->InAt(2);
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset);
+        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), obj, offset);
       } else {
-        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8));
-        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), IP, data_offset);
+        __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_8));
+        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset);
       }
       break;
     }
@@ -1429,44 +2213,46 @@
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
       LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
-
+      UNREACHABLE();
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
   }
 }
 
 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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
-  Register obj = locations->InAt(0).AsArm().AsCoreRegister();
-  Register out = locations->Out().AsArm().AsCoreRegister();
+  Register obj = locations->InAt(0).As<Register>();
+  Register out = locations->Out().As<Register>();
   __ LoadFromOffset(kLoadWord, out, obj, offset);
 }
 
 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);
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
 }
 
 void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
-      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
-  Register index = locations->InAt(0).AsArm().AsCoreRegister();
-  Register length = locations->InAt(1).AsArm().AsCoreRegister();
+  Register index = locations->InAt(0).As<Register>();
+  Register length = locations->InAt(1).As<Register>();
 
   __ cmp(index, ShifterOperand(length));
   __ b(slow_path->GetEntryLabel(), CS);
@@ -1487,9 +2273,11 @@
 
 void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp) {
   // Nothing to do, this is driven by the code generator.
+  UNUSED(temp);
 }
 
 void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction) {
+  UNUSED(instruction);
   LOG(FATAL) << "Unreachable";
 }
 
@@ -1497,6 +2285,43 @@
   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);
+
+  __ LoadFromOffset(
+      kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmWordSize>().Int32Value());
+  __ cmp(IP, ShifterOperand(0));
+  // TODO: Figure out the branch offsets and use cbz/cbnz.
+  if (successor == nullptr) {
+    __ b(slow_path->GetEntryLabel(), NE);
+    __ Bind(slow_path->GetReturnLabel());
+  } else {
+    __ b(codegen_->GetLabelOf(successor), EQ);
+    __ b(slow_path->GetEntryLabel());
+  }
+}
+
 ArmAssembler* ParallelMoveResolverARM::GetAssembler() const {
   return codegen_->GetAssembler();
 }
@@ -1508,15 +2333,15 @@
 
   if (source.IsRegister()) {
     if (destination.IsRegister()) {
-      __ Mov(destination.AsArm().AsCoreRegister(), source.AsArm().AsCoreRegister());
+      __ Mov(destination.As<Register>(), source.As<Register>());
     } else {
       DCHECK(destination.IsStackSlot());
-      __ StoreToOffset(kStoreWord, source.AsArm().AsCoreRegister(),
+      __ StoreToOffset(kStoreWord, source.As<Register>(),
                        SP, destination.GetStackIndex());
     }
   } else if (source.IsStackSlot()) {
     if (destination.IsRegister()) {
-      __ LoadFromOffset(kLoadWord, destination.AsArm().AsCoreRegister(),
+      __ LoadFromOffset(kLoadWord, destination.As<Register>(),
                         SP, source.GetStackIndex());
     } else {
       DCHECK(destination.IsStackSlot());
@@ -1525,14 +2350,14 @@
     }
   } else {
     DCHECK(source.IsConstant());
-    DCHECK(source.GetConstant()->AsIntConstant() != nullptr);
+    DCHECK(source.GetConstant()->IsIntConstant());
     int32_t value = source.GetConstant()->AsIntConstant()->GetValue();
     if (destination.IsRegister()) {
-      __ LoadImmediate(destination.AsArm().AsCoreRegister(), value);
+      __ LoadImmediate(destination.As<Register>(), value);
     } else {
       DCHECK(destination.IsStackSlot());
       __ LoadImmediate(IP, value);
-      __ str(IP, Address(SP, destination.GetStackIndex()));
+      __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
     }
   }
 }
@@ -1560,15 +2385,15 @@
   Location destination = move->GetDestination();
 
   if (source.IsRegister() && destination.IsRegister()) {
-    DCHECK_NE(source.AsArm().AsCoreRegister(), IP);
-    DCHECK_NE(destination.AsArm().AsCoreRegister(), IP);
-    __ Mov(IP, source.AsArm().AsCoreRegister());
-    __ Mov(source.AsArm().AsCoreRegister(), destination.AsArm().AsCoreRegister());
-    __ Mov(destination.AsArm().AsCoreRegister(), IP);
+    DCHECK_NE(source.As<Register>(), IP);
+    DCHECK_NE(destination.As<Register>(), IP);
+    __ Mov(IP, source.As<Register>());
+    __ Mov(source.As<Register>(), destination.As<Register>());
+    __ Mov(destination.As<Register>(), IP);
   } else if (source.IsRegister() && destination.IsStackSlot()) {
-    Exchange(source.AsArm().AsCoreRegister(), destination.GetStackIndex());
+    Exchange(source.As<Register>(), destination.GetStackIndex());
   } else if (source.IsStackSlot() && destination.IsRegister()) {
-    Exchange(destination.AsArm().AsCoreRegister(), source.GetStackIndex());
+    Exchange(destination.As<Register>(), source.GetStackIndex());
   } else if (source.IsStackSlot() && destination.IsStackSlot()) {
     Exchange(source.GetStackIndex(), destination.GetStackIndex());
   } else {
@@ -1584,5 +2409,303 @@
   __ Pop(static_cast<Register>(reg));
 }
 
+void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
+  LocationSummary::CallKind call_kind = cls->CanCallRuntime()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) {
+  Register out = cls->GetLocations()->Out().As<Register>();
+  if (cls->IsReferrersClass()) {
+    DCHECK(!cls->CanCallRuntime());
+    DCHECK(!cls->MustGenerateClinitCheck());
+    codegen_->LoadCurrentMethod(out);
+    __ LoadFromOffset(kLoadWord, out, out, mirror::ArtMethod::DeclaringClassOffset().Int32Value());
+  } else {
+    DCHECK(cls->CanCallRuntime());
+    codegen_->LoadCurrentMethod(out);
+    __ LoadFromOffset(
+        kLoadWord, out, out, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value());
+    __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+
+    SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
+        cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+    codegen_->AddSlowPath(slow_path);
+    __ cmp(out, ShifterOperand(0));
+    __ b(slow_path->GetEntryLabel(), EQ);
+    if (cls->MustGenerateClinitCheck()) {
+      GenerateClassInitializationCheck(slow_path, out);
+    } else {
+      __ Bind(slow_path->GetExitLabel());
+    }
+  }
+}
+
+void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
+  locations->SetInAt(0, Location::RequiresRegister());
+  if (check->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
+}
+
+void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) {
+  // We assume the class is not null.
+  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
+      check->GetLoadClass(), check, check->GetDexPc(), true);
+  codegen_->AddSlowPath(slow_path);
+  GenerateClassInitializationCheck(slow_path, check->GetLocations()->InAt(0).As<Register>());
+}
+
+void InstructionCodeGeneratorARM::GenerateClassInitializationCheck(
+    SlowPathCodeARM* slow_path, Register class_reg) {
+  __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value());
+  __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized));
+  __ b(slow_path->GetEntryLabel(), LT);
+  // Even if the initialized flag is set, we may be in a situation where caches are not synced
+  // properly. Therefore, we do a memory fence.
+  __ dmb(ISH);
+  __ Bind(slow_path->GetExitLabel());
+}
+
+void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register cls = locations->InAt(0).As<Register>();
+  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+
+  switch (instruction->GetType()) {
+    case Primitive::kPrimBoolean: {
+      Register out = locations->Out().As<Register>();
+      __ LoadFromOffset(kLoadUnsignedByte, out, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimByte: {
+      Register out = locations->Out().As<Register>();
+      __ LoadFromOffset(kLoadSignedByte, out, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimShort: {
+      Register out = locations->Out().As<Register>();
+      __ LoadFromOffset(kLoadSignedHalfword, out, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimChar: {
+      Register out = locations->Out().As<Register>();
+      __ LoadFromOffset(kLoadUnsignedHalfword, out, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      Register out = locations->Out().As<Register>();
+      __ LoadFromOffset(kLoadWord, out, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      // TODO: support volatile.
+      Location out = locations->Out();
+      __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      SRegister out = locations->Out().As<SRegister>();
+      __ LoadSFromOffset(out, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      DRegister out = FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>());
+      __ LoadDFromOffset(out, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot;
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  // Temporary registers for the write barrier.
+  if (is_object_type) {
+    locations->AddTemp(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+  }
+}
+
+void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register cls = locations->InAt(0).As<Register>();
+  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+  Primitive::Type field_type = instruction->GetFieldType();
+
+  switch (field_type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte: {
+      Register value = locations->InAt(1).As<Register>();
+      __ StoreToOffset(kStoreByte, value, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar: {
+      Register value = locations->InAt(1).As<Register>();
+      __ StoreToOffset(kStoreHalfword, value, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      Register value = locations->InAt(1).As<Register>();
+      __ StoreToOffset(kStoreWord, value, cls, offset);
+      if (field_type == Primitive::kPrimNot) {
+        Register temp = locations->GetTemp(0).As<Register>();
+        Register card = locations->GetTemp(1).As<Register>();
+        codegen_->MarkGCCard(temp, card, cls, value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      Location value = locations->InAt(1);
+      __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      SRegister value = locations->InAt(1).As<SRegister>();
+      __ StoreSToOffset(value, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      DRegister value = FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>());
+      __ StoreDToOffset(value, cls, offset);
+      break;
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << field_type;
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) {
+  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
+  codegen_->AddSlowPath(slow_path);
+
+  Register out = load->GetLocations()->Out().As<Register>();
+  codegen_->LoadCurrentMethod(out);
+  __ LoadFromOffset(
+      kLoadWord, out, out, mirror::ArtMethod::DexCacheStringsOffset().Int32Value());
+  __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+  __ cmp(out, ShifterOperand(0));
+  __ b(slow_path->GetEntryLabel(), EQ);
+  __ Bind(slow_path->GetExitLabel());
+}
+
+void LocationsBuilderARM::VisitLoadException(HLoadException* load) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) {
+  Register out = load->GetLocations()->Out().As<Register>();
+  int32_t offset = Thread::ExceptionOffset<kArmWordSize>().Int32Value();
+  __ LoadFromOffset(kLoadWord, out, TR, offset);
+  __ LoadImmediate(IP, 0);
+  __ StoreToOffset(kStoreWord, IP, TR, offset);
+}
+
+void LocationsBuilderARM::VisitThrow(HThrow* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+}
+
+void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) {
+  codegen_->InvokeRuntime(
+      QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc());
+}
+
+void LocationsBuilderARM::VisitTypeCheck(HTypeCheck* instruction) {
+  LocationSummary::CallKind call_kind = instruction->IsClassFinal()
+      ? LocationSummary::kNoCall
+      : LocationSummary::kCallOnSlowPath;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM::VisitTypeCheck(HTypeCheck* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register obj = locations->InAt(0).As<Register>();
+  Register cls = locations->InAt(1).As<Register>();
+  Register out = locations->Out().As<Register>();
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+  Label done, zero;
+  SlowPathCodeARM* slow_path = nullptr;
+
+  // Return 0 if `obj` is null.
+  // TODO: avoid this check if we know obj is not null.
+  __ cmp(obj, ShifterOperand(0));
+  __ b(&zero, EQ);
+  // Compare the class of `obj` with `cls`.
+  __ LoadFromOffset(kLoadWord, out, obj, class_offset);
+  __ cmp(out, ShifterOperand(cls));
+  if (instruction->IsClassFinal()) {
+    // Classes must be equal for the instanceof to succeed.
+    __ b(&zero, NE);
+    __ LoadImmediate(out, 1);
+    __ b(&done);
+  } else {
+    // If the classes are not equal, we go into a slow path.
+    DCHECK(locations->OnlyCallsOnSlowPath());
+    slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(
+        instruction, Location::RegisterLocation(out));
+    codegen_->AddSlowPath(slow_path);
+    __ b(slow_path->GetEntryLabel(), NE);
+    __ LoadImmediate(out, 1);
+    __ b(&done);
+  }
+  __ Bind(&zero);
+  __ LoadImmediate(out, 0);
+  if (slow_path != nullptr) {
+    __ Bind(slow_path->GetExitLabel());
+  }
+  __ Bind(&done);
+}
+
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 610625c..5d51993 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -26,17 +26,24 @@
 namespace arm {
 
 class CodeGeneratorARM;
+class SlowPathCodeARM;
 
 static constexpr size_t kArmWordSize = 4;
 
 static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 };
 static constexpr RegisterPair kParameterCorePairRegisters[] = { R1_R2, R2_R3 };
 static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
+static constexpr SRegister kParameterFpuRegisters[] =
+    { S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15 };
+static constexpr size_t kParameterFpuRegistersLength = arraysize(kParameterFpuRegisters);
 
-class InvokeDexCallingConvention : public CallingConvention<Register> {
+class InvokeDexCallingConvention : public CallingConvention<Register, SRegister> {
  public:
   InvokeDexCallingConvention()
-      : CallingConvention(kParameterCoreRegisters, kParameterCoreRegistersLength) {}
+      : CallingConvention(kParameterCoreRegisters,
+                          kParameterCoreRegistersLength,
+                          kParameterFpuRegisters,
+                          kParameterFpuRegistersLength) {}
 
   RegisterPair GetRegisterPairAt(size_t argument_index) {
     DCHECK_LT(argument_index + 1, GetNumberOfRegisters());
@@ -49,13 +56,18 @@
 
 class InvokeDexCallingConventionVisitor {
  public:
-  InvokeDexCallingConventionVisitor() : gp_index_(0) {}
+  InvokeDexCallingConventionVisitor()
+      : gp_index_(0), float_index_(0), double_index_(0), stack_index_(0) {}
 
   Location GetNextLocation(Primitive::Type type);
+  Location GetReturnLocation(Primitive::Type type);
 
  private:
   InvokeDexCallingConvention calling_convention;
   uint32_t gp_index_;
+  uint32_t float_index_;
+  uint32_t double_index_;
+  uint32_t stack_index_;
 
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor);
 };
@@ -65,10 +77,10 @@
   ParallelMoveResolverARM(ArenaAllocator* allocator, CodeGeneratorARM* codegen)
       : ParallelMoveResolver(allocator), codegen_(codegen) {}
 
-  virtual void EmitMove(size_t index) OVERRIDE;
-  virtual void EmitSwap(size_t index) OVERRIDE;
-  virtual void SpillScratch(int reg) OVERRIDE;
-  virtual void RestoreScratch(int reg) OVERRIDE;
+  void EmitMove(size_t index) OVERRIDE;
+  void EmitSwap(size_t index) OVERRIDE;
+  void SpillScratch(int reg) OVERRIDE;
+  void RestoreScratch(int reg) OVERRIDE;
 
   ArmAssembler* GetAssembler() const;
 
@@ -83,16 +95,18 @@
 
 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)     \
-  virtual void Visit##name(H##name* instr);
+#define DECLARE_VISIT_INSTRUCTION(name, super)     \
+  void Visit##name(H##name* instr);
 
   FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
+  void HandleInvoke(HInvoke* invoke);
+
  private:
   CodeGeneratorARM* const codegen_;
   InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -104,17 +118,22 @@
  public:
   InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen);
 
-#define DECLARE_VISIT_INSTRUCTION(name)     \
-  virtual void Visit##name(H##name* instr);
+#define DECLARE_VISIT_INSTRUCTION(name, super)     \
+  void Visit##name(H##name* instr);
 
   FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
   ArmAssembler* GetAssembler() const { return assembler_; }
-  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);
+  void GenerateClassInitializationCheck(SlowPathCodeARM* slow_path, Register class_reg);
+
   ArmAssembler* const assembler_;
   CodeGeneratorARM* const codegen_;
 
@@ -124,54 +143,54 @@
 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;
+  void GenerateFrameEntry() OVERRIDE;
+  void GenerateFrameExit() OVERRIDE;
+  void Bind(HBasicBlock* block) OVERRIDE;
+  void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+  size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
 
-  virtual size_t GetWordSize() const OVERRIDE {
+  size_t GetWordSize() const OVERRIDE {
     return kArmWordSize;
   }
 
-  virtual size_t FrameEntrySpillSize() const OVERRIDE;
+  size_t FrameEntrySpillSize() const OVERRIDE;
 
-  virtual HGraphVisitor* GetLocationBuilder() OVERRIDE {
+  HGraphVisitor* GetLocationBuilder() OVERRIDE {
     return &location_builder_;
   }
 
-  virtual HGraphVisitor* GetInstructionVisitor() OVERRIDE {
+  HGraphVisitor* GetInstructionVisitor() OVERRIDE {
     return &instruction_visitor_;
   }
 
-  virtual ArmAssembler* GetAssembler() OVERRIDE {
+  ArmAssembler* GetAssembler() OVERRIDE {
     return &assembler_;
   }
 
-  virtual void SetupBlockedRegisters(bool* blocked_registers) const OVERRIDE;
-  virtual ManagedRegister AllocateFreeRegister(
-      Primitive::Type type, bool* blocked_registers) const OVERRIDE;
-  virtual size_t GetNumberOfRegisters() const OVERRIDE;
-
-  virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
-
-  virtual size_t GetNumberOfCoreRegisters() const OVERRIDE {
-    return kNumberOfCoreRegisters;
+  uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+    return GetLabelOf(block)->Position();
   }
 
-  virtual size_t GetNumberOfFloatingPointRegisters() const OVERRIDE {
-    return kNumberOfDRegisters;
-  }
+  void SetupBlockedRegisters() const OVERRIDE;
 
-  virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
-  virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
+  Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
+
+  Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
+
+  void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
+  void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
+
+  // Blocks all register pairs made out of blocked core registers.
+  void UpdateBlockedPairRegisters() const;
 
   ParallelMoveResolverARM* GetMoveResolver() {
     return &move_resolver_;
   }
 
-  virtual InstructionSet GetInstructionSet() const OVERRIDE {
+  InstructionSet GetInstructionSet() const OVERRIDE {
     return InstructionSet::kThumb2;
   }
 
@@ -180,10 +199,26 @@
   // Helper method to move a 64bits value between two locations.
   void Move64(Location destination, Location source);
 
+  // Load current method into `reg`.
+  void LoadCurrentMethod(Register reg);
+
+  // Generate code to invoke a runtime entry point.
+  void InvokeRuntime(int32_t offset, HInstruction* instruction, uint32_t dex_pc);
+
   // Emit a write barrier.
   void MarkGCCard(Register temp, Register card, Register object, Register value);
 
+  Label* GetLabelOf(HBasicBlock* block) const {
+    return block_labels_.GetRawStorage() + block->GetBlockId();
+  }
+
+  void Initialize() OVERRIDE {
+    block_labels_.SetSize(GetGraph()->GetBlocks().Size());
+  }
+
  private:
+  // Labels for each block that will be compiled.
+  GrowableArray<Label> block_labels_;
   LocationsBuilderARM location_builder_;
   InstructionCodeGeneratorARM instruction_visitor_;
   ParallelMoveResolverARM move_resolver_;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
new file mode 100644
index 0000000..4dc836f
--- /dev/null
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -0,0 +1,1484 @@
+/*
+ * 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 "code_generator_arm64.h"
+
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "gc/accounting/card_table.h"
+#include "mirror/array-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/class.h"
+#include "thread.h"
+#include "utils/arm64/assembler_arm64.h"
+#include "utils/assembler.h"
+#include "utils/stack_checks.h"
+
+
+using namespace vixl;   // NOLINT(build/namespaces)
+
+#ifdef __
+#error "ARM64 Codegen VIXL macro-assembler macro already defined."
+#endif
+
+
+namespace art {
+
+namespace arm64 {
+
+// TODO: clean-up some of the constant definitions.
+static constexpr size_t kHeapRefSize = sizeof(mirror::HeapReference<mirror::Object>);
+static constexpr int kCurrentMethodStackOffset = 0;
+
+namespace {
+
+bool IsFPType(Primitive::Type type) {
+  return type == Primitive::kPrimFloat || type == Primitive::kPrimDouble;
+}
+
+bool Is64BitType(Primitive::Type type) {
+  return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
+}
+
+// Convenience helpers to ease conversion to and from VIXL operands.
+
+int VIXLRegCodeFromART(int code) {
+  // TODO: static check?
+  DCHECK_EQ(SP, 31);
+  DCHECK_EQ(WSP, 31);
+  DCHECK_EQ(XZR, 32);
+  DCHECK_EQ(WZR, 32);
+  if (code == SP) {
+    return vixl::kSPRegInternalCode;
+  }
+  if (code == XZR) {
+    return vixl::kZeroRegCode;
+  }
+  return code;
+}
+
+int ARTRegCodeFromVIXL(int code) {
+  // TODO: static check?
+  DCHECK_EQ(SP, 31);
+  DCHECK_EQ(WSP, 31);
+  DCHECK_EQ(XZR, 32);
+  DCHECK_EQ(WZR, 32);
+  if (code == vixl::kSPRegInternalCode) {
+    return SP;
+  }
+  if (code == vixl::kZeroRegCode) {
+    return XZR;
+  }
+  return code;
+}
+
+Register XRegisterFrom(Location location) {
+  return Register::XRegFromCode(VIXLRegCodeFromART(location.reg()));
+}
+
+Register WRegisterFrom(Location location) {
+  return Register::WRegFromCode(VIXLRegCodeFromART(location.reg()));
+}
+
+Register RegisterFrom(Location location, Primitive::Type type) {
+  DCHECK(type != Primitive::kPrimVoid && !IsFPType(type));
+  return type == Primitive::kPrimLong ? XRegisterFrom(location) : WRegisterFrom(location);
+}
+
+Register OutputRegister(HInstruction* instr) {
+  return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
+}
+
+Register InputRegisterAt(HInstruction* instr, int input_index) {
+  return RegisterFrom(instr->GetLocations()->InAt(input_index),
+                      instr->InputAt(input_index)->GetType());
+}
+
+FPRegister DRegisterFrom(Location location) {
+  return FPRegister::DRegFromCode(location.reg());
+}
+
+FPRegister SRegisterFrom(Location location) {
+  return FPRegister::SRegFromCode(location.reg());
+}
+
+FPRegister FPRegisterFrom(Location location, Primitive::Type type) {
+  DCHECK(IsFPType(type));
+  return type == Primitive::kPrimDouble ? DRegisterFrom(location) : SRegisterFrom(location);
+}
+
+FPRegister OutputFPRegister(HInstruction* instr) {
+  return FPRegisterFrom(instr->GetLocations()->Out(), instr->GetType());
+}
+
+FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
+  return FPRegisterFrom(instr->GetLocations()->InAt(input_index),
+                        instr->InputAt(input_index)->GetType());
+}
+
+int64_t Int64ConstantFrom(Location location) {
+  HConstant* instr = location.GetConstant();
+  return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue()
+                                : instr->AsLongConstant()->GetValue();
+}
+
+Operand OperandFrom(Location location, Primitive::Type type) {
+  if (location.IsRegister()) {
+    return Operand(RegisterFrom(location, type));
+  } else {
+    return Operand(Int64ConstantFrom(location));
+  }
+}
+
+Operand InputOperandAt(HInstruction* instr, int input_index) {
+  return OperandFrom(instr->GetLocations()->InAt(input_index),
+                     instr->InputAt(input_index)->GetType());
+}
+
+MemOperand StackOperandFrom(Location location) {
+  return MemOperand(sp, location.GetStackIndex());
+}
+
+MemOperand HeapOperand(const Register& base, Offset offset) {
+  // A heap reference must be 32bit, so fit in a W register.
+  DCHECK(base.IsW());
+  return MemOperand(base.X(), offset.SizeValue());
+}
+
+MemOperand HeapOperandFrom(Location location, Primitive::Type type, Offset offset) {
+  return HeapOperand(RegisterFrom(location, type), offset);
+}
+
+Location LocationFrom(const Register& reg) {
+  return Location::RegisterLocation(ARTRegCodeFromVIXL(reg.code()));
+}
+
+Location LocationFrom(const FPRegister& fpreg) {
+  return Location::FpuRegisterLocation(fpreg.code());
+}
+
+}  // namespace
+
+inline Condition ARM64Condition(IfCondition cond) {
+  switch (cond) {
+    case kCondEQ: return eq;
+    case kCondNE: return ne;
+    case kCondLT: return lt;
+    case kCondLE: return le;
+    case kCondGT: return gt;
+    case kCondGE: return ge;
+    default:
+      LOG(FATAL) << "Unknown if condition";
+  }
+  return nv;  // Unreachable.
+}
+
+Location ARM64ReturnLocation(Primitive::Type return_type) {
+  DCHECK_NE(return_type, Primitive::kPrimVoid);
+  // Note that in practice, `LocationFrom(x0)` and `LocationFrom(w0)` create the
+  // same Location object, and so do `LocationFrom(d0)` and `LocationFrom(s0)`,
+  // but we use the exact registers for clarity.
+  if (return_type == Primitive::kPrimFloat) {
+    return LocationFrom(s0);
+  } else if (return_type == Primitive::kPrimDouble) {
+    return LocationFrom(d0);
+  } else if (return_type == Primitive::kPrimLong) {
+    return LocationFrom(x0);
+  } else {
+    return LocationFrom(w0);
+  }
+}
+
+static const Register kRuntimeParameterCoreRegisters[] = { x0, x1, x2, x3, x4, x5, x6, x7 };
+static constexpr size_t kRuntimeParameterCoreRegistersLength =
+    arraysize(kRuntimeParameterCoreRegisters);
+static const FPRegister kRuntimeParameterFpuRegisters[] = { };
+static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+
+class InvokeRuntimeCallingConvention : public CallingConvention<Register, FPRegister> {
+ public:
+  static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
+
+  InvokeRuntimeCallingConvention()
+      : CallingConvention(kRuntimeParameterCoreRegisters,
+                          kRuntimeParameterCoreRegistersLength,
+                          kRuntimeParameterFpuRegisters,
+                          kRuntimeParameterFpuRegistersLength) {}
+
+  Location GetReturnLocation(Primitive::Type return_type);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
+};
+
+Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type return_type) {
+  return ARM64ReturnLocation(return_type);
+}
+
+#define __ reinterpret_cast<Arm64Assembler*>(codegen->GetAssembler())->vixl_masm_->
+
+class SlowPathCodeARM64 : public SlowPathCode {
+ public:
+  SlowPathCodeARM64() : entry_label_(), exit_label_() {}
+
+  vixl::Label* GetEntryLabel() { return &entry_label_; }
+  vixl::Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  vixl::Label entry_label_;
+  vixl::Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64);
+};
+
+class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+  explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction,
+                                    Location index_location,
+                                    Location length_location)
+      : instruction_(instruction),
+        index_location_(index_location),
+        length_location_(length_location) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorARM64* arm64_codegen = reinterpret_cast<CodeGeneratorARM64*>(codegen);
+    __ Bind(GetEntryLabel());
+    InvokeRuntimeCallingConvention calling_convention;
+    arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(0)),
+                              index_location_, Primitive::kPrimInt);
+    arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(1)),
+                              length_location_, Primitive::kPrimInt);
+    size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowArrayBounds).SizeValue();
+    __ Ldr(lr, MemOperand(tr, offset));
+    __ Blr(lr);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+  }
+
+ private:
+  HBoundsCheck* const instruction_;
+  const Location index_location_;
+  const Location length_location_;
+
+  DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64);
+};
+
+class NullCheckSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+  explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    __ Bind(GetEntryLabel());
+    int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowNullPointer).Int32Value();
+    __ Ldr(lr, MemOperand(tr, offset));
+    __ Blr(lr);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+  }
+
+ private:
+  HNullCheck* const instruction_;
+
+  DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64);
+};
+
+class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+  explicit SuspendCheckSlowPathARM64(HSuspendCheck* instruction,
+                                     HBasicBlock* successor)
+      : instruction_(instruction), successor_(successor) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pTestSuspend).SizeValue();
+    __ Bind(GetEntryLabel());
+    __ Ldr(lr, MemOperand(tr, offset));
+    __ Blr(lr);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+    __ B(GetReturnLabel());
+  }
+
+  vixl::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.
+  vixl::Label return_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM64);
+};
+
+#undef __
+
+Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
+  Location next_location;
+  if (type == Primitive::kPrimVoid) {
+    LOG(FATAL) << "Unreachable type " << type;
+  }
+
+  if (IsFPType(type) && (fp_index_ < calling_convention.GetNumberOfFpuRegisters())) {
+    next_location = LocationFrom(calling_convention.GetFpuRegisterAt(fp_index_++));
+  } else if (!IsFPType(type) && (gp_index_ < calling_convention.GetNumberOfRegisters())) {
+    next_location = LocationFrom(calling_convention.GetRegisterAt(gp_index_++));
+  } else {
+    size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
+    next_location = Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
+                                      : Location::StackSlot(stack_offset);
+  }
+
+  // Space on the stack is reserved for all arguments.
+  stack_index_ += Is64BitType(type) ? 2 : 1;
+  return next_location;
+}
+
+CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph)
+    : CodeGenerator(graph,
+                    kNumberOfAllocatableRegisters,
+                    kNumberOfAllocatableFPRegisters,
+                    kNumberOfAllocatableRegisterPairs),
+      block_labels_(nullptr),
+      location_builder_(graph, this),
+      instruction_visitor_(graph, this) {}
+
+#define __ reinterpret_cast<Arm64Assembler*>(GetAssembler())->vixl_masm_->
+
+void CodeGeneratorARM64::GenerateFrameEntry() {
+  // TODO: Add proper support for the stack overflow check.
+  UseScratchRegisterScope temps(assembler_.vixl_masm_);
+  Register temp = temps.AcquireX();
+  __ Add(temp, sp, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64)));
+  __ Ldr(temp, MemOperand(temp, 0));
+  RecordPcInfo(nullptr, 0);
+
+  CPURegList preserved_regs = GetFramePreservedRegisters();
+  int frame_size = GetFrameSize();
+  core_spill_mask_ |= preserved_regs.list();
+
+  __ Str(w0, MemOperand(sp, -frame_size, PreIndex));
+  __ PokeCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes());
+
+  // Stack layout:
+  // sp[frame_size - 8]        : lr.
+  // ...                       : other preserved registers.
+  // sp[frame_size - regs_size]: first preserved register.
+  // ...                       : reserved frame space.
+  // sp[0]                     : context pointer.
+}
+
+void CodeGeneratorARM64::GenerateFrameExit() {
+  int frame_size = GetFrameSize();
+  CPURegList preserved_regs = GetFramePreservedRegisters();
+  __ PeekCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes());
+  __ Drop(frame_size);
+}
+
+void CodeGeneratorARM64::Bind(HBasicBlock* block) {
+  __ Bind(GetLabelOf(block));
+}
+
+void CodeGeneratorARM64::Move(HInstruction* instruction,
+                              Location location,
+                              HInstruction* move_for) {
+  LocationSummary* locations = instruction->GetLocations();
+  if (locations != nullptr && locations->Out().Equals(location)) {
+    return;
+  }
+
+  Primitive::Type type = instruction->GetType();
+  DCHECK_NE(type, Primitive::kPrimVoid);
+
+  if (instruction->IsIntConstant() || instruction->IsLongConstant()) {
+    int64_t value = instruction->IsIntConstant() ? instruction->AsIntConstant()->GetValue()
+                                                 : instruction->AsLongConstant()->GetValue();
+    if (location.IsRegister()) {
+      Register dst = RegisterFrom(location, type);
+      DCHECK((instruction->IsIntConstant() && dst.Is32Bits()) ||
+             (instruction->IsLongConstant() && dst.Is64Bits()));
+      __ Mov(dst, value);
+    } else {
+      DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot());
+      UseScratchRegisterScope temps(assembler_.vixl_masm_);
+      Register temp = instruction->IsIntConstant() ? temps.AcquireW() : temps.AcquireX();
+      __ Mov(temp, value);
+      __ Str(temp, StackOperandFrom(location));
+    }
+  } else if (instruction->IsTemporary()) {
+    Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
+    MoveHelper(location, temp_location, type);
+  } else if (instruction->IsLoadLocal()) {
+    uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
+    if (Is64BitType(type)) {
+      MoveHelper(location, Location::DoubleStackSlot(stack_slot), type);
+    } else {
+      MoveHelper(location, Location::StackSlot(stack_slot), type);
+    }
+
+  } else {
+    DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
+    MoveHelper(location, locations->Out(), type);
+  }
+}
+
+size_t CodeGeneratorARM64::FrameEntrySpillSize() const {
+  return GetFramePreservedRegistersSize();
+}
+
+Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
+  Primitive::Type type = load->GetType();
+
+  switch (type) {
+    case Primitive::kPrimNot:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      return Location::StackSlot(GetStackSlot(load->GetLocal()));
+
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
+      return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
+
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unexpected type " << type;
+  }
+
+  LOG(FATAL) << "Unreachable";
+  return Location::NoLocation();
+}
+
+void CodeGeneratorARM64::MarkGCCard(Register object, Register value) {
+  UseScratchRegisterScope temps(assembler_.vixl_masm_);
+  Register card = temps.AcquireX();
+  Register temp = temps.AcquireX();
+  vixl::Label done;
+  __ Cbz(value, &done);
+  __ Ldr(card, MemOperand(tr, Thread::CardTableOffset<kArm64WordSize>().Int32Value()));
+  __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
+  __ Strb(card, MemOperand(card, temp));
+  __ Bind(&done);
+}
+
+void CodeGeneratorARM64::SetupBlockedRegisters() const {
+  // Block reserved registers:
+  //   ip0 (VIXL temporary)
+  //   ip1 (VIXL temporary)
+  //   xSuspend (Suspend counter)
+  //   lr
+  // sp is not part of the allocatable registers, so we don't need to block it.
+  // TODO: Avoid blocking callee-saved registers, and instead preserve them
+  // where necessary.
+  CPURegList reserved_core_registers = vixl_reserved_core_registers;
+  reserved_core_registers.Combine(runtime_reserved_core_registers);
+  reserved_core_registers.Combine(quick_callee_saved_registers);
+  while (!reserved_core_registers.IsEmpty()) {
+    blocked_core_registers_[reserved_core_registers.PopLowestIndex().code()] = true;
+  }
+  CPURegList reserved_fp_registers = vixl_reserved_fp_registers;
+  reserved_fp_registers.Combine(CPURegList::GetCalleeSavedFP());
+  while (!reserved_core_registers.IsEmpty()) {
+    blocked_fpu_registers_[reserved_fp_registers.PopLowestIndex().code()] = true;
+  }
+}
+
+Location CodeGeneratorARM64::AllocateFreeRegister(Primitive::Type type) const {
+  if (type == Primitive::kPrimVoid) {
+    LOG(FATAL) << "Unreachable type " << type;
+  }
+
+  if (IsFPType(type)) {
+    ssize_t reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfAllocatableFPRegisters);
+    DCHECK_NE(reg, -1);
+    return Location::FpuRegisterLocation(reg);
+  } else {
+    ssize_t reg = FindFreeEntry(blocked_core_registers_, kNumberOfAllocatableRegisters);
+    DCHECK_NE(reg, -1);
+    return Location::RegisterLocation(reg);
+  }
+}
+
+void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const {
+  stream << Arm64ManagedRegister::FromXRegister(XRegister(reg));
+}
+
+void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
+  stream << Arm64ManagedRegister::FromDRegister(DRegister(reg));
+}
+
+void CodeGeneratorARM64::MoveHelper(Location destination,
+                                    Location source,
+                                    Primitive::Type type) {
+  if (source.Equals(destination)) {
+    return;
+  }
+  if (destination.IsRegister()) {
+    Register dst = RegisterFrom(destination, type);
+    if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
+      DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
+      __ Ldr(dst, StackOperandFrom(source));
+    } else {
+      __ Mov(dst, OperandFrom(source, type));
+    }
+  } else if (destination.IsFpuRegister()) {
+    FPRegister dst = FPRegisterFrom(destination, type);
+    if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
+      DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
+      __ Ldr(dst, StackOperandFrom(source));
+    } else if (source.IsFpuRegister()) {
+      __ Fmov(dst, FPRegisterFrom(source, type));
+    } else {
+      HConstant* cst = source.GetConstant();
+      if (cst->IsFloatConstant()) {
+        __ Fmov(dst, cst->AsFloatConstant()->GetValue());
+      } else {
+        DCHECK(cst->IsDoubleConstant());
+        __ Fmov(dst, cst->AsDoubleConstant()->GetValue());
+      }
+    }
+  } else {
+    DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
+    if (source.IsRegister()) {
+      __ Str(RegisterFrom(source, type), StackOperandFrom(destination));
+    } else if (source.IsFpuRegister()) {
+      __ Str(FPRegisterFrom(source, type), StackOperandFrom(destination));
+    } else {
+      UseScratchRegisterScope temps(assembler_.vixl_masm_);
+      Register temp = destination.IsDoubleStackSlot() ? temps.AcquireX() : temps.AcquireW();
+      __ Ldr(temp, StackOperandFrom(source));
+      __ Str(temp, StackOperandFrom(destination));
+    }
+  }
+}
+
+void CodeGeneratorARM64::Load(Primitive::Type type,
+                              vixl::Register dst,
+                              const vixl::MemOperand& src) {
+  switch (type) {
+    case Primitive::kPrimBoolean:
+      __ Ldrb(dst, src);
+      break;
+    case Primitive::kPrimByte:
+      __ Ldrsb(dst, src);
+      break;
+    case Primitive::kPrimShort:
+      __ Ldrsh(dst, src);
+      break;
+    case Primitive::kPrimChar:
+      __ Ldrh(dst, src);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot:
+    case Primitive::kPrimLong:
+      DCHECK(dst.Is64Bits() == (type == Primitive::kPrimLong));
+      __ Ldr(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << type;
+  }
+}
+
+void CodeGeneratorARM64::Store(Primitive::Type type,
+                               vixl::Register rt,
+                               const vixl::MemOperand& dst) {
+  switch (type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      __ Strb(rt, dst);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      __ Strh(rt, dst);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot:
+    case Primitive::kPrimLong:
+      DCHECK(rt.Is64Bits() == (type == Primitive::kPrimLong));
+      __ Str(rt, dst);
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << type;
+  }
+}
+
+#undef __
+#define __ GetAssembler()->vixl_masm_->
+
+InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph,
+                                                             CodeGeneratorARM64* codegen)
+      : HGraphVisitor(graph),
+        assembler_(codegen->GetAssembler()),
+        codegen_(codegen) {}
+
+#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M)              \
+  M(ClinitCheck)                                           \
+  M(DivZeroCheck)                                          \
+  M(InvokeInterface)                                       \
+  M(LoadClass)                                             \
+  M(LoadException)                                         \
+  M(LoadString)                                            \
+  M(ParallelMove)                                          \
+  M(StaticFieldGet)                                        \
+  M(StaticFieldSet)                                        \
+  M(Throw)                                                 \
+  M(TypeCheck)                                             \
+  M(TypeConversion)                                        \
+
+#define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode
+
+enum UnimplementedInstructionBreakCode {
+#define ENUM_UNIMPLEMENTED_INSTRUCTION(name) UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name),
+  FOR_EACH_UNIMPLEMENTED_INSTRUCTION(ENUM_UNIMPLEMENTED_INSTRUCTION)
+#undef ENUM_UNIMPLEMENTED_INSTRUCTION
+};
+
+#define DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS(name)                               \
+  void InstructionCodeGeneratorARM64::Visit##name(H##name* instr) {                   \
+    UNUSED(instr);                                                                    \
+    __ Brk(UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name));                               \
+  }                                                                                   \
+  void LocationsBuilderARM64::Visit##name(H##name* instr) {                           \
+    LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); \
+    locations->SetOut(Location::Any());                                               \
+  }
+  FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS)
+#undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS
+
+#undef UNIMPLEMENTED_INSTRUCTION_BREAK_CODE
+
+void LocationsBuilderARM64::HandleAddSub(HBinaryOperation* instr) {
+  DCHECK(instr->IsAdd() || instr->IsSub());
+  DCHECK_EQ(instr->InputCount(), 2U);
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
+  Primitive::Type type = instr->GetResultType();
+  switch (type) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1)));
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected " << instr->DebugName() << " type " << type;
+  }
+}
+
+void InstructionCodeGeneratorARM64::HandleAddSub(HBinaryOperation* instr) {
+  DCHECK(instr->IsAdd() || instr->IsSub());
+
+  Primitive::Type type = instr->GetType();
+
+  switch (type) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong: {
+      Register dst = OutputRegister(instr);
+      Register lhs = InputRegisterAt(instr, 0);
+      Operand rhs = InputOperandAt(instr, 1);
+      if (instr->IsAdd()) {
+        __ Add(dst, lhs, rhs);
+      } else {
+        __ Sub(dst, lhs, rhs);
+      }
+      break;
+    }
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      FPRegister dst = OutputFPRegister(instr);
+      FPRegister lhs = InputFPRegisterAt(instr, 0);
+      FPRegister rhs = InputFPRegisterAt(instr, 1);
+      if (instr->IsAdd()) {
+        __ Fadd(dst, lhs, rhs);
+      } else {
+        __ Fsub(dst, lhs, rhs);
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unexpected add/sub type " << type;
+  }
+}
+
+void LocationsBuilderARM64::VisitAdd(HAdd* instruction) {
+  HandleAddSub(instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitAdd(HAdd* instruction) {
+  HandleAddSub(instruction);
+}
+
+void LocationsBuilderARM64::VisitArrayGet(HArrayGet* 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());
+}
+
+void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Primitive::Type type = instruction->GetType();
+  Register obj = InputRegisterAt(instruction, 0);
+  Register out = OutputRegister(instruction);
+  Location index = locations->InAt(1);
+  size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value();
+  MemOperand source(obj);
+  UseScratchRegisterScope temps(GetAssembler()->vixl_masm_);
+
+  if (index.IsConstant()) {
+    offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type);
+    source = MemOperand(obj, offset);
+  } else {
+    Register temp = temps.AcquireSameSizeAs(obj);
+    Register index_reg = RegisterFrom(index, Primitive::kPrimInt);
+    __ Add(temp, obj, Operand(index_reg, LSL, Primitive::ComponentSizeShift(type)));
+    source = MemOperand(temp, offset);
+  }
+
+  codegen_->Load(type, out, source);
+}
+
+void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) {
+  __ Ldr(OutputRegister(instruction),
+         HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset()));
+}
+
+void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) {
+  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, LocationFrom(calling_convention.GetRegisterAt(0)));
+    locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
+    locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
+  } else {
+    locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+    locations->SetInAt(2, Location::RequiresRegister());
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
+  Primitive::Type value_type = instruction->GetComponentType();
+  if (value_type == Primitive::kPrimNot) {
+    __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAputObject).Int32Value()));
+    __ Blr(lr);
+    codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+    DCHECK(!codegen_->IsLeafMethod());
+  } else {
+    LocationSummary* locations = instruction->GetLocations();
+    Register obj = InputRegisterAt(instruction, 0);
+    Register value = InputRegisterAt(instruction, 2);
+    Location index = locations->InAt(1);
+    size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
+    MemOperand destination(obj);
+    UseScratchRegisterScope temps(GetAssembler()->vixl_masm_);
+
+    if (index.IsConstant()) {
+      offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type);
+      destination = MemOperand(obj, offset);
+    } else {
+      Register temp = temps.AcquireSameSizeAs(obj);
+      Register index_reg = InputRegisterAt(instruction, 1);
+      __ Add(temp, obj, Operand(index_reg, LSL, Primitive::ComponentSizeShift(value_type)));
+      destination = MemOperand(temp, offset);
+    }
+
+    codegen_->Store(value_type, value, destination);
+  }
+}
+
+void LocationsBuilderARM64::VisitCompare(HCompare* 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(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM64::VisitCompare(HCompare* instruction) {
+  Primitive::Type in_type = instruction->InputAt(0)->GetType();
+
+  DCHECK_EQ(in_type, Primitive::kPrimLong);
+  switch (in_type) {
+    case Primitive::kPrimLong: {
+      vixl::Label done;
+      Register result = OutputRegister(instruction);
+      Register left = InputRegisterAt(instruction, 0);
+      Operand right = InputOperandAt(instruction, 1);
+      __ Subs(result, left, right);
+      __ B(eq, &done);
+      __ Mov(result, 1);
+      __ Cneg(result, result, le);
+      __ Bind(&done);
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unimplemented compare type " << in_type;
+  }
+}
+
+void LocationsBuilderARM64::VisitCondition(HCondition* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+  if (instruction->NeedsMaterialization()) {
+    locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitCondition(HCondition* instruction) {
+  if (!instruction->NeedsMaterialization()) {
+    return;
+  }
+
+  LocationSummary* locations = instruction->GetLocations();
+  Register lhs = InputRegisterAt(instruction, 0);
+  Operand rhs = InputOperandAt(instruction, 1);
+  Register res = RegisterFrom(locations->Out(), instruction->GetType());
+  Condition cond = ARM64Condition(instruction->GetCondition());
+
+  __ Cmp(lhs, rhs);
+  __ Csel(res, vixl::Assembler::AppropriateZeroRegFor(res), Operand(1), InvertCondition(cond));
+}
+
+#define FOR_EACH_CONDITION_INSTRUCTION(M)                                                \
+  M(Equal)                                                                               \
+  M(NotEqual)                                                                            \
+  M(LessThan)                                                                            \
+  M(LessThanOrEqual)                                                                     \
+  M(GreaterThan)                                                                         \
+  M(GreaterThanOrEqual)
+#define DEFINE_CONDITION_VISITORS(Name)                                                  \
+void LocationsBuilderARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); }         \
+void InstructionCodeGeneratorARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); }
+FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS)
+#undef FOR_EACH_CONDITION_INSTRUCTION
+
+void LocationsBuilderARM64::VisitDiv(HDiv* div) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall);
+  switch (div->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) {
+  Primitive::Type type = div->GetResultType();
+  switch (type) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      __ Fdiv(OutputFPRegister(div), InputFPRegisterAt(div, 0), InputFPRegisterAt(div, 1));
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << type;
+  }
+}
+
+void LocationsBuilderARM64::VisitDoubleConstant(HDoubleConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARM64::VisitDoubleConstant(HDoubleConstant* constant) {
+  UNUSED(constant);
+  // Will be generated at use site.
+}
+
+void LocationsBuilderARM64::VisitExit(HExit* exit) {
+  exit->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitExit(HExit* exit) {
+  UNUSED(exit);
+  if (kIsDebugBuild) {
+    down_cast<Arm64Assembler*>(GetAssembler())->Comment("Unreachable");
+    __ Brk(0);    // TODO: Introduce special markers for such code locations.
+  }
+}
+
+void LocationsBuilderARM64::VisitFloatConstant(HFloatConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARM64::VisitFloatConstant(HFloatConstant* constant) {
+  UNUSED(constant);
+  // Will be generated at use site.
+}
+
+void LocationsBuilderARM64::VisitGoto(HGoto* got) {
+  got->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitGoto(HGoto* got) {
+  HBasicBlock* successor = got->GetSuccessor();
+  // TODO: Support for suspend checks emission.
+  if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
+    __ B(codegen_->GetLabelOf(successor));
+  }
+}
+
+void LocationsBuilderARM64::VisitIf(HIf* if_instr) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+  HInstruction* cond = if_instr->InputAt(0);
+  DCHECK(cond->IsCondition());
+  if (cond->AsCondition()->NeedsMaterialization()) {
+    locations->SetInAt(0, Location::RequiresRegister());
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitIf(HIf* if_instr) {
+  HInstruction* cond = if_instr->InputAt(0);
+  DCHECK(cond->IsCondition());
+  HCondition* condition = cond->AsCondition();
+  vixl::Label* true_target = codegen_->GetLabelOf(if_instr->IfTrueSuccessor());
+  vixl::Label* false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor());
+
+  // TODO: Support constant condition input in VisitIf.
+
+  if (condition->NeedsMaterialization()) {
+    // The condition instruction has been materialized, compare the output to 0.
+    Location cond_val = if_instr->GetLocations()->InAt(0);
+    DCHECK(cond_val.IsRegister());
+    __ Cbnz(InputRegisterAt(if_instr, 0), true_target);
+
+  } else {
+    // The condition instruction has not been materialized, use its inputs as
+    // the comparison and its condition as the branch condition.
+    Register lhs = InputRegisterAt(condition, 0);
+    Operand rhs = InputOperandAt(condition, 1);
+    Condition arm64_cond = ARM64Condition(condition->GetCondition());
+    if ((arm64_cond == eq || arm64_cond == ne) && rhs.IsImmediate() && (rhs.immediate() == 0)) {
+      if (arm64_cond == eq) {
+        __ Cbz(lhs, true_target);
+      } else {
+        __ Cbnz(lhs, true_target);
+      }
+    } else {
+      __ Cmp(lhs, rhs);
+      __ B(arm64_cond, true_target);
+    }
+  }
+
+  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
+    __ B(false_target);
+  }
+}
+
+void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+  MemOperand field = MemOperand(InputRegisterAt(instruction, 0),
+                                instruction->GetFieldOffset().Uint32Value());
+  codegen_->Load(instruction->GetType(), OutputRegister(instruction), field);
+}
+
+void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+  Primitive::Type field_type = instruction->GetFieldType();
+  Register value = InputRegisterAt(instruction, 1);
+  Register obj = InputRegisterAt(instruction, 0);
+  codegen_->Store(field_type, value, MemOperand(obj, instruction->GetFieldOffset().Uint32Value()));
+  if (field_type == Primitive::kPrimNot) {
+    codegen_->MarkGCCard(obj, value);
+  }
+}
+
+void LocationsBuilderARM64::VisitIntConstant(HIntConstant* constant) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant) {
+  // Will be generated at use site.
+  UNUSED(constant);
+}
+
+void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
+  HandleInvoke(invoke);
+}
+
+void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  HandleInvoke(invoke);
+}
+
+void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
+  locations->AddTemp(LocationFrom(x0));
+
+  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()));
+  }
+
+  Primitive::Type return_type = invoke->GetType();
+  if (return_type != Primitive::kPrimVoid) {
+    locations->SetOut(calling_convention_visitor.GetReturnLocation(return_type));
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
+  Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
+  // Make sure that ArtMethod* is passed in W0 as per the calling convention
+  DCHECK(temp.Is(w0));
+  size_t index_in_cache = mirror::Array::DataOffset(kHeapRefSize).SizeValue() +
+    invoke->GetIndexInDexCache() * kHeapRefSize;
+
+  // 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;
+  __ Ldr(temp, MemOperand(sp, kCurrentMethodStackOffset));
+  // temp = temp->dex_cache_resolved_methods_;
+  __ Ldr(temp, MemOperand(temp.X(),
+                          mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
+  // temp = temp[index_in_cache];
+  __ Ldr(temp, MemOperand(temp.X(), index_in_cache));
+  // lr = temp->entry_point_from_quick_compiled_code_;
+  __ Ldr(lr, MemOperand(temp.X(),
+                        mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+  // lr();
+  __ Blr(lr);
+
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+  DCHECK(!codegen_->IsLeafMethod());
+}
+
+void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  Register temp = XRegisterFrom(invoke->GetLocations()->GetTemp(0));
+  size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() +
+    invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
+  Offset class_offset = mirror::Object::ClassOffset();
+  Offset entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset();
+
+  // temp = object->GetClass();
+  if (receiver.IsStackSlot()) {
+    __ Ldr(temp.W(), MemOperand(sp, receiver.GetStackIndex()));
+    __ Ldr(temp.W(), MemOperand(temp, class_offset.SizeValue()));
+  } else {
+    DCHECK(receiver.IsRegister());
+    __ Ldr(temp.W(), HeapOperandFrom(receiver, Primitive::kPrimNot,
+                                     class_offset));
+  }
+  // temp = temp->GetMethodAt(method_offset);
+  __ Ldr(temp.W(), MemOperand(temp, method_offset));
+  // lr = temp->GetEntryPoint();
+  __ Ldr(lr, MemOperand(temp, entry_point.SizeValue()));
+  // lr();
+  __ Blr(lr);
+  DCHECK(!codegen_->IsLeafMethod());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) {
+  load->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load) {
+  // Nothing to do, this is driven by the code generator.
+  UNUSED(load);
+}
+
+void LocationsBuilderARM64::VisitLocal(HLocal* local) {
+  local->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitLocal(HLocal* local) {
+  DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
+}
+
+void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant) {
+  // Will be generated at use site.
+  UNUSED(constant);
+}
+
+void LocationsBuilderARM64::VisitMul(HMul* mul) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitMul(HMul* mul) {
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      __ Fmul(OutputFPRegister(mul), InputFPRegisterAt(mul, 0), InputFPRegisterAt(mul, 1));
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void LocationsBuilderARM64::VisitNeg(HNeg* neg) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
+  switch (neg->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong: {
+      locations->SetInAt(0, Location::RegisterOrConstant(neg->InputAt(0)));
+      locations->SetOut(Location::RequiresRegister());
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType();
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitNeg(HNeg* neg) {
+  switch (neg->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      __ Neg(OutputRegister(neg), InputOperandAt(neg, 0));
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType();
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
+  }
+}
+
+void LocationsBuilderARM64::VisitNewArray(HNewArray* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
+  locations->SetOut(LocationFrom(x0));
+  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(2)));
+}
+
+void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  InvokeRuntimeCallingConvention calling_convention;
+  Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
+  DCHECK(type_index.Is(w0));
+  Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot);
+  DCHECK(current_method.Is(w1));
+  __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset));
+  __ Mov(type_index, instruction->GetTypeIndex());
+  int32_t quick_entrypoint_offset =
+      QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocArrayWithAccessCheck).Int32Value();
+  __ Ldr(lr, MemOperand(tr, quick_entrypoint_offset));
+  __ Blr(lr);
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+  DCHECK(!codegen_->IsLeafMethod());
+}
+
+void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
+  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
+}
+
+void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
+  DCHECK(type_index.Is(w0));
+  Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot);
+  DCHECK(current_method.Is(w1));
+  __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset));
+  __ Mov(type_index, instruction->GetTypeIndex());
+  int32_t quick_entrypoint_offset =
+      QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocObjectWithAccessCheck).Int32Value();
+  __ Ldr(lr, MemOperand(tr, quick_entrypoint_offset));
+  __ Blr(lr);
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+  DCHECK(!codegen_->IsLeafMethod());
+}
+
+void LocationsBuilderARM64::VisitNot(HNot* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM64::VisitNot(HNot* instruction) {
+  switch (instruction->InputAt(0)->GetType()) {
+    case Primitive::kPrimBoolean:
+      __ Eor(OutputRegister(instruction), InputRegisterAt(instruction, 0), Operand(1));
+      break;
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      __ Mvn(OutputRegister(instruction), InputOperandAt(instruction, 0));
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType();
+  }
+}
+
+void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
+  SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction);
+  codegen_->AddSlowPath(slow_path);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location obj = locations->InAt(0);
+  if (obj.IsRegister()) {
+    __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel());
+  } else {
+    DCHECK(obj.IsConstant()) << obj;
+    DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
+    __ B(slow_path->GetEntryLabel());
+  }
+}
+
+void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
+  if (location.IsStackSlot()) {
+    location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
+  } else if (location.IsDoubleStackSlot()) {
+    location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
+  }
+  locations->SetOut(location);
+}
+
+void InstructionCodeGeneratorARM64::VisitParameterValue(HParameterValue* instruction) {
+  // Nothing to do, the parameter is already at its location.
+  UNUSED(instruction);
+}
+
+void LocationsBuilderARM64::VisitPhi(HPhi* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
+    locations->SetInAt(i, Location::Any());
+  }
+  locations->SetOut(Location::Any());
+}
+
+void InstructionCodeGeneratorARM64::VisitPhi(HPhi* instruction) {
+  UNUSED(instruction);
+  LOG(FATAL) << "Unreachable";
+}
+
+void LocationsBuilderARM64::VisitReturn(HReturn* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  Primitive::Type return_type = instruction->InputAt(0)->GetType();
+  locations->SetInAt(0, ARM64ReturnLocation(return_type));
+}
+
+void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction) {
+  UNUSED(instruction);
+  codegen_->GenerateFrameExit();
+  __ Br(lr);
+}
+
+void LocationsBuilderARM64::VisitReturnVoid(HReturnVoid* instruction) {
+  instruction->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction) {
+  UNUSED(instruction);
+  codegen_->GenerateFrameExit();
+  __ Br(lr);
+}
+
+void LocationsBuilderARM64::VisitStoreLocal(HStoreLocal* store) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+  Primitive::Type field_type = store->InputAt(1)->GetType();
+  switch (field_type) {
+    case Primitive::kPrimNot:
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
+      break;
+
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
+      break;
+
+    default:
+      LOG(FATAL) << "Unimplemented local type " << field_type;
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store) {
+  UNUSED(store);
+}
+
+void LocationsBuilderARM64::VisitSub(HSub* instruction) {
+  HandleAddSub(instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitSub(HSub* instruction) {
+  HandleAddSub(instruction);
+}
+
+void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64(
+      instruction, locations->InAt(0), locations->InAt(1));
+  codegen_->AddSlowPath(slow_path);
+
+  __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1));
+  __ B(slow_path->GetEntryLabel(), hs);
+}
+
+void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
+  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+}
+
+void InstructionCodeGeneratorARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
+  // TODO: Improve support for suspend checks.
+  SuspendCheckSlowPathARM64* slow_path =
+      new (GetGraph()->GetArena()) SuspendCheckSlowPathARM64(instruction, nullptr);
+  codegen_->AddSlowPath(slow_path);
+
+  __ Subs(wSuspend, wSuspend, 1);
+  __ B(slow_path->GetEntryLabel(), le);
+  __ Bind(slow_path->GetReturnLabel());
+}
+
+void LocationsBuilderARM64::VisitTemporary(HTemporary* temp) {
+  temp->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp) {
+  // Nothing to do, this is driven by the code generator.
+  UNUSED(temp);
+}
+
+}  // namespace arm64
+}  // namespace art
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
new file mode 100644
index 0000000..f2ead21
--- /dev/null
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -0,0 +1,247 @@
+/*
+ * 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_CODE_GENERATOR_ARM64_H_
+#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
+
+#include "code_generator.h"
+#include "nodes.h"
+#include "parallel_move_resolver.h"
+#include "utils/arm64/assembler_arm64.h"
+#include "a64/disasm-a64.h"
+#include "a64/macro-assembler-a64.h"
+#include "arch/arm64/quick_method_frame_info_arm64.h"
+
+namespace art {
+namespace arm64 {
+
+class CodeGeneratorARM64;
+
+static constexpr size_t kArm64WordSize = 8;
+static const vixl::Register kParameterCoreRegisters[] = {
+  vixl::x1, vixl::x2, vixl::x3, vixl::x4, vixl::x5, vixl::x6, vixl::x7
+};
+static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
+static const vixl::FPRegister kParameterFPRegisters[] = {
+  vixl::d0, vixl::d1, vixl::d2, vixl::d3, vixl::d4, vixl::d5, vixl::d6, vixl::d7
+};
+static constexpr size_t kParameterFPRegistersLength = arraysize(kParameterFPRegisters);
+
+const vixl::Register tr = vixl::x18;        // Thread Register
+const vixl::Register wSuspend = vixl::w19;  // Suspend Register
+const vixl::Register xSuspend = vixl::x19;
+
+const vixl::CPURegList vixl_reserved_core_registers(vixl::ip0, vixl::ip1);
+const vixl::CPURegList vixl_reserved_fp_registers(vixl::d31);
+const vixl::CPURegList runtime_reserved_core_registers(tr, xSuspend, vixl::lr);
+const vixl::CPURegList quick_callee_saved_registers(vixl::CPURegister::kRegister,
+                                                    vixl::kXRegSize,
+                                                    kArm64CalleeSaveRefSpills);
+
+Location ARM64ReturnLocation(Primitive::Type return_type);
+
+class InvokeDexCallingConvention : public CallingConvention<vixl::Register, vixl::FPRegister> {
+ public:
+  InvokeDexCallingConvention()
+      : CallingConvention(kParameterCoreRegisters,
+                          kParameterCoreRegistersLength,
+                          kParameterFPRegisters,
+                          kParameterFPRegistersLength) {}
+
+  Location GetReturnLocation(Primitive::Type return_type) {
+    return ARM64ReturnLocation(return_type);
+  }
+
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention);
+};
+
+class InvokeDexCallingConventionVisitor {
+ public:
+  InvokeDexCallingConventionVisitor() : gp_index_(0), fp_index_(0), stack_index_(0) {}
+
+  Location GetNextLocation(Primitive::Type type);
+  Location GetReturnLocation(Primitive::Type return_type) {
+    return calling_convention.GetReturnLocation(return_type);
+  }
+
+ private:
+  InvokeDexCallingConvention calling_convention;
+  // The current index for core registers.
+  uint32_t gp_index_;
+  // The current index for floating-point registers.
+  uint32_t fp_index_;
+  // The current stack index.
+  uint32_t stack_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor);
+};
+
+class InstructionCodeGeneratorARM64 : public HGraphVisitor {
+ public:
+  InstructionCodeGeneratorARM64(HGraph* graph, CodeGeneratorARM64* codegen);
+
+#define DECLARE_VISIT_INSTRUCTION(name, super) \
+  void Visit##name(H##name* instr) OVERRIDE;
+  FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+#undef DECLARE_VISIT_INSTRUCTION
+
+  void LoadCurrentMethod(XRegister reg);
+
+  Arm64Assembler* GetAssembler() const { return assembler_; }
+
+ private:
+  void HandleAddSub(HBinaryOperation* instr);
+
+  Arm64Assembler* const assembler_;
+  CodeGeneratorARM64* const codegen_;
+
+  DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorARM64);
+};
+
+class LocationsBuilderARM64 : public HGraphVisitor {
+ public:
+  explicit LocationsBuilderARM64(HGraph* graph, CodeGeneratorARM64* codegen)
+      : HGraphVisitor(graph), codegen_(codegen) {}
+
+#define DECLARE_VISIT_INSTRUCTION(name, super) \
+  void Visit##name(H##name* instr) OVERRIDE;
+  FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+#undef DECLARE_VISIT_INSTRUCTION
+
+ private:
+  void HandleAddSub(HBinaryOperation* instr);
+  void HandleInvoke(HInvoke* instr);
+
+  CodeGeneratorARM64* const codegen_;
+  InvokeDexCallingConventionVisitor parameter_visitor_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARM64);
+};
+
+class CodeGeneratorARM64 : public CodeGenerator {
+ public:
+  explicit CodeGeneratorARM64(HGraph* graph);
+  virtual ~CodeGeneratorARM64() {}
+
+  void GenerateFrameEntry() OVERRIDE;
+  void GenerateFrameExit() OVERRIDE;
+
+  static const vixl::CPURegList& GetFramePreservedRegisters() {
+    static const vixl::CPURegList frame_preserved_regs =
+        vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize, vixl::lr.Bit());
+    return frame_preserved_regs;
+  }
+  static int GetFramePreservedRegistersSize() {
+    return GetFramePreservedRegisters().TotalSizeInBytes();
+  }
+
+  void Bind(HBasicBlock* block) OVERRIDE;
+
+  vixl::Label* GetLabelOf(HBasicBlock* block) const {
+    return block_labels_ + block->GetBlockId();
+  }
+
+  void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+
+  size_t GetWordSize() const OVERRIDE {
+    return kArm64WordSize;
+  }
+
+  uintptr_t GetAddressOf(HBasicBlock* block ATTRIBUTE_UNUSED) const OVERRIDE {
+    UNIMPLEMENTED(INFO) << "TODO: GetAddressOf";
+    return 0u;
+  }
+
+  size_t FrameEntrySpillSize() const OVERRIDE;
+
+  HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; }
+  HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
+  Arm64Assembler* GetAssembler() OVERRIDE { return &assembler_; }
+
+  // Emit a write barrier.
+  void MarkGCCard(vixl::Register object, vixl::Register value);
+
+  // Register allocation.
+
+  void SetupBlockedRegisters() const OVERRIDE;
+  // AllocateFreeRegister() is only used when allocating registers locally
+  // during CompileBaseline().
+  Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
+
+  Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
+
+  size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE {
+    UNUSED(stack_index);
+    UNUSED(reg_id);
+    UNIMPLEMENTED(INFO) << "TODO: SaveCoreRegister";
+    return 0;
+  }
+
+  size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE {
+    UNUSED(stack_index);
+    UNUSED(reg_id);
+    UNIMPLEMENTED(INFO) << "TODO: RestoreCoreRegister";
+    return 0;
+  }
+
+  // The number of registers that can be allocated. The register allocator may
+  // decide to reserve and not use a few of them.
+  // We do not consider registers sp, xzr, wzr. They are either not allocatable
+  // (xzr, wzr), or make for poor allocatable registers (sp alignment
+  // requirements, etc.). This also facilitates our task as all other registers
+  // can easily be mapped via to or from their type and index or code.
+  static const int kNumberOfAllocatableRegisters = vixl::kNumberOfRegisters - 1;
+  static const int kNumberOfAllocatableFPRegisters = vixl::kNumberOfFPRegisters;
+  static constexpr int kNumberOfAllocatableRegisterPairs = 0;
+
+  void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
+  void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
+
+  InstructionSet GetInstructionSet() const OVERRIDE {
+    return InstructionSet::kArm64;
+  }
+
+  void Initialize() OVERRIDE {
+    HGraph* graph = GetGraph();
+    int length = graph->GetBlocks().Size();
+    block_labels_ = graph->GetArena()->AllocArray<vixl::Label>(length);
+    for (int i = 0; i < length; ++i) {
+      new(block_labels_ + i) vixl::Label();
+    }
+  }
+
+  // Code generation helpers.
+  void MoveHelper(Location destination, Location source, Primitive::Type type);
+  void Load(Primitive::Type type, vixl::Register dst, const vixl::MemOperand& src);
+  void Store(Primitive::Type type, vixl::Register rt, const vixl::MemOperand& dst);
+
+ private:
+  // Labels for each block that will be compiled.
+  vixl::Label* block_labels_;
+
+  LocationsBuilderARM64 location_builder_;
+  InstructionCodeGeneratorARM64 instruction_visitor_;
+  Arm64Assembler assembler_;
+
+  DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64);
+};
+
+}  // namespace arm64
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index f544d47..d66180b 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -15,23 +15,20 @@
  */
 
 #include "code_generator_x86.h"
+
+#include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/accounting/card_table.h"
+#include "mirror/array-inl.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 {
-  return reg().AsX86();
-}
-
 namespace x86 {
 
 static constexpr bool kExplicitStackOverflowCheck = false;
@@ -39,19 +36,19 @@
 static constexpr int kNumberOfPushedRegistersAtEntry = 1;
 static constexpr int kCurrentMethodStackOffset = 0;
 
-static Location X86CpuLocation(Register reg) {
-  return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg));
-}
-
 static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX };
 static constexpr size_t kRuntimeParameterCoreRegistersLength =
     arraysize(kRuntimeParameterCoreRegisters);
+static constexpr XmmRegister kRuntimeParameterFpuRegisters[] = { };
+static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
 
-class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
+class InvokeRuntimeCallingConvention : public CallingConvention<Register, XmmRegister> {
  public:
   InvokeRuntimeCallingConvention()
       : CallingConvention(kRuntimeParameterCoreRegisters,
-                          kRuntimeParameterCoreRegistersLength) {}
+                          kRuntimeParameterCoreRegistersLength,
+                          kRuntimeParameterFpuRegisters,
+                          kRuntimeParameterFpuRegistersLength) {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
@@ -59,22 +56,66 @@
 
 #define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())->
 
-class NullCheckSlowPathX86 : public SlowPathCode {
+class SlowPathCodeX86 : public SlowPathCode {
  public:
-  explicit NullCheckSlowPathX86(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+  SlowPathCodeX86() : entry_label_(), exit_label_() {}
+
+  Label* GetEntryLabel() { return &entry_label_; }
+  Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  Label entry_label_;
+  Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86);
+};
+
+class NullCheckSlowPathX86 : public SlowPathCodeX86 {
+ public:
+  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);
 };
 
-class StackOverflowCheckSlowPathX86 : public SlowPathCode {
+class DivZeroCheckSlowPathX86 : public SlowPathCodeX86 {
+ public:
+  explicit DivZeroCheckSlowPathX86(HDivZeroCheck* instruction) : instruction_(instruction) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    __ Bind(GetEntryLabel());
+    __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowDivZero)));
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+  }
+
+ private:
+  HDivZeroCheck* const instruction_;
+  DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86);
+};
+
+class DivMinusOneSlowPathX86 : public SlowPathCodeX86 {
+ public:
+  explicit DivMinusOneSlowPathX86(Register reg) : reg_(reg) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    __ Bind(GetEntryLabel());
+    __ negl(reg_);
+    __ jmp(GetExitLabel());
+  }
+
+ private:
+  Register reg_;
+  DISALLOW_COPY_AND_ASSIGN(DivMinusOneSlowPathX86);
+};
+
+class StackOverflowCheckSlowPathX86 : public SlowPathCodeX86 {
  public:
   StackOverflowCheckSlowPathX86() {}
 
@@ -89,31 +130,187 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86);
 };
 
-class BoundsCheckSlowPathX86 : public SlowPathCode {
+class BoundsCheckSlowPathX86 : public SlowPathCodeX86 {
  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);
+    CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
     __ Bind(GetEntryLabel());
     InvokeRuntimeCallingConvention calling_convention;
-    x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
-    x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
+    x86_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
+    x86_codegen->Move32(Location::RegisterLocation(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 SlowPathCodeX86 {
+ public:
+  explicit SuspendCheckSlowPathX86(HSuspendCheck* instruction, HBasicBlock* successor)
+      : instruction_(instruction), successor_(successor) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
+    __ 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(x86_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);
+};
+
+class LoadStringSlowPathX86 : public SlowPathCodeX86 {
+ public:
+  explicit LoadStringSlowPathX86(HLoadString* instruction) : instruction_(instruction) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = instruction_->GetLocations();
+    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+
+    CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(locations);
+
+    InvokeRuntimeCallingConvention calling_convention;
+    x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0));
+    __ movl(calling_convention.GetRegisterAt(1), Immediate(instruction_->GetStringIndex()));
+    __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pResolveString)));
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+    x86_codegen->Move32(locations->Out(), Location::RegisterLocation(EAX));
+    codegen->RestoreLiveRegisters(locations);
+
+    __ jmp(GetExitLabel());
+  }
+
+ private:
+  HLoadString* const instruction_;
+
+  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86);
+};
+
+class LoadClassSlowPathX86 : public SlowPathCodeX86 {
+ public:
+  LoadClassSlowPathX86(HLoadClass* cls,
+                       HInstruction* at,
+                       uint32_t dex_pc,
+                       bool do_clinit)
+      : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+    DCHECK(at->IsLoadClass() || at->IsClinitCheck());
+  }
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = at_->GetLocations();
+    CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(locations);
+
+    InvokeRuntimeCallingConvention calling_convention;
+    __ movl(calling_convention.GetRegisterAt(0), Immediate(cls_->GetTypeIndex()));
+    x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+    __ fs()->call(Address::Absolute(do_clinit_
+        ? QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pInitializeStaticStorage)
+        : QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pInitializeType)));
+    codegen->RecordPcInfo(at_, dex_pc_);
+
+    // Move the class to the desired location.
+    Location out = locations->Out();
+    if (out.IsValid()) {
+      DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+      x86_codegen->Move32(out, Location::RegisterLocation(EAX));
+    }
+
+    codegen->RestoreLiveRegisters(locations);
+    __ jmp(GetExitLabel());
+  }
+
+ private:
+  // The class this slow path will load.
+  HLoadClass* const cls_;
+
+  // The instruction where this slow path is happening.
+  // (Might be the load class or an initialization check).
+  HInstruction* const at_;
+
+  // The dex PC of `at_`.
+  const uint32_t dex_pc_;
+
+  // Whether to initialize the class.
+  const bool do_clinit_;
+
+  DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathX86);
+};
+
+class TypeCheckSlowPathX86 : public SlowPathCodeX86 {
+ public:
+  TypeCheckSlowPathX86(HTypeCheck* instruction, Location object_class)
+      : instruction_(instruction),
+        object_class_(object_class) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = instruction_->GetLocations();
+    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+
+    CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(locations);
+
+    // We're moving two locations to locations that could overlap, so we need a parallel
+    // move resolver.
+    InvokeRuntimeCallingConvention calling_convention;
+    MoveOperands move1(locations->InAt(1),
+                       Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+                       nullptr);
+    MoveOperands move2(object_class_,
+                       Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
+                       nullptr);
+    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
+    parallel_move.AddMove(&move1);
+    parallel_move.AddMove(&move2);
+    x86_codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
+
+    __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pInstanceofNonTrivial)));
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+    x86_codegen->Move32(locations->Out(), Location::RegisterLocation(EAX));
+    codegen->RestoreLiveRegisters(locations);
+
+    __ jmp(GetExitLabel());
+  }
+
+ private:
+  HTypeCheck* const instruction_;
+  const Location object_class_;
+
+  DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathX86);
+};
+
 #undef __
 #define __ reinterpret_cast<X86Assembler*>(GetAssembler())->
 
@@ -139,8 +336,19 @@
   stream << X86ManagedRegister::FromXmmRegister(XmmRegister(reg));
 }
 
+size_t CodeGeneratorX86::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
+  __ movl(Address(ESP, stack_index), static_cast<Register>(reg_id));
+  return kX86WordSize;
+}
+
+size_t CodeGeneratorX86::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
+  __ movl(static_cast<Register>(reg_id), Address(ESP, stack_index));
+  return kX86WordSize;
+}
+
 CodeGeneratorX86::CodeGeneratorX86(HGraph* graph)
-    : CodeGenerator(graph, kNumberOfRegIds),
+    : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters, kNumberOfRegisterPairs),
+      block_labels_(graph->GetArena(), 0),
       location_builder_(graph, this),
       instruction_visitor_(graph, this),
       move_resolver_(graph->GetArena(), this) {}
@@ -149,32 +357,18 @@
   return kNumberOfPushedRegistersAtEntry * kX86WordSize;
 }
 
-static bool* GetBlockedRegisterPairs(bool* blocked_registers) {
-  return blocked_registers + kNumberOfAllocIds;
-}
-
-ManagedRegister CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type,
-                                                       bool* blocked_registers) const {
+Location CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type) const {
   switch (type) {
     case Primitive::kPrimLong: {
-      bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
-      size_t reg = AllocateFreeRegisterInternal(blocked_register_pairs, kNumberOfRegisterPairs);
+      size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs);
       X86ManagedRegister pair =
           X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg));
-      blocked_registers[pair.AsRegisterPairLow()] = true;
-      blocked_registers[pair.AsRegisterPairHigh()] = true;
-      // Block all other register pairs that share a register with `pair`.
-      for (int i = 0; i < kNumberOfRegisterPairs; i++) {
-        X86ManagedRegister current =
-            X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
-        if (current.AsRegisterPairLow() == pair.AsRegisterPairLow()
-            || current.AsRegisterPairLow() == pair.AsRegisterPairHigh()
-            || current.AsRegisterPairHigh() == pair.AsRegisterPairLow()
-            || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) {
-          blocked_register_pairs[i] = true;
-        }
-      }
-      return pair;
+      DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]);
+      DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]);
+      blocked_core_registers_[pair.AsRegisterPairLow()] = true;
+      blocked_core_registers_[pair.AsRegisterPairHigh()] = true;
+      UpdateBlockedPairRegisters();
+      return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh());
     }
 
     case Primitive::kPrimByte:
@@ -184,51 +378,55 @@
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
       Register reg = static_cast<Register>(
-          AllocateFreeRegisterInternal(blocked_registers, kNumberOfCpuRegisters));
+          FindFreeEntry(blocked_core_registers_, kNumberOfCpuRegisters));
       // Block all register pairs that contain `reg`.
-      bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
       for (int i = 0; i < kNumberOfRegisterPairs; i++) {
         X86ManagedRegister current =
             X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
         if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) {
-          blocked_register_pairs[i] = true;
+          blocked_register_pairs_[i] = true;
         }
       }
-      return X86ManagedRegister::FromCpuRegister(reg);
+      return Location::RegisterLocation(reg);
     }
 
     case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << type;
+    case Primitive::kPrimDouble: {
+      return Location::FpuRegisterLocation(
+          FindFreeEntry(blocked_fpu_registers_, kNumberOfXmmRegisters));
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << type;
   }
 
-  return ManagedRegister::NoRegister();
+  return Location();
 }
 
-void CodeGeneratorX86::SetupBlockedRegisters(bool* blocked_registers) const {
-  bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
-
+void CodeGeneratorX86::SetupBlockedRegisters() const {
   // Don't allocate the dalvik style register pair passing.
-  blocked_register_pairs[ECX_EDX] = true;
+  blocked_register_pairs_[ECX_EDX] = true;
 
   // Stack register is always reserved.
-  blocked_registers[ESP] = true;
+  blocked_core_registers_[ESP] = true;
 
   // TODO: We currently don't use Quick's callee saved registers.
-  blocked_registers[EBP] = true;
-  blocked_registers[ESI] = true;
-  blocked_registers[EDI] = true;
-  blocked_register_pairs[EAX_EDI] = true;
-  blocked_register_pairs[EDX_EDI] = true;
-  blocked_register_pairs[ECX_EDI] = true;
-  blocked_register_pairs[EBX_EDI] = true;
+  blocked_core_registers_[EBP] = true;
+  blocked_core_registers_[ESI] = true;
+  blocked_core_registers_[EDI] = true;
+
+  UpdateBlockedPairRegisters();
 }
 
-size_t CodeGeneratorX86::GetNumberOfRegisters() const {
-  return kNumberOfRegIds;
+void CodeGeneratorX86::UpdateBlockedPairRegisters() const {
+  for (int i = 0; i < kNumberOfRegisterPairs; i++) {
+    X86ManagedRegister current =
+        X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
+    if (blocked_core_registers_[current.AsRegisterPairLow()]
+        || blocked_core_registers_[current.AsRegisterPairHigh()]) {
+      blocked_register_pairs_[i] = true;
+    }
+  }
 }
 
 InstructionCodeGeneratorX86::InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen)
@@ -244,14 +442,14 @@
   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.
   __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
 
   if (!skip_overflow_check && kExplicitStackOverflowCheck) {
-    SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86();
+    SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86();
     AddSlowPath(slow_path);
 
     __ fs()->cmpl(ESP, Address::Absolute(Thread::StackEndOffset<kX86WordSize>()));
@@ -265,27 +463,25 @@
   __ addl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
 }
 
-void CodeGeneratorX86::Bind(Label* label) {
-  __ Bind(label);
+void CodeGeneratorX86::Bind(HBasicBlock* block) {
+  __ Bind(GetLabelOf(block));
 }
 
-void InstructionCodeGeneratorX86::LoadCurrentMethod(Register reg) {
+void CodeGeneratorX86::LoadCurrentMethod(Register reg) {
   __ movl(reg, Address(ESP, kCurrentMethodStackOffset));
 }
 
 Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const {
   switch (load->GetType()) {
     case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
       return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
       break;
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
-      return Location::StackSlot(GetStackSlot(load->GetLocal()));
-
     case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented type " << load->GetType();
+      return Location::StackSlot(GetStackSlot(load->GetLocal()));
 
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -306,33 +502,33 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
     case Primitive::kPrimNot: {
       uint32_t index = gp_index_++;
       if (index < calling_convention.GetNumberOfRegisters()) {
-        return X86CpuLocation(calling_convention.GetRegisterAt(index));
+        return Location::RegisterLocation(calling_convention.GetRegisterAt(index));
       } else {
         return Location::StackSlot(calling_convention.GetStackOffsetOf(index));
       }
     }
 
-    case Primitive::kPrimLong: {
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble: {
       uint32_t index = gp_index_;
       gp_index_ += 2;
       if (index + 1 < calling_convention.GetNumberOfRegisters()) {
-        return Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(
-            calling_convention.GetRegisterPairAt(index)));
+        X86ManagedRegister pair = X86ManagedRegister::FromRegisterPair(
+            calling_convention.GetRegisterPairAt(index));
+        return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh());
       } else if (index + 1 == calling_convention.GetNumberOfRegisters()) {
-        return Location::QuickParameter(index);
+        // On X86, the register index and stack index of a quick parameter is the same, since
+        // we are passing floating pointer values in core registers.
+        return Location::QuickParameter(index, index);
       } else {
         return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(index));
       }
     }
 
-    case Primitive::kPrimDouble:
-    case Primitive::kPrimFloat:
-      LOG(FATAL) << "Unimplemented parameter type " << type;
-      break;
-
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unexpected parameter type " << type;
       break;
@@ -346,14 +542,28 @@
   }
   if (destination.IsRegister()) {
     if (source.IsRegister()) {
-      __ movl(destination.AsX86().AsCpuRegister(), source.AsX86().AsCpuRegister());
+      __ movl(destination.As<Register>(), source.As<Register>());
+    } else if (source.IsFpuRegister()) {
+      __ movd(destination.As<Register>(), source.As<XmmRegister>());
     } else {
       DCHECK(source.IsStackSlot());
-      __ movl(destination.AsX86().AsCpuRegister(), Address(ESP, source.GetStackIndex()));
+      __ movl(destination.As<Register>(), Address(ESP, source.GetStackIndex()));
+    }
+  } else if (destination.IsFpuRegister()) {
+    if (source.IsRegister()) {
+      __ movd(destination.As<XmmRegister>(), source.As<Register>());
+    } else if (source.IsFpuRegister()) {
+      __ movaps(destination.As<XmmRegister>(), source.As<XmmRegister>());
+    } else {
+      DCHECK(source.IsStackSlot());
+      __ movss(destination.As<XmmRegister>(), Address(ESP, source.GetStackIndex()));
     }
   } else {
+    DCHECK(destination.IsStackSlot());
     if (source.IsRegister()) {
-      __ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsCpuRegister());
+      __ movl(Address(ESP, destination.GetStackIndex()), source.As<Register>());
+    } else if (source.IsFpuRegister()) {
+      __ movss(Address(ESP, destination.GetStackIndex()), source.As<XmmRegister>());
     } else {
       DCHECK(source.IsStackSlot());
       __ pushl(Address(ESP, source.GetStackIndex()));
@@ -366,50 +576,65 @@
   if (source.Equals(destination)) {
     return;
   }
-  if (destination.IsRegister()) {
-    if (source.IsRegister()) {
-      __ movl(destination.AsX86().AsRegisterPairLow(), source.AsX86().AsRegisterPairLow());
-      __ movl(destination.AsX86().AsRegisterPairHigh(), source.AsX86().AsRegisterPairHigh());
+  if (destination.IsRegisterPair()) {
+    if (source.IsRegisterPair()) {
+      __ movl(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
+      __ movl(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
+    } else if (source.IsFpuRegister()) {
+      LOG(FATAL) << "Unimplemented";
     } else if (source.IsQuickParameter()) {
-      uint32_t argument_index = source.GetQuickParameterIndex();
+      uint16_t register_index = source.GetQuickParameterRegisterIndex();
+      uint16_t stack_index = source.GetQuickParameterStackIndex();
       InvokeDexCallingConvention calling_convention;
-      __ movl(destination.AsX86().AsRegisterPairLow(),
-              calling_convention.GetRegisterAt(argument_index));
-      __ movl(destination.AsX86().AsRegisterPairHigh(), Address(ESP,
-          calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
+      __ movl(destination.AsRegisterPairLow<Register>(),
+              calling_convention.GetRegisterAt(register_index));
+      __ movl(destination.AsRegisterPairHigh<Register>(), Address(ESP,
+          calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize()));
     } else {
       DCHECK(source.IsDoubleStackSlot());
-      __ movl(destination.AsX86().AsRegisterPairLow(), Address(ESP, source.GetStackIndex()));
-      __ movl(destination.AsX86().AsRegisterPairHigh(),
+      __ movl(destination.AsRegisterPairLow<Register>(), Address(ESP, source.GetStackIndex()));
+      __ movl(destination.AsRegisterPairHigh<Register>(),
               Address(ESP, source.GetHighStackIndex(kX86WordSize)));
     }
   } else if (destination.IsQuickParameter()) {
     InvokeDexCallingConvention calling_convention;
-    uint32_t argument_index = destination.GetQuickParameterIndex();
+    uint16_t register_index = destination.GetQuickParameterRegisterIndex();
+    uint16_t stack_index = destination.GetQuickParameterStackIndex();
     if (source.IsRegister()) {
-      __ movl(calling_convention.GetRegisterAt(argument_index), source.AsX86().AsRegisterPairLow());
-      __ movl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1)),
-              source.AsX86().AsRegisterPairHigh());
+      __ movl(calling_convention.GetRegisterAt(register_index), source.AsRegisterPairLow<Register>());
+      __ movl(Address(ESP, calling_convention.GetStackOffsetOf(stack_index + 1)),
+              source.AsRegisterPairHigh<Register>());
+    } else if (source.IsFpuRegister()) {
+      LOG(FATAL) << "Unimplemented";
     } else {
       DCHECK(source.IsDoubleStackSlot());
-      __ movl(calling_convention.GetRegisterAt(argument_index),
+      __ movl(calling_convention.GetRegisterAt(register_index),
               Address(ESP, source.GetStackIndex()));
       __ pushl(Address(ESP, source.GetHighStackIndex(kX86WordSize)));
-      __ popl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1)));
+      __ popl(Address(ESP, calling_convention.GetStackOffsetOf(stack_index + 1)));
+    }
+  } else if (destination.IsFpuRegister()) {
+    if (source.IsDoubleStackSlot()) {
+      __ movsd(destination.As<XmmRegister>(), Address(ESP, source.GetStackIndex()));
+    } else {
+      LOG(FATAL) << "Unimplemented";
     }
   } else {
-    if (source.IsRegister()) {
-      __ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsRegisterPairLow());
+    DCHECK(destination.IsDoubleStackSlot());
+    if (source.IsRegisterPair()) {
+      __ movl(Address(ESP, destination.GetStackIndex()), source.AsRegisterPairLow<Register>());
       __ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)),
-              source.AsX86().AsRegisterPairHigh());
+              source.AsRegisterPairHigh<Register>());
     } else if (source.IsQuickParameter()) {
       InvokeDexCallingConvention calling_convention;
-      uint32_t argument_index = source.GetQuickParameterIndex();
+      uint16_t register_index = source.GetQuickParameterRegisterIndex();
+      uint16_t stack_index = source.GetQuickParameterStackIndex();
       __ movl(Address(ESP, destination.GetStackIndex()),
-              calling_convention.GetRegisterAt(argument_index));
-      __ pushl(Address(ESP,
-          calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
-      __ popl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)));
+              calling_convention.GetRegisterAt(register_index));
+      DCHECK_EQ(calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize(),
+                static_cast<size_t>(destination.GetHighStackIndex(kX86WordSize)));
+    } else if (source.IsFpuRegister()) {
+      __ movsd(Address(ESP, destination.GetStackIndex()), source.As<XmmRegister>());
     } else {
       DCHECK(source.IsDoubleStackSlot());
       __ pushl(Address(ESP, source.GetStackIndex()));
@@ -421,23 +646,33 @@
 }
 
 void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
-  if (instruction->AsIntConstant() != nullptr) {
+  if (instruction->IsIntConstant()) {
     Immediate imm(instruction->AsIntConstant()->GetValue());
     if (location.IsRegister()) {
-      __ movl(location.AsX86().AsCpuRegister(), imm);
-    } else {
+      __ movl(location.As<Register>(), imm);
+    } else if (location.IsStackSlot()) {
       __ movl(Address(ESP, location.GetStackIndex()), imm);
+    } else {
+      DCHECK(location.IsConstant());
+      DCHECK_EQ(location.GetConstant(), instruction);
     }
-  } else if (instruction->AsLongConstant() != nullptr) {
+  } else if (instruction->IsLongConstant()) {
     int64_t value = instruction->AsLongConstant()->GetValue();
     if (location.IsRegister()) {
-      __ movl(location.AsX86().AsRegisterPairLow(), Immediate(Low32Bits(value)));
-      __ movl(location.AsX86().AsRegisterPairHigh(), Immediate(High32Bits(value)));
-    } else {
+      __ movl(location.AsRegisterPairLow<Register>(), Immediate(Low32Bits(value)));
+      __ movl(location.AsRegisterPairHigh<Register>(), Immediate(High32Bits(value)));
+    } else if (location.IsDoubleStackSlot()) {
       __ movl(Address(ESP, location.GetStackIndex()), Immediate(Low32Bits(value)));
       __ movl(Address(ESP, location.GetHighStackIndex(kX86WordSize)), Immediate(High32Bits(value)));
+    } else {
+      DCHECK(location.IsConstant());
+      DCHECK_EQ(location.GetConstant(), instruction);
     }
-  } else if (instruction->AsLoadLocal() != nullptr) {
+  } else if (instruction->IsTemporary()) {
+    Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
+    Move32(location, temp_location);
+  } else if (instruction->IsLoadLocal()) {
+    int slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
     switch (instruction->GetType()) {
       case Primitive::kPrimBoolean:
       case Primitive::kPrimByte:
@@ -445,12 +680,13 @@
       case Primitive::kPrimShort:
       case Primitive::kPrimInt:
       case Primitive::kPrimNot:
-        Move32(location, Location::StackSlot(GetStackSlot(instruction->AsLoadLocal()->GetLocal())));
+      case Primitive::kPrimFloat:
+        Move32(location, Location::StackSlot(slot));
         break;
 
       case Primitive::kPrimLong:
-        Move64(location, Location::DoubleStackSlot(
-            GetStackSlot(instruction->AsLoadLocal()->GetLocal())));
+      case Primitive::kPrimDouble:
+        Move64(location, Location::DoubleStackSlot(slot));
         break;
 
       default:
@@ -465,15 +701,17 @@
       case Primitive::kPrimShort:
       case Primitive::kPrimInt:
       case Primitive::kPrimNot:
+      case Primitive::kPrimFloat:
         Move32(location, instruction->GetLocations()->Out());
         break;
 
       case Primitive::kPrimLong:
+      case Primitive::kPrimDouble:
         Move64(location, instruction->GetLocations()->Out());
         break;
 
       default:
-        LOG(FATAL) << "Unimplemented type " << instruction->GetType();
+        LOG(FATAL) << "Unexpected type " << instruction->GetType();
     }
   }
 }
@@ -484,9 +722,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));
   }
 }
@@ -496,6 +747,7 @@
 }
 
 void InstructionCodeGeneratorX86::VisitExit(HExit* exit) {
+  UNUSED(exit);
   if (kIsDebugBuild) {
     __ Comment("Unreachable");
     __ int3();
@@ -503,46 +755,70 @@
 }
 
 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()) {
+  if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
     locations->SetInAt(0, Location::Any());
   }
-  if_instr->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) {
   HInstruction* cond = if_instr->InputAt(0);
-  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));
+  if (cond->IsIntConstant()) {
+    // Constant condition, statically compared against 1.
+    int32_t cond_value = cond->AsIntConstant()->GetValue();
+    if (cond_value == 1) {
+      if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                     if_instr->IfTrueSuccessor())) {
+        __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      }
+      return;
     } else {
-      __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0));
+      DCHECK_EQ(cond_value, 0);
     }
-    __ j(kEqual,  codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
   } else {
-    Location lhs = condition->GetLocations()->InAt(0);
-    Location rhs = condition->GetLocations()->InAt(1);
-    // LHS is guaranteed to be in a register (see LocationsBuilderX86::VisitCondition).
-    if (rhs.IsRegister()) {
-      __ cmpl(lhs.AsX86().AsCpuRegister(), rhs.AsX86().AsCpuRegister());
-    } else if (rhs.IsConstant()) {
-      HIntConstant* instruction = rhs.GetConstant()->AsIntConstant();
-      Immediate imm(instruction->AsIntConstant()->GetValue());
-      __ cmpl(lhs.AsX86().AsCpuRegister(), imm);
+    bool materialized =
+        !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
+    // 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.
+    bool eflags_set = cond->IsCondition()
+        && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr);
+    if (materialized) {
+      if (!eflags_set) {
+        // Materialized condition, compare against 0.
+        Location lhs = if_instr->GetLocations()->InAt(0);
+        if (lhs.IsRegister()) {
+          __ cmpl(lhs.As<Register>(), Immediate(0));
+        } else {
+          __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0));
+        }
+        __ j(kNotEqual,  codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      } else {
+        __ j(X86Condition(cond->AsCondition()->GetCondition()),
+             codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      }
     } else {
-      __ cmpl(lhs.AsX86().AsCpuRegister(), Address(ESP, rhs.GetStackIndex()));
+      Location lhs = cond->GetLocations()->InAt(0);
+      Location rhs = cond->GetLocations()->InAt(1);
+      // LHS is guaranteed to be in a register (see
+      // LocationsBuilderX86::VisitCondition).
+      if (rhs.IsRegister()) {
+        __ cmpl(lhs.As<Register>(), rhs.As<Register>());
+      } else if (rhs.IsConstant()) {
+        HIntConstant* instruction = rhs.GetConstant()->AsIntConstant();
+        Immediate imm(instruction->AsIntConstant()->GetValue());
+        __ cmpl(lhs.As<Register>(), imm);
+      } else {
+        __ cmpl(lhs.As<Register>(), Address(ESP, rhs.GetStackIndex()));
+      }
+      __ j(X86Condition(cond->AsCondition()->GetCondition()),
+           codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
     }
-    __ j(X86Condition(condition->GetCondition()),
-         codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
   }
-  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
+  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                 if_instr->IfFalseSuccessor())) {
     __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
   }
 }
@@ -561,10 +837,12 @@
 
 void InstructionCodeGeneratorX86::VisitLoadLocal(HLoadLocal* load) {
   // Nothing to do, this is driven by the code generator.
+  UNUSED(load);
 }
 
 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:
@@ -572,47 +850,53 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
+    case Primitive::kPrimFloat:
       locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
       break;
 
     case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
       locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
       break;
 
     default:
-      LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
+      LOG(FATAL) << "Unknown local type " << store->InputAt(1)->GetType();
   }
   store->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store) {
+  UNUSED(store);
 }
 
 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().As<Register>();
+    // 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());
+      __ cmpl(locations->InAt(0).As<Register>(),
+              locations->InAt(1).As<Register>());
     } else if (locations->InAt(1).IsConstant()) {
       HConstant* instruction = locations->InAt(1).GetConstant();
       Immediate imm(instruction->AsIntConstant()->GetValue());
-      __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), imm);
+      __ cmpl(locations->InAt(0).As<Register>(), imm);
     } else {
-      __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(),
+      __ cmpl(locations->InAt(0).As<Register>(),
               Address(ESP, locations->InAt(1).GetStackIndex()));
     }
-    __ setb(X86Condition(comp->GetCondition()), locations->Out().AsX86().AsCpuRegister());
+    __ setb(X86Condition(comp->GetCondition()), reg);
   }
 }
 
@@ -665,22 +949,47 @@
 }
 
 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) {
+  // Will be generated at use site.
+  UNUSED(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) {
   // Will be generated at use site.
+  UNUSED(constant);
+}
+
+void LocationsBuilderX86::VisitFloatConstant(HFloatConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorX86::VisitFloatConstant(HFloatConstant* constant) {
+  // Will be generated at use site.
+  UNUSED(constant);
+}
+
+void LocationsBuilderX86::VisitDoubleConstant(HDoubleConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorX86::VisitDoubleConstant(HDoubleConstant* constant) {
+  // Will be generated at use site.
+  UNUSED(constant);
 }
 
 void LocationsBuilderX86::VisitReturnVoid(HReturnVoid* ret) {
@@ -688,12 +997,14 @@
 }
 
 void InstructionCodeGeneratorX86::VisitReturnVoid(HReturnVoid* ret) {
+  UNUSED(ret);
   codegen_->GenerateFrameExit();
   __ ret();
 }
 
 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:
@@ -701,18 +1012,23 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
-      locations->SetInAt(0, X86CpuLocation(EAX));
+      locations->SetInAt(0, Location::RegisterLocation(EAX));
       break;
 
     case Primitive::kPrimLong:
       locations->SetInAt(
-          0, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX)));
+          0, Location::RegisterPairLocation(EAX, EDX));
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(
+          0, Location::FpuRegisterLocation(XMM0));
       break;
 
     default:
-      LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
+      LOG(FATAL) << "Unknown return type " << ret->InputAt(0)->GetType();
   }
-  ret->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) {
@@ -724,15 +1040,21 @@
       case Primitive::kPrimShort:
       case Primitive::kPrimInt:
       case Primitive::kPrimNot:
-        DCHECK_EQ(ret->GetLocations()->InAt(0).AsX86().AsCpuRegister(), EAX);
+        DCHECK_EQ(ret->GetLocations()->InAt(0).As<Register>(), EAX);
         break;
 
       case Primitive::kPrimLong:
-        DCHECK_EQ(ret->GetLocations()->InAt(0).AsX86().AsRegisterPair(), EAX_EDX);
+        DCHECK_EQ(ret->GetLocations()->InAt(0).AsRegisterPairLow<Register>(), EAX);
+        DCHECK_EQ(ret->GetLocations()->InAt(0).AsRegisterPairHigh<Register>(), EDX);
+        break;
+
+      case Primitive::kPrimFloat:
+      case Primitive::kPrimDouble:
+        DCHECK_EQ(ret->GetLocations()->InAt(0).As<XmmRegister>(), XMM0);
         break;
 
       default:
-        LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
+        LOG(FATAL) << "Unknown return type " << ret->InputAt(0)->GetType();
     }
   }
   codegen_->GenerateFrameExit();
@@ -740,9 +1062,40 @@
 }
 
 void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
-  locations->AddTemp(X86CpuLocation(EAX));
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+  Register temp = invoke->GetLocations()->GetTemp(0).As<Register>();
+
+  // 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;
+  codegen_->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, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
+  // (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(Location::RegisterLocation(EAX));
 
   InvokeDexCallingConventionVisitor calling_convention_visitor;
   for (size_t i = 0; i < invoke->InputCount(); i++) {
@@ -757,11 +1110,11 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
-      locations->SetOut(X86CpuLocation(EAX));
+      locations->SetOut(Location::RegisterLocation(EAX));
       break;
 
     case Primitive::kPrimLong:
-      locations->SetOut(Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX)));
+      locations->SetOut(Location::RegisterPairLocation(EAX, EDX));
       break;
 
     case Primitive::kPrimVoid:
@@ -769,41 +1122,229 @@
 
     case Primitive::kPrimDouble:
     case Primitive::kPrimFloat:
-      LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
+      locations->SetOut(Location::FpuRegisterLocation(XMM0));
       break;
   }
 
   invoke->SetLocations(locations);
 }
 
-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)()
+void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  Register temp = invoke->GetLocations()->GetTemp(0).As<Register>();
+  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.As<Register>(), 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::VisitInvokeInterface(HInvokeInterface* invoke) {
+  HandleInvoke(invoke);
+  // Add the hidden argument.
+  invoke->GetLocations()->AddTemp(Location::FpuRegisterLocation(XMM0));
+}
+
+void InstructionCodeGeneratorX86::VisitInvokeInterface(HInvokeInterface* invoke) {
+  // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
+  Register temp = invoke->GetLocations()->GetTemp(0).As<Register>();
+  uint32_t method_offset = mirror::Class::EmbeddedImTableOffset().Uint32Value() +
+          (invoke->GetImtIndex() % mirror::Class::kImtSize) * sizeof(mirror::Class::ImTableEntry);
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+
+  // Set the hidden argument.
+  __ movl(temp, Immediate(invoke->GetDexMethodIndex()));
+  __ movd(invoke->GetLocations()->GetTemp(1).As<XmmRegister>(), temp);
+
+  // temp = object->GetClass();
+  if (receiver.IsStackSlot()) {
+    __ movl(temp, Address(ESP, receiver.GetStackIndex()));
+    __ movl(temp, Address(temp, class_offset));
+  } else {
+    __ movl(temp, Address(receiver.As<Register>(), class_offset));
+  }
+  // temp = temp->GetImtEntryAt(method_offset);
+  __ movl(temp, Address(temp, method_offset));
+  // call temp->GetEntryPoint();
+  __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+
+  DCHECK(!codegen_->IsLeafMethod());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderX86::VisitNeg(HNeg* neg) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
+  switch (neg->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      // Output overlaps as we need a fresh (zero-initialized)
+      // register to perform subtraction from zero.
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitNeg(HNeg* neg) {
+  LocationSummary* locations = neg->GetLocations();
+  Location out = locations->Out();
+  Location in = locations->InAt(0);
+  switch (neg->GetResultType()) {
+    case Primitive::kPrimInt:
+      DCHECK(in.IsRegister());
+      DCHECK(in.Equals(out));
+      __ negl(out.As<Register>());
+      break;
+
+    case Primitive::kPrimLong:
+      DCHECK(in.IsRegisterPair());
+      DCHECK(in.Equals(out));
+      __ negl(out.AsRegisterPairLow<Register>());
+      // Negation is similar to subtraction from zero.  The least
+      // significant byte triggers a borrow when it is different from
+      // zero; to take it into account, add 1 to the most significant
+      // byte if the carry flag (CF) is set to 1 after the first NEGL
+      // operation.
+      __ adcl(out.AsRegisterPairHigh<Register>(), Immediate(0));
+      __ negl(out.AsRegisterPairHigh<Register>());
+      break;
+
+    case Primitive::kPrimFloat:
+      DCHECK(!in.Equals(out));
+      // out = 0
+      __ xorps(out.As<XmmRegister>(), out.As<XmmRegister>());
+      // out = out - in
+      __ subss(out.As<XmmRegister>(), in.As<XmmRegister>());
+      break;
+
+    case Primitive::kPrimDouble:
+      DCHECK(!in.Equals(out));
+      // out = 0
+      __ xorpd(out.As<XmmRegister>(), out.As<XmmRegister>());
+      // out = out - in
+      __ subsd(out.As<XmmRegister>(), in.As<XmmRegister>());
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
+  }
+}
+
+void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall);
+  Primitive::Type result_type = conversion->GetResultType();
+  Primitive::Type input_type = conversion->GetInputType();
+  switch (result_type) {
+    case Primitive::kPrimLong:
+      switch (input_type) {
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // int-to-long conversion.
+          locations->SetInAt(0, Location::RegisterLocation(EAX));
+          locations->SetOut(Location::RegisterPairLocation(EAX, EDX));
+          break;
+
+        case Primitive::kPrimFloat:
+        case Primitive::kPrimDouble:
+          LOG(FATAL) << "Type conversion from " << input_type << " to "
+                     << result_type << " not yet implemented";
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Type conversion from " << input_type
+                 << " to " << result_type << " not yet implemented";
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected type conversion from " << input_type
+                 << " to " << result_type;
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversion) {
+  LocationSummary* locations = conversion->GetLocations();
+  Location out = locations->Out();
+  Location in = locations->InAt(0);
+  Primitive::Type result_type = conversion->GetResultType();
+  Primitive::Type input_type = conversion->GetInputType();
+  switch (result_type) {
+    case Primitive::kPrimLong:
+      switch (input_type) {
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // int-to-long conversion.
+          DCHECK_EQ(out.AsRegisterPairLow<Register>(), EAX);
+          DCHECK_EQ(out.AsRegisterPairHigh<Register>(), EDX);
+          DCHECK_EQ(in.As<Register>(), EAX);
+          __ cdq();
+          break;
+
+        case Primitive::kPrimFloat:
+        case Primitive::kPrimDouble:
+          LOG(FATAL) << "Type conversion from " << input_type << " to "
+                     << result_type << " not yet implemented";
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Type conversion from " << input_type
+                 << " to " << result_type << " not yet implemented";
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected type conversion from " << input_type
+                 << " to " << result_type;
+  }
 }
 
 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: {
@@ -813,70 +1354,75 @@
       break;
     }
 
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::Any());
+      locations->SetOut(Location::SameAsFirstInput());
       break;
+    }
 
     default:
-      LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
+      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+      break;
   }
-  add->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
   LocationSummary* locations = add->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  DCHECK(first.Equals(locations->Out()));
   switch (add->GetResultType()) {
     case Primitive::kPrimInt: {
-      DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(),
-                locations->Out().AsX86().AsCpuRegister());
-      if (locations->InAt(1).IsRegister()) {
-        __ addl(locations->InAt(0).AsX86().AsCpuRegister(),
-                locations->InAt(1).AsX86().AsCpuRegister());
-      } else if (locations->InAt(1).IsConstant()) {
-        HConstant* instruction = locations->InAt(1).GetConstant();
-        Immediate imm(instruction->AsIntConstant()->GetValue());
-        __ addl(locations->InAt(0).AsX86().AsCpuRegister(), imm);
+      if (second.IsRegister()) {
+        __ addl(first.As<Register>(), second.As<Register>());
+      } else if (second.IsConstant()) {
+        __ addl(first.As<Register>(), Immediate(second.GetConstant()->AsIntConstant()->GetValue()));
       } else {
-        __ addl(locations->InAt(0).AsX86().AsCpuRegister(),
-                Address(ESP, locations->InAt(1).GetStackIndex()));
+        __ addl(first.As<Register>(), Address(ESP, second.GetStackIndex()));
       }
       break;
     }
 
     case Primitive::kPrimLong: {
-      DCHECK_EQ(locations->InAt(0).AsX86().AsRegisterPair(),
-                locations->Out().AsX86().AsRegisterPair());
-      if (locations->InAt(1).IsRegister()) {
-        __ addl(locations->InAt(0).AsX86().AsRegisterPairLow(),
-                locations->InAt(1).AsX86().AsRegisterPairLow());
-        __ adcl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
-                locations->InAt(1).AsX86().AsRegisterPairHigh());
+      if (second.IsRegister()) {
+        __ addl(first.AsRegisterPairLow<Register>(), second.AsRegisterPairLow<Register>());
+        __ adcl(first.AsRegisterPairHigh<Register>(), second.AsRegisterPairHigh<Register>());
       } else {
-        __ addl(locations->InAt(0).AsX86().AsRegisterPairLow(),
-                Address(ESP, locations->InAt(1).GetStackIndex()));
-        __ adcl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
-                Address(ESP, locations->InAt(1).GetHighStackIndex(kX86WordSize)));
+        __ addl(first.AsRegisterPairLow<Register>(), Address(ESP, second.GetStackIndex()));
+        __ adcl(first.AsRegisterPairHigh<Register>(),
+                Address(ESP, second.GetHighStackIndex(kX86WordSize)));
       }
       break;
     }
 
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+    case Primitive::kPrimFloat: {
+      if (second.IsFpuRegister()) {
+        __ addss(first.As<XmmRegister>(), second.As<XmmRegister>());
+      } else {
+        __ addss(first.As<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+      }
       break;
+    }
+
+    case Primitive::kPrimDouble: {
+      if (second.IsFpuRegister()) {
+        __ addsd(first.As<XmmRegister>(), second.As<XmmRegister>());
+      } else {
+        __ addsd(first.As<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+      }
+      break;
+    }
 
     default:
-      LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
+      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
   }
 }
 
 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: {
@@ -885,93 +1431,331 @@
       locations->SetOut(Location::SameAsFirstInput());
       break;
     }
-
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
       break;
+    }
 
     default:
-      LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
+      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
   }
-  sub->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
   LocationSummary* locations = sub->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  DCHECK(first.Equals(locations->Out()));
   switch (sub->GetResultType()) {
     case Primitive::kPrimInt: {
-      DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(),
-                locations->Out().AsX86().AsCpuRegister());
-      if (locations->InAt(1).IsRegister()) {
-        __ subl(locations->InAt(0).AsX86().AsCpuRegister(),
-                locations->InAt(1).AsX86().AsCpuRegister());
-      } else if (locations->InAt(1).IsConstant()) {
-        HConstant* instruction = locations->InAt(1).GetConstant();
-        Immediate imm(instruction->AsIntConstant()->GetValue());
-        __ subl(locations->InAt(0).AsX86().AsCpuRegister(), imm);
+      if (second.IsRegister()) {
+        __ subl(first.As<Register>(), second.As<Register>());
+      } else if (second.IsConstant()) {
+        __ subl(first.As<Register>(), Immediate(second.GetConstant()->AsIntConstant()->GetValue()));
       } else {
-        __ subl(locations->InAt(0).AsX86().AsCpuRegister(),
-                Address(ESP, locations->InAt(1).GetStackIndex()));
+        __ subl(first.As<Register>(), Address(ESP, second.GetStackIndex()));
       }
       break;
     }
 
     case Primitive::kPrimLong: {
-      DCHECK_EQ(locations->InAt(0).AsX86().AsRegisterPair(),
-                locations->Out().AsX86().AsRegisterPair());
-      if (locations->InAt(1).IsRegister()) {
-        __ subl(locations->InAt(0).AsX86().AsRegisterPairLow(),
-                locations->InAt(1).AsX86().AsRegisterPairLow());
-        __ sbbl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
-                locations->InAt(1).AsX86().AsRegisterPairHigh());
+      if (second.IsRegister()) {
+        __ subl(first.AsRegisterPairLow<Register>(), second.AsRegisterPairLow<Register>());
+        __ sbbl(first.AsRegisterPairHigh<Register>(), second.AsRegisterPairHigh<Register>());
       } else {
-        __ subl(locations->InAt(0).AsX86().AsRegisterPairLow(),
-                Address(ESP, locations->InAt(1).GetStackIndex()));
-        __ sbbl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
-                Address(ESP, locations->InAt(1).GetHighStackIndex(kX86WordSize)));
+        __ subl(first.AsRegisterPairLow<Register>(), Address(ESP, second.GetStackIndex()));
+        __ sbbl(first.AsRegisterPairHigh<Register>(),
+                Address(ESP, second.GetHighStackIndex(kX86WordSize)));
       }
       break;
     }
 
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
+    case Primitive::kPrimFloat: {
+      __ subss(first.As<XmmRegister>(), second.As<XmmRegister>());
       break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ subsd(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
 
     default:
-      LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
+      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
   }
 }
 
+void LocationsBuilderX86::VisitMul(HMul* mul) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::Any());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    case Primitive::kPrimLong: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      // TODO: Currently this handles only stack operands:
+      // - we don't have enough registers because we currently use Quick ABI.
+      // - by the time we have a working register allocator we will probably change the ABI
+      // and fix the above.
+      // - we don't have a way yet to request operands on stack but the base line compiler
+      // will leave the operands on the stack with Any().
+      locations->SetInAt(1, Location::Any());
+      locations->SetOut(Location::SameAsFirstInput());
+      // Needed for imul on 32bits with 64bits output.
+      locations->AddTemp(Location::RegisterLocation(EAX));
+      locations->AddTemp(Location::RegisterLocation(EDX));
+      break;
+    }
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitMul(HMul* mul) {
+  LocationSummary* locations = mul->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  DCHECK(first.Equals(locations->Out()));
+
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt: {
+      if (second.IsRegister()) {
+        __ imull(first.As<Register>(), second.As<Register>());
+      } else if (second.IsConstant()) {
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+        __ imull(first.As<Register>(), imm);
+      } else {
+        DCHECK(second.IsStackSlot());
+        __ imull(first.As<Register>(), Address(ESP, second.GetStackIndex()));
+      }
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      DCHECK(second.IsDoubleStackSlot());
+
+      Register in1_hi = first.AsRegisterPairHigh<Register>();
+      Register in1_lo = first.AsRegisterPairLow<Register>();
+      Address in2_hi(ESP, second.GetHighStackIndex(kX86WordSize));
+      Address in2_lo(ESP, second.GetStackIndex());
+      Register eax = locations->GetTemp(0).As<Register>();
+      Register edx = locations->GetTemp(1).As<Register>();
+
+      DCHECK_EQ(EAX, eax);
+      DCHECK_EQ(EDX, edx);
+
+      // input: in1 - 64 bits, in2 - 64 bits
+      // output: in1
+      // formula: in1.hi : in1.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
+      // parts: in1.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
+      // parts: in1.lo = (in1.lo * in2.lo)[31:0]
+
+      __ movl(eax, in2_hi);
+      // eax <- in1.lo * in2.hi
+      __ imull(eax, in1_lo);
+      // in1.hi <- in1.hi * in2.lo
+      __ imull(in1_hi, in2_lo);
+      // in1.hi <- in1.lo * in2.hi + in1.hi * in2.lo
+      __ addl(in1_hi, eax);
+      // move in1_lo to eax to prepare for double precision
+      __ movl(eax, in1_lo);
+      // edx:eax <- in1.lo * in2.lo
+      __ mull(in2_lo);
+      // in1.hi <- in2.hi * in1.lo +  in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
+      __ addl(in1_hi, edx);
+      // in1.lo <- (in1.lo * in2.lo)[31:0];
+      __ movl(in1_lo, eax);
+
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ mulss(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ mulsd(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void LocationsBuilderX86::VisitDiv(HDiv* div) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall);
+  switch (div->GetResultType()) {
+    case Primitive::kPrimInt: {
+      locations->SetInAt(0, Location::RegisterLocation(EAX));
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      // Intel uses edx:eax as the dividend.
+      locations->AddTemp(Location::RegisterLocation(EDX));
+      break;
+    }
+    case Primitive::kPrimLong: {
+      LOG(FATAL) << "Not implemented div type" << div->GetResultType();
+      break;
+    }
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) {
+  LocationSummary* locations = div->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  DCHECK(first.Equals(locations->Out()));
+
+  switch (div->GetResultType()) {
+    case Primitive::kPrimInt: {
+      Register first_reg = first.As<Register>();
+      Register second_reg = second.As<Register>();
+      DCHECK_EQ(EAX, first_reg);
+      DCHECK_EQ(EDX, locations->GetTemp(0).As<Register>());
+
+      SlowPathCodeX86* slow_path =
+          new (GetGraph()->GetArena()) DivMinusOneSlowPathX86(first_reg);
+      codegen_->AddSlowPath(slow_path);
+
+      // 0x80000000/-1 triggers an arithmetic exception!
+      // Dividing by -1 is actually negation and -0x800000000 = 0x80000000 so
+      // it's safe to just use negl instead of more complex comparisons.
+
+      __ cmpl(second_reg, Immediate(-1));
+      __ j(kEqual, slow_path->GetEntryLabel());
+
+      // edx:eax <- sign-extended of eax
+      __ cdq();
+      // eax = quotient, edx = remainder
+      __ idivl(second_reg);
+
+      __ Bind(slow_path->GetExitLabel());
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      LOG(FATAL) << "Not implemented div type" << div->GetResultType();
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ divss(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ divsd(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
+  }
+}
+
+void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::Any());
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+  SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathX86(instruction);
+  codegen_->AddSlowPath(slow_path);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location value = locations->InAt(0);
+
+  if (value.IsRegister()) {
+    __ testl(value.As<Register>(), value.As<Register>());
+  } else if (value.IsStackSlot()) {
+    __ cmpl(Address(ESP, value.GetStackIndex()), Immediate(0));
+  } else {
+    DCHECK(value.IsConstant()) << value;
+    if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
+    __ jmp(slow_path->GetEntryLabel());
+    }
+    return;
+  }
+  __ j(kEqual, slow_path->GetEntryLabel());
+}
+
 void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
-  locations->SetOut(X86CpuLocation(EAX));
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  locations->SetOut(Location::RegisterLocation(EAX));
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(0)));
-  locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(1)));
-  instruction->SetLocations(locations);
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
   InvokeRuntimeCallingConvention calling_convention;
-  LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
   __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
 
   __ fs()->call(
       Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
 
-  codegen_->RecordPcInfo(instruction->GetDexPc());
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+  DCHECK(!codegen_->IsLeafMethod());
+}
+
+void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  locations->SetOut(Location::RegisterLocation(EAX));
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+}
+
+void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) {
+  InvokeRuntimeCallingConvention calling_convention;
+  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+  __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
+
+  __ fs()->call(
+      Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocArrayWithAccessCheck)));
+
+  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,56 +1763,73 @@
     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   }
   locations->SetOut(location);
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) {
+  UNUSED(instruction);
 }
 
-void LocationsBuilderX86::VisitNot(HNot* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+void LocationsBuilderX86::VisitNot(HNot* not_) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
-void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
+void InstructionCodeGeneratorX86::VisitNot(HNot* not_) {
+  LocationSummary* locations = not_->GetLocations();
+  Location in = locations->InAt(0);
   Location out = locations->Out();
-  DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), out.AsX86().AsCpuRegister());
-  __ xorl(out.AsX86().AsCpuRegister(), Immediate(1));
+  DCHECK(in.Equals(out));
+  switch (not_->InputAt(0)->GetType()) {
+    case Primitive::kPrimBoolean:
+      __ xorl(out.As<Register>(), Immediate(1));
+      break;
+
+    case Primitive::kPrimInt:
+      __ notl(out.As<Register>());
+      break;
+
+    case Primitive::kPrimLong:
+      __ notl(out.AsRegisterPairLow<Register>());
+      __ notl(out.AsRegisterPairHigh<Register>());
+      break;
+
+    default:
+      LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
+  }
 }
 
 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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
-  Label greater, done;
   LocationSummary* locations = compare->GetLocations();
   switch (compare->InputAt(0)->GetType()) {
     case Primitive::kPrimLong: {
       Label less, greater, done;
-      Register output = locations->Out().AsX86().AsCpuRegister();
-      X86ManagedRegister left = locations->InAt(0).AsX86();
+      Register output = locations->Out().As<Register>();
+      Location left = locations->InAt(0);
       Location right = locations->InAt(1);
       if (right.IsRegister()) {
-        __ cmpl(left.AsRegisterPairHigh(), right.AsX86().AsRegisterPairHigh());
+        __ cmpl(left.AsRegisterPairHigh<Register>(), right.AsRegisterPairHigh<Register>());
       } else {
         DCHECK(right.IsDoubleStackSlot());
-        __ cmpl(left.AsRegisterPairHigh(), Address(ESP, right.GetHighStackIndex(kX86WordSize)));
+        __ cmpl(left.AsRegisterPairHigh<Register>(),
+                Address(ESP, right.GetHighStackIndex(kX86WordSize)));
       }
       __ j(kLess, &less);  // Signed compare.
       __ j(kGreater, &greater);  // Signed compare.
-      if (right.IsRegister()) {
-        __ cmpl(left.AsRegisterPairLow(), right.AsX86().AsRegisterPairLow());
+      if (right.IsRegisterPair()) {
+        __ cmpl(left.AsRegisterPairLow<Register>(), right.AsRegisterPairLow<Register>());
       } else {
         DCHECK(right.IsDoubleStackSlot());
-        __ cmpl(left.AsRegisterPairLow(), Address(ESP, right.GetStackIndex()));
+        __ cmpl(left.AsRegisterPairLow<Register>(), Address(ESP, right.GetStackIndex()));
       }
       __ movl(output, Immediate(0));
       __ j(kEqual, &done);
@@ -1050,84 +1851,99 @@
 }
 
 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) {
+  UNUSED(instruction);
   LOG(FATAL) << "Unreachable";
 }
 
 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();
-  if (field_type == Primitive::kPrimBoolean || field_type == Primitive::kPrimByte) {
+  Primitive::Type field_type = instruction->GetFieldType();
+  bool is_object_type = field_type == Primitive::kPrimNot;
+  bool is_byte_type = (field_type == Primitive::kPrimBoolean)
+      || (field_type == Primitive::kPrimByte);
+  // The register allocator does not support multiple
+  // inputs that die at entry with one in a specific register.
+  if (is_byte_type) {
     // Ensure the value is in a byte register.
-    locations->SetInAt(1, X86CpuLocation(EAX));
+    locations->SetInAt(1, Location::RegisterLocation(EAX));
   } else {
     locations->SetInAt(1, Location::RequiresRegister());
   }
   // Temporary registers for the write barrier.
-  if (field_type == Primitive::kPrimNot) {
+  if (is_object_type) {
     locations->AddTemp(Location::RequiresRegister());
     // Ensure the card is in a byte register.
-    locations->AddTemp(X86CpuLocation(ECX));
+    locations->AddTemp(Location::RegisterLocation(ECX));
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+  Register obj = locations->InAt(0).As<Register>();
   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:
     case Primitive::kPrimByte: {
-      ByteRegister value = locations->InAt(1).AsX86().AsByteRegister();
+      ByteRegister value = locations->InAt(1).As<ByteRegister>();
       __ movb(Address(obj, offset), value);
       break;
     }
 
     case Primitive::kPrimShort:
     case Primitive::kPrimChar: {
-      Register value = locations->InAt(1).AsX86().AsCpuRegister();
+      Register value = locations->InAt(1).As<Register>();
       __ movw(Address(obj, offset), value);
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      Register value = locations->InAt(1).AsX86().AsCpuRegister();
+      Register value = locations->InAt(1).As<Register>();
       __ movl(Address(obj, offset), value);
 
       if (field_type == Primitive::kPrimNot) {
-        Register temp = locations->GetTemp(0).AsX86().AsCpuRegister();
-        Register card = locations->GetTemp(1).AsX86().AsCpuRegister();
+        Register temp = locations->GetTemp(0).As<Register>();
+        Register card = locations->GetTemp(1).As<Register>();
         codegen_->MarkGCCard(temp, card, obj, value);
       }
       break;
     }
 
     case Primitive::kPrimLong: {
-      X86ManagedRegister value = locations->InAt(1).AsX86();
-      __ movl(Address(obj, offset), value.AsRegisterPairLow());
-      __ movl(Address(obj, kX86WordSize + offset), value.AsRegisterPairHigh());
+      Location value = locations->InAt(1);
+      __ movl(Address(obj, offset), value.AsRegisterPairLow<Register>());
+      __ movl(Address(obj, kX86WordSize + offset), value.AsRegisterPairHigh<Register>());
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << field_type;
+    case Primitive::kPrimFloat: {
+      XmmRegister value = locations->InAt(1).As<XmmRegister>();
+      __ movss(Address(obj, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      XmmRegister value = locations->InAt(1).As<XmmRegister>();
+      __ movsd(Address(obj, offset), value);
+      break;
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << field_type;
+      UNREACHABLE();
   }
 }
 
@@ -1144,150 +1960,161 @@
 }
 
 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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+  Register obj = locations->InAt(0).As<Register>();
   uint32_t offset = instruction->GetFieldOffset().Uint32Value();
 
   switch (instruction->GetType()) {
     case Primitive::kPrimBoolean: {
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       __ movzxb(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimByte: {
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       __ movsxb(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimShort: {
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       __ movsxw(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimChar: {
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       __ movzxw(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       __ movl(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimLong: {
       // TODO: support volatile.
-      X86ManagedRegister out = locations->Out().AsX86();
-      __ movl(out.AsRegisterPairLow(), Address(obj, offset));
-      __ movl(out.AsRegisterPairHigh(), Address(obj, kX86WordSize + offset));
+      __ movl(locations->Out().AsRegisterPairLow<Register>(), Address(obj, offset));
+      __ movl(locations->Out().AsRegisterPairHigh<Register>(), Address(obj, kX86WordSize + offset));
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+    case Primitive::kPrimFloat: {
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      __ movss(out, Address(obj, offset));
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      __ movsd(out, Address(obj, offset));
+      break;
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
   }
 }
 
 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);
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
 }
 
 void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction->GetDexPc());
+  SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
   Location obj = locations->InAt(0);
-  DCHECK(obj.Equals(locations->Out()));
 
   if (obj.IsRegister()) {
-    __ cmpl(obj.AsX86().AsCpuRegister(), Immediate(0));
-  } else {
-    DCHECK(locations->InAt(0).IsStackSlot());
+    __ cmpl(obj.As<Register>(), Immediate(0));
+  } else if (obj.IsStackSlot()) {
     __ cmpl(Address(ESP, obj.GetStackIndex()), Immediate(0));
+  } else {
+    DCHECK(obj.IsConstant()) << obj;
+    DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
+    __ jmp(slow_path->GetEntryLabel());
+    return;
   }
   __ j(kEqual, slow_path->GetEntryLabel());
 }
 
 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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+  Register obj = locations->InAt(0).As<Register>();
   Location index = locations->InAt(1);
 
   switch (instruction->GetType()) {
     case Primitive::kPrimBoolean: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         __ movzxb(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
       } else {
-        __ movzxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset));
+        __ movzxb(out, Address(obj, index.As<Register>(), TIMES_1, data_offset));
       }
       break;
     }
 
     case Primitive::kPrimByte: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         __ movsxb(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
       } else {
-        __ movsxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset));
+        __ movsxb(out, Address(obj, index.As<Register>(), TIMES_1, data_offset));
       }
       break;
     }
 
     case Primitive::kPrimShort: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         __ movsxw(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
       } else {
-        __ movsxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset));
+        __ movsxw(out, Address(obj, index.As<Register>(), TIMES_2, data_offset));
       }
       break;
     }
 
     case Primitive::kPrimChar: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         __ movzxw(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
       } else {
-        __ movzxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset));
+        __ movzxw(out, Address(obj, index.As<Register>(), TIMES_2, data_offset));
       }
       break;
     }
@@ -1295,28 +2122,28 @@
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
-      Register out = locations->Out().AsX86().AsCpuRegister();
+      Register out = locations->Out().As<Register>();
       if (index.IsConstant()) {
         __ movl(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
       } else {
-        __ movl(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset));
+        __ movl(out, Address(obj, index.As<Register>(), TIMES_4, data_offset));
       }
       break;
     }
 
     case Primitive::kPrimLong: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
-      X86ManagedRegister out = locations->Out().AsX86();
+      Location out = locations->Out();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ movl(out.AsRegisterPairLow(), Address(obj, offset));
-        __ movl(out.AsRegisterPairHigh(), Address(obj, offset + kX86WordSize));
+        __ movl(out.AsRegisterPairLow<Register>(), Address(obj, offset));
+        __ movl(out.AsRegisterPairHigh<Register>(), Address(obj, offset + kX86WordSize));
       } else {
-        __ movl(out.AsRegisterPairLow(),
-                Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset));
-        __ movl(out.AsRegisterPairHigh(),
-                Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize));
+        __ movl(out.AsRegisterPairLow<Register>(),
+                Address(obj, index.As<Register>(), TIMES_8, data_offset));
+        __ movl(out.AsRegisterPairHigh<Register>(),
+                Address(obj, index.As<Register>(), TIMES_8, data_offset + kX86WordSize));
       }
       break;
     }
@@ -1324,51 +2151,68 @@
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
       LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
-
+      UNREACHABLE();
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
   }
 }
 
 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();
+    locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+    locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
   } else {
+    bool is_byte_type = (value_type == Primitive::kPrimBoolean)
+        || (value_type == Primitive::kPrimByte);
+    // We need the inputs to be different than the output in case of long operation.
+    // In case of a byte operation, the register allocator does not support multiple
+    // inputs that die at entry with one in a specific register.
     locations->SetInAt(0, Location::RequiresRegister());
     locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
-    if (value_type == Primitive::kPrimBoolean || value_type == Primitive::kPrimByte) {
+    if (is_byte_type) {
       // Ensure the value is in a byte register.
-      locations->SetInAt(2, X86CpuLocation(EAX));
+      locations->SetInAt(2, Location::ByteRegisterOrConstant(EAX, instruction->InputAt(2)));
     } else {
-      locations->SetInAt(2, Location::RequiresRegister());
+      locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
     }
   }
-
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+  Register obj = locations->InAt(0).As<Register>();
   Location index = locations->InAt(1);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Location value = locations->InAt(2);
+  Primitive::Type value_type = instruction->GetComponentType();
 
   switch (value_type) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
-      ByteRegister value = locations->InAt(2).AsX86().AsByteRegister();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
-        __ movb(Address(obj, offset), value);
+        if (value.IsRegister()) {
+          __ movb(Address(obj, offset), value.As<ByteRegister>());
+        } else {
+          __ movb(Address(obj, offset),
+                  Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       } else {
-        __ movb(Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset), value);
+        if (value.IsRegister()) {
+          __ movb(Address(obj, index.As<Register>(), TIMES_1, data_offset),
+                  value.As<ByteRegister>());
+        } else {
+          __ movb(Address(obj, index.As<Register>(), TIMES_1, data_offset),
+                  Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       }
       break;
     }
@@ -1376,24 +2220,43 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimChar: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
-      Register value = locations->InAt(2).AsX86().AsCpuRegister();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
-        __ movw(Address(obj, offset), value);
+        if (value.IsRegister()) {
+          __ movw(Address(obj, offset), value.As<Register>());
+        } else {
+          __ movw(Address(obj, offset),
+                  Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       } else {
-        __ movw(Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset), value);
+        if (value.IsRegister()) {
+          __ movw(Address(obj, index.As<Register>(), TIMES_2, data_offset),
+                  value.As<Register>());
+        } else {
+          __ movw(Address(obj, index.As<Register>(), TIMES_2, data_offset),
+                  Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       }
       break;
     }
 
     case Primitive::kPrimInt: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
-      Register value = locations->InAt(2).AsX86().AsCpuRegister();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-        __ movl(Address(obj, offset), value);
+        if (value.IsRegister()) {
+          __ movl(Address(obj, offset), value.As<Register>());
+        } else {
+          __ movl(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       } else {
-        __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset), value);
+        if (value.IsRegister()) {
+          __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset),
+                  value.As<Register>());
+        } else {
+          __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset),
+                  Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       }
       break;
     }
@@ -1401,22 +2264,37 @@
     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;
     }
 
     case Primitive::kPrimLong: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
-      X86ManagedRegister value = locations->InAt(2).AsX86();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ movl(Address(obj, offset), value.AsRegisterPairLow());
-        __ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh());
+        if (value.IsRegisterPair()) {
+          __ movl(Address(obj, offset), value.AsRegisterPairLow<Register>());
+          __ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh<Register>());
+        } else {
+          DCHECK(value.IsConstant());
+          int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
+          __ movl(Address(obj, offset), Immediate(Low32Bits(val)));
+          __ movl(Address(obj, offset + kX86WordSize), Immediate(High32Bits(val)));
+        }
       } else {
-        __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset),
-                value.AsRegisterPairLow());
-        __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize),
-                value.AsRegisterPairHigh());
+        if (value.IsRegisterPair()) {
+          __ movl(Address(obj, index.As<Register>(), TIMES_8, data_offset),
+                  value.AsRegisterPairLow<Register>());
+          __ movl(Address(obj, index.As<Register>(), TIMES_8, data_offset + kX86WordSize),
+                  value.AsRegisterPairHigh<Register>());
+        } else {
+          DCHECK(value.IsConstant());
+          int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
+          __ movl(Address(obj, index.As<Register>(), TIMES_8, data_offset),
+                  Immediate(Low32Bits(val)));
+          __ movl(Address(obj, index.As<Register>(), TIMES_8, data_offset + kX86WordSize),
+                  Immediate(High32Bits(val)));
+        }
       }
       break;
     }
@@ -1424,44 +2302,46 @@
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
       LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
-
+      UNREACHABLE();
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
   }
 }
 
 void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
   instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
-  Register obj = locations->InAt(0).AsX86().AsCpuRegister();
-  Register out = locations->Out().AsX86().AsCpuRegister();
+  Register obj = locations->InAt(0).As<Register>();
+  Register out = locations->Out().As<Register>();
   __ movl(out, Address(obj, offset));
 }
 
 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);
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
 }
 
 void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
-      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+  SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
-  Register index = locations->InAt(0).AsX86().AsCpuRegister();
-  Register length = locations->InAt(1).AsX86().AsCpuRegister();
+  Register index = locations->InAt(0).As<Register>();
+  Register length = locations->InAt(1).As<Register>();
 
   __ cmpl(index, length);
   __ j(kAboveEqual, slow_path->GetEntryLabel());
@@ -1473,9 +2353,11 @@
 
 void InstructionCodeGeneratorX86::VisitTemporary(HTemporary* temp) {
   // Nothing to do, this is driven by the code generator.
+  UNUSED(temp);
 }
 
 void LocationsBuilderX86::VisitParallelMove(HParallelMove* instruction) {
+  UNUSED(instruction);
   LOG(FATAL) << "Unreachable";
 }
 
@@ -1483,6 +2365,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();
 }
@@ -1502,14 +2418,14 @@
 
   if (source.IsRegister()) {
     if (destination.IsRegister()) {
-      __ movl(destination.AsX86().AsCpuRegister(), source.AsX86().AsCpuRegister());
+      __ movl(destination.As<Register>(), source.As<Register>());
     } else {
       DCHECK(destination.IsStackSlot());
-      __ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsCpuRegister());
+      __ movl(Address(ESP, destination.GetStackIndex()), source.As<Register>());
     }
   } else if (source.IsStackSlot()) {
     if (destination.IsRegister()) {
-      __ movl(destination.AsX86().AsCpuRegister(), Address(ESP, source.GetStackIndex()));
+      __ movl(destination.As<Register>(), Address(ESP, source.GetStackIndex()));
     } else {
       DCHECK(destination.IsStackSlot());
       MoveMemoryToMemory(destination.GetStackIndex(),
@@ -1519,7 +2435,7 @@
     HIntConstant* instruction = source.GetConstant()->AsIntConstant();
     Immediate imm(instruction->AsIntConstant()->GetValue());
     if (destination.IsRegister()) {
-      __ movl(destination.AsX86().AsCpuRegister(), imm);
+      __ movl(destination.As<Register>(), imm);
     } else {
       __ movl(Address(ESP, destination.GetStackIndex()), imm);
     }
@@ -1561,11 +2477,11 @@
   Location destination = move->GetDestination();
 
   if (source.IsRegister() && destination.IsRegister()) {
-    __ xchgl(destination.AsX86().AsCpuRegister(), source.AsX86().AsCpuRegister());
+    __ xchgl(destination.As<Register>(), source.As<Register>());
   } else if (source.IsRegister() && destination.IsStackSlot()) {
-    Exchange(source.AsX86().AsCpuRegister(), destination.GetStackIndex());
+    Exchange(source.As<Register>(), destination.GetStackIndex());
   } else if (source.IsStackSlot() && destination.IsRegister()) {
-    Exchange(destination.AsX86().AsCpuRegister(), source.GetStackIndex());
+    Exchange(destination.As<Register>(), source.GetStackIndex());
   } else if (source.IsStackSlot() && destination.IsStackSlot()) {
     Exchange(destination.GetStackIndex(), source.GetStackIndex());
   } else {
@@ -1581,5 +2497,316 @@
   __ popl(static_cast<Register>(reg));
 }
 
+void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) {
+  LocationSummary::CallKind call_kind = cls->CanCallRuntime()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) {
+  Register out = cls->GetLocations()->Out().As<Register>();
+  if (cls->IsReferrersClass()) {
+    DCHECK(!cls->CanCallRuntime());
+    DCHECK(!cls->MustGenerateClinitCheck());
+    codegen_->LoadCurrentMethod(out);
+    __ movl(out, Address(out, mirror::ArtMethod::DeclaringClassOffset().Int32Value()));
+  } else {
+    DCHECK(cls->CanCallRuntime());
+    codegen_->LoadCurrentMethod(out);
+    __ movl(out, Address(out, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+    __ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+
+    SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86(
+        cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+    codegen_->AddSlowPath(slow_path);
+    __ testl(out, out);
+    __ j(kEqual, slow_path->GetEntryLabel());
+    if (cls->MustGenerateClinitCheck()) {
+      GenerateClassInitializationCheck(slow_path, out);
+    } else {
+      __ Bind(slow_path->GetExitLabel());
+    }
+  }
+}
+
+void LocationsBuilderX86::VisitClinitCheck(HClinitCheck* check) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
+  locations->SetInAt(0, Location::RequiresRegister());
+  if (check->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitClinitCheck(HClinitCheck* check) {
+  // We assume the class to not be null.
+  SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86(
+      check->GetLoadClass(), check, check->GetDexPc(), true);
+  codegen_->AddSlowPath(slow_path);
+  GenerateClassInitializationCheck(slow_path, check->GetLocations()->InAt(0).As<Register>());
+}
+
+void InstructionCodeGeneratorX86::GenerateClassInitializationCheck(
+    SlowPathCodeX86* slow_path, Register class_reg) {
+  __ cmpl(Address(class_reg,  mirror::Class::StatusOffset().Int32Value()),
+          Immediate(mirror::Class::kStatusInitialized));
+  __ j(kLess, slow_path->GetEntryLabel());
+  __ Bind(slow_path->GetExitLabel());
+  // No need for memory fence, thanks to the X86 memory model.
+}
+
+void LocationsBuilderX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register cls = locations->InAt(0).As<Register>();
+  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+
+  switch (instruction->GetType()) {
+    case Primitive::kPrimBoolean: {
+      Register out = locations->Out().As<Register>();
+      __ movzxb(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimByte: {
+      Register out = locations->Out().As<Register>();
+      __ movsxb(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimShort: {
+      Register out = locations->Out().As<Register>();
+      __ movsxw(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimChar: {
+      Register out = locations->Out().As<Register>();
+      __ movzxw(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      Register out = locations->Out().As<Register>();
+      __ movl(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      // TODO: support volatile.
+      __ movl(locations->Out().AsRegisterPairLow<Register>(), Address(cls, offset));
+      __ movl(locations->Out().AsRegisterPairHigh<Register>(), Address(cls, kX86WordSize + offset));
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      __ movss(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      __ movsd(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  Primitive::Type field_type = instruction->GetFieldType();
+  bool is_object_type = field_type == Primitive::kPrimNot;
+  bool is_byte_type = (field_type == Primitive::kPrimBoolean)
+      || (field_type == Primitive::kPrimByte);
+  // The register allocator does not support multiple
+  // inputs that die at entry with one in a specific register.
+  if (is_byte_type) {
+    // Ensure the value is in a byte register.
+    locations->SetInAt(1, Location::RegisterLocation(EAX));
+  } else {
+    locations->SetInAt(1, Location::RequiresRegister());
+  }
+  // Temporary registers for the write barrier.
+  if (is_object_type) {
+    locations->AddTemp(Location::RequiresRegister());
+    // Ensure the card is in a byte register.
+    locations->AddTemp(Location::RegisterLocation(ECX));
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register cls = locations->InAt(0).As<Register>();
+  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+  Primitive::Type field_type = instruction->GetFieldType();
+
+  switch (field_type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte: {
+      ByteRegister value = locations->InAt(1).As<ByteRegister>();
+      __ movb(Address(cls, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar: {
+      Register value = locations->InAt(1).As<Register>();
+      __ movw(Address(cls, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      Register value = locations->InAt(1).As<Register>();
+      __ movl(Address(cls, offset), value);
+
+      if (field_type == Primitive::kPrimNot) {
+        Register temp = locations->GetTemp(0).As<Register>();
+        Register card = locations->GetTemp(1).As<Register>();
+        codegen_->MarkGCCard(temp, card, cls, value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      Location value = locations->InAt(1);
+      __ movl(Address(cls, offset), value.AsRegisterPairLow<Register>());
+      __ movl(Address(cls, kX86WordSize + offset), value.AsRegisterPairHigh<Register>());
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      XmmRegister value = locations->InAt(1).As<XmmRegister>();
+      __ movss(Address(cls, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      XmmRegister value = locations->InAt(1).As<XmmRegister>();
+      __ movsd(Address(cls, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << field_type;
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) {
+  SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
+  codegen_->AddSlowPath(slow_path);
+
+  Register out = load->GetLocations()->Out().As<Register>();
+  codegen_->LoadCurrentMethod(out);
+  __ movl(out, Address(out, mirror::ArtMethod::DexCacheStringsOffset().Int32Value()));
+  __ movl(out, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
+  __ testl(out, out);
+  __ j(kEqual, slow_path->GetEntryLabel());
+  __ Bind(slow_path->GetExitLabel());
+}
+
+void LocationsBuilderX86::VisitLoadException(HLoadException* load) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitLoadException(HLoadException* load) {
+  Address address = Address::Absolute(Thread::ExceptionOffset<kX86WordSize>().Int32Value());
+  __ fs()->movl(load->GetLocations()->Out().As<Register>(), address);
+  __ fs()->movl(address, Immediate(0));
+}
+
+void LocationsBuilderX86::VisitThrow(HThrow* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+}
+
+void InstructionCodeGeneratorX86::VisitThrow(HThrow* instruction) {
+  __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pDeliverException)));
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void LocationsBuilderX86::VisitTypeCheck(HTypeCheck* instruction) {
+  LocationSummary::CallKind call_kind = instruction->IsClassFinal()
+      ? LocationSummary::kNoCall
+      : LocationSummary::kCallOnSlowPath;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::Any());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitTypeCheck(HTypeCheck* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register obj = locations->InAt(0).As<Register>();
+  Location cls = locations->InAt(1);
+  Register out = locations->Out().As<Register>();
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+  Label done, zero;
+  SlowPathCodeX86* slow_path = nullptr;
+
+  // Return 0 if `obj` is null.
+  // TODO: avoid this check if we know obj is not null.
+  __ testl(obj, obj);
+  __ j(kEqual, &zero);
+  __ movl(out, Address(obj, class_offset));
+  // Compare the class of `obj` with `cls`.
+  if (cls.IsRegister()) {
+    __ cmpl(out, cls.As<Register>());
+  } else {
+    DCHECK(cls.IsStackSlot()) << cls;
+    __ cmpl(out, Address(ESP, cls.GetStackIndex()));
+  }
+
+  if (instruction->IsClassFinal()) {
+    // Classes must be equal for the instanceof to succeed.
+    __ j(kNotEqual, &zero);
+    __ movl(out, Immediate(1));
+    __ jmp(&done);
+  } else {
+    // If the classes are not equal, we go into a slow path.
+    DCHECK(locations->OnlyCallsOnSlowPath());
+    slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathX86(
+        instruction, Location::RegisterLocation(out));
+    codegen_->AddSlowPath(slow_path);
+    __ j(kNotEqual, slow_path->GetEntryLabel());
+    __ movl(out, Immediate(1));
+    __ jmp(&done);
+  }
+  __ Bind(&zero);
+  __ movl(out, Immediate(0));
+  if (slow_path != nullptr) {
+    __ Bind(slow_path->GetExitLabel());
+  }
+  __ Bind(&done);
+}
+
 }  // namespace x86
 }  // namespace art
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 7c50204..85fe21c 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -28,15 +28,21 @@
 static constexpr size_t kX86WordSize = 4;
 
 class CodeGeneratorX86;
+class SlowPathCodeX86;
 
 static constexpr Register kParameterCoreRegisters[] = { ECX, EDX, EBX };
 static constexpr RegisterPair kParameterCorePairRegisters[] = { ECX_EDX, EDX_EBX };
 static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
+static constexpr XmmRegister kParameterFpuRegisters[] = { };
+static constexpr size_t kParameterFpuRegistersLength = 0;
 
-class InvokeDexCallingConvention : public CallingConvention<Register> {
+class InvokeDexCallingConvention : public CallingConvention<Register, XmmRegister> {
  public:
-  InvokeDexCallingConvention()
-      : CallingConvention(kParameterCoreRegisters, kParameterCoreRegistersLength) {}
+  InvokeDexCallingConvention() : CallingConvention(
+      kParameterCoreRegisters,
+      kParameterCoreRegistersLength,
+      kParameterFpuRegisters,
+      kParameterFpuRegistersLength) {}
 
   RegisterPair GetRegisterPairAt(size_t argument_index) {
     DCHECK_LT(argument_index + 1, GetNumberOfRegisters());
@@ -65,10 +71,10 @@
   ParallelMoveResolverX86(ArenaAllocator* allocator, CodeGeneratorX86* codegen)
       : ParallelMoveResolver(allocator), codegen_(codegen) {}
 
-  virtual void EmitMove(size_t index) OVERRIDE;
-  virtual void EmitSwap(size_t index) OVERRIDE;
-  virtual void SpillScratch(int reg) OVERRIDE;
-  virtual void RestoreScratch(int reg) OVERRIDE;
+  void EmitMove(size_t index) OVERRIDE;
+  void EmitSwap(size_t index) OVERRIDE;
+  void SpillScratch(int reg) OVERRIDE;
+  void RestoreScratch(int reg) OVERRIDE;
 
   X86Assembler* GetAssembler() const;
 
@@ -87,13 +93,15 @@
   LocationsBuilderX86(HGraph* graph, CodeGeneratorX86* codegen)
       : HGraphVisitor(graph), codegen_(codegen) {}
 
-#define DECLARE_VISIT_INSTRUCTION(name)     \
-  virtual void Visit##name(H##name* instr);
+#define DECLARE_VISIT_INSTRUCTION(name, super)     \
+  void Visit##name(H##name* instr) OVERRIDE;
 
   FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
+  void HandleInvoke(HInvoke* invoke);
+
  private:
   CodeGeneratorX86* const codegen_;
   InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -105,18 +113,22 @@
  public:
   InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen);
 
-#define DECLARE_VISIT_INSTRUCTION(name)     \
-  virtual void Visit##name(H##name* instr);
+#define DECLARE_VISIT_INSTRUCTION(name, super)     \
+  void Visit##name(H##name* instr) OVERRIDE;
 
   FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
-  void LoadCurrentMethod(Register reg);
-
   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);
+  void GenerateClassInitializationCheck(SlowPathCodeX86* slow_path, Register class_reg);
+
   X86Assembler* const assembler_;
   CodeGeneratorX86* const codegen_;
 
@@ -128,52 +140,52 @@
   explicit CodeGeneratorX86(HGraph* graph);
   virtual ~CodeGeneratorX86() {}
 
-  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;
+  void GenerateFrameEntry() OVERRIDE;
+  void GenerateFrameExit() OVERRIDE;
+  void Bind(HBasicBlock* block) OVERRIDE;
+  void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+  size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
 
-  virtual size_t GetWordSize() const OVERRIDE {
+  size_t GetWordSize() const OVERRIDE {
     return kX86WordSize;
   }
 
-  virtual size_t FrameEntrySpillSize() const OVERRIDE;
+  size_t FrameEntrySpillSize() const OVERRIDE;
 
-  virtual HGraphVisitor* GetLocationBuilder() OVERRIDE {
+  HGraphVisitor* GetLocationBuilder() OVERRIDE {
     return &location_builder_;
   }
 
-  virtual HGraphVisitor* GetInstructionVisitor() OVERRIDE {
+  HGraphVisitor* GetInstructionVisitor() OVERRIDE {
     return &instruction_visitor_;
   }
 
-  virtual X86Assembler* GetAssembler() OVERRIDE {
+  X86Assembler* GetAssembler() OVERRIDE {
     return &assembler_;
   }
 
-  virtual size_t GetNumberOfRegisters() const OVERRIDE;
-  virtual void SetupBlockedRegisters(bool* blocked_registers) const OVERRIDE;
-  virtual ManagedRegister AllocateFreeRegister(
-      Primitive::Type type, bool* blocked_registers) const OVERRIDE;
-
-  virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
-
-  virtual size_t GetNumberOfCoreRegisters() const OVERRIDE {
-    return kNumberOfCpuRegisters;
+  uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+    return GetLabelOf(block)->Position();
   }
 
-  virtual size_t GetNumberOfFloatingPointRegisters() const OVERRIDE {
-    return kNumberOfXmmRegisters;
-  }
+  void SetupBlockedRegisters() const OVERRIDE;
 
-  virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
-  virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
+  Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
+
+  Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
+
+  void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
+  void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
+
+  // Blocks all register pairs made out of blocked core registers.
+  void UpdateBlockedPairRegisters() const;
 
   ParallelMoveResolverX86* GetMoveResolver() {
     return &move_resolver_;
   }
 
-  virtual InstructionSet GetInstructionSet() const OVERRIDE {
+  InstructionSet GetInstructionSet() const OVERRIDE {
     return InstructionSet::kX86;
   }
 
@@ -185,7 +197,19 @@
   // Emit a write barrier.
   void MarkGCCard(Register temp, Register card, Register object, Register value);
 
+  void LoadCurrentMethod(Register reg);
+
+  Label* GetLabelOf(HBasicBlock* block) const {
+    return block_labels_.GetRawStorage() + block->GetBlockId();
+  }
+
+  void Initialize() OVERRIDE {
+    block_labels_.SetSize(GetGraph()->GetBlocks().Size());
+  }
+
  private:
+  // Labels for each block that will be compiled.
+  GrowableArray<Label> block_labels_;
   LocationsBuilderX86 location_builder_;
   InstructionCodeGeneratorX86 instruction_visitor_;
   ParallelMoveResolverX86 move_resolver_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index e1807dc..e09b6ca 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -18,8 +18,9 @@
 
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/accounting/card_table.h"
-#include "mirror/array.h"
+#include "mirror/array-inl.h"
 #include "mirror/art_method.h"
+#include "mirror/class.h"
 #include "mirror/object_reference.h"
 #include "thread.h"
 #include "utils/assembler.h"
@@ -29,13 +30,9 @@
 
 namespace art {
 
-x86_64::X86_64ManagedRegister Location::AsX86_64() const {
-  return reg().AsX86_64();
-}
-
 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;
@@ -43,19 +40,19 @@
 static constexpr int kNumberOfPushedRegistersAtEntry = 1;
 static constexpr int kCurrentMethodStackOffset = 0;
 
-static Location X86_64CpuLocation(Register reg) {
-  return Location::RegisterLocation(X86_64ManagedRegister::FromCpuRegister(reg));
-}
-
 static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX };
 static constexpr size_t kRuntimeParameterCoreRegistersLength =
     arraysize(kRuntimeParameterCoreRegisters);
+static constexpr FloatRegister kRuntimeParameterFpuRegisters[] = { };
+static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
 
-class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
+class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatRegister> {
  public:
   InvokeRuntimeCallingConvention()
       : CallingConvention(kRuntimeParameterCoreRegisters,
-                          kRuntimeParameterCoreRegistersLength) {}
+                          kRuntimeParameterCoreRegistersLength,
+                          kRuntimeParameterFpuRegisters,
+                          kRuntimeParameterFpuRegistersLength) {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
@@ -63,23 +60,68 @@
 
 #define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
 
-class NullCheckSlowPathX86_64 : public SlowPathCode {
+class SlowPathCodeX86_64 : public SlowPathCode {
  public:
-  explicit NullCheckSlowPathX86_64(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+  SlowPathCodeX86_64() : entry_label_(), exit_label_() {}
+
+  Label* GetEntryLabel() { return &entry_label_; }
+  Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  Label entry_label_;
+  Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86_64);
+};
+
+class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+  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);
 };
 
-class StackOverflowCheckSlowPathX86_64 : public SlowPathCode {
+class DivZeroCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+  explicit DivZeroCheckSlowPathX86_64(HDivZeroCheck* instruction) : instruction_(instruction) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    __ Bind(GetEntryLabel());
+    __ gs()->call(
+        Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowDivZero), true));
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+  }
+
+ private:
+  HDivZeroCheck* const instruction_;
+  DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86_64);
+};
+
+class DivMinusOneSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+  explicit DivMinusOneSlowPathX86_64(Register reg) : reg_(reg) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    __ Bind(GetEntryLabel());
+    __ negl(CpuRegister(reg_));
+    __ jmp(GetExitLabel());
+  }
+
+ private:
+  Register reg_;
+  DISALLOW_COPY_AND_ASSIGN(DivMinusOneSlowPathX86_64);
+};
+
+class StackOverflowCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
  public:
   StackOverflowCheckSlowPathX86_64() {}
 
@@ -95,32 +137,195 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
 };
 
-class BoundsCheckSlowPathX86_64 : public SlowPathCode {
+class SuspendCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
  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) {}
+  explicit SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor)
+      : instruction_(instruction), successor_(successor) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen);
+    CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
-    InvokeRuntimeCallingConvention calling_convention;
-    x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
-    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->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(x64_codegen->GetLabelOf(successor_));
+    }
+  }
+
+  Label* GetReturnLabel() {
+    DCHECK(successor_ == nullptr);
+    return &return_label_;
   }
 
  private:
-  const uint32_t dex_pc_;
+  HSuspendCheck* const instruction_;
+  HBasicBlock* const successor_;
+  Label return_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86_64);
+};
+
+class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+  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 = down_cast<CodeGeneratorX86_64*>(codegen);
+    __ Bind(GetEntryLabel());
+    InvokeRuntimeCallingConvention calling_convention;
+    x64_codegen->Move(
+        Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
+    x64_codegen->Move(
+        Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_);
+    __ gs()->call(Address::Absolute(
+        QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true));
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+  }
+
+ private:
+  HBoundsCheck* const instruction_;
   const Location index_location_;
   const Location length_location_;
 
   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64);
 };
 
+class LoadClassSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+  LoadClassSlowPathX86_64(HLoadClass* cls,
+                          HInstruction* at,
+                          uint32_t dex_pc,
+                          bool do_clinit)
+      : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+    DCHECK(at->IsLoadClass() || at->IsClinitCheck());
+  }
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = at_->GetLocations();
+    CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
+    __ Bind(GetEntryLabel());
+
+    codegen->SaveLiveRegisters(locations);
+
+    InvokeRuntimeCallingConvention calling_convention;
+    __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(cls_->GetTypeIndex()));
+    x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+    __ gs()->call(Address::Absolute((do_clinit_
+          ? QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pInitializeStaticStorage)
+          : QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pInitializeType)) , true));
+    codegen->RecordPcInfo(at_, dex_pc_);
+
+    Location out = locations->Out();
+    // Move the class to the desired location.
+    if (out.IsValid()) {
+      DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+      x64_codegen->Move(out, Location::RegisterLocation(RAX));
+    }
+
+    codegen->RestoreLiveRegisters(locations);
+    __ jmp(GetExitLabel());
+  }
+
+ private:
+  // The class this slow path will load.
+  HLoadClass* const cls_;
+
+  // The instruction where this slow path is happening.
+  // (Might be the load class or an initialization check).
+  HInstruction* const at_;
+
+  // The dex PC of `at_`.
+  const uint32_t dex_pc_;
+
+  // Whether to initialize the class.
+  const bool do_clinit_;
+
+  DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathX86_64);
+};
+
+class LoadStringSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+  explicit LoadStringSlowPathX86_64(HLoadString* instruction) : instruction_(instruction) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = instruction_->GetLocations();
+    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+
+    CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(locations);
+
+    InvokeRuntimeCallingConvention calling_convention;
+    x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(0)));
+    __ movl(CpuRegister(calling_convention.GetRegisterAt(1)),
+            Immediate(instruction_->GetStringIndex()));
+    __ gs()->call(Address::Absolute(
+        QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pResolveString), true));
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+    x64_codegen->Move(locations->Out(), Location::RegisterLocation(RAX));
+    codegen->RestoreLiveRegisters(locations);
+    __ jmp(GetExitLabel());
+  }
+
+ private:
+  HLoadString* const instruction_;
+
+  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86_64);
+};
+
+class TypeCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+  TypeCheckSlowPathX86_64(HTypeCheck* instruction, Location object_class)
+      : instruction_(instruction),
+        object_class_(object_class) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = instruction_->GetLocations();
+    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+
+    CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(locations);
+
+    // We're moving two locations to locations that could overlap, so we need a parallel
+    // move resolver.
+    InvokeRuntimeCallingConvention calling_convention;
+    MoveOperands move1(locations->InAt(1),
+                       Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+                       nullptr);
+    MoveOperands move2(object_class_,
+                       Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
+                       nullptr);
+    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
+    parallel_move.AddMove(&move1);
+    parallel_move.AddMove(&move2);
+    x64_codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
+
+    __ gs()->call(
+        Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pInstanceofNonTrivial), true));
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+    x64_codegen->Move(locations->Out(), Location::RegisterLocation(RAX));
+
+    codegen->RestoreLiveRegisters(locations);
+    __ jmp(GetExitLabel());
+  }
+
+ private:
+  HTypeCheck* const instruction_;
+  const Location object_class_;
+
+  DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathX86_64);
+};
+
 #undef __
 #define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())->
 
@@ -146,8 +351,29 @@
   stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg));
 }
 
+size_t CodeGeneratorX86_64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
+  __ movq(Address(CpuRegister(RSP), stack_index), CpuRegister(reg_id));
+  return kX86_64WordSize;
+}
+
+size_t CodeGeneratorX86_64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
+  __ movq(CpuRegister(reg_id), Address(CpuRegister(RSP), stack_index));
+  return kX86_64WordSize;
+}
+
+size_t CodeGeneratorX86_64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+  __ movsd(Address(CpuRegister(RSP), stack_index), XmmRegister(reg_id));
+  return kX86_64WordSize;
+}
+
+size_t CodeGeneratorX86_64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+  __ movsd(XmmRegister(reg_id), Address(CpuRegister(RSP), stack_index));
+  return kX86_64WordSize;
+}
+
 CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph)
-      : CodeGenerator(graph, kNumberOfRegIds),
+      : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfFloatRegisters, 0),
+        block_labels_(graph->GetArena(), 0),
         location_builder_(graph, this),
         instruction_visitor_(graph, this),
         move_resolver_(graph->GetArena(), this) {}
@@ -162,8 +388,7 @@
         assembler_(codegen->GetAssembler()),
         codegen_(codegen) {}
 
-ManagedRegister CodeGeneratorX86_64::AllocateFreeRegister(Primitive::Type type,
-                                                          bool* blocked_registers) const {
+Location CodeGeneratorX86_64::AllocateFreeRegister(Primitive::Type type) const {
   switch (type) {
     case Primitive::kPrimLong:
     case Primitive::kPrimByte:
@@ -172,35 +397,42 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      size_t reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCpuRegisters);
-      return X86_64ManagedRegister::FromCpuRegister(static_cast<Register>(reg));
+      size_t reg = FindFreeEntry(blocked_core_registers_, kNumberOfCpuRegisters);
+      return Location::RegisterLocation(reg);
     }
 
     case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << type;
+    case Primitive::kPrimDouble: {
+      size_t reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfFloatRegisters);
+      return Location::FpuRegisterLocation(reg);
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << type;
   }
 
-  return ManagedRegister::NoRegister();
+  return Location();
 }
 
-void CodeGeneratorX86_64::SetupBlockedRegisters(bool* blocked_registers) const {
+void CodeGeneratorX86_64::SetupBlockedRegisters() const {
   // Stack register is always reserved.
-  blocked_registers[RSP] = true;
+  blocked_core_registers_[RSP] = true;
 
   // Block the register used as TMP.
-  blocked_registers[TMP] = true;
+  blocked_core_registers_[TMP] = true;
 
   // TODO: We currently don't use Quick's callee saved registers.
-  blocked_registers[RBX] = true;
-  blocked_registers[RBP] = true;
-  blocked_registers[R12] = true;
-  blocked_registers[R13] = true;
-  blocked_registers[R14] = true;
-  blocked_registers[R15] = true;
+  blocked_core_registers_[RBX] = true;
+  blocked_core_registers_[RBP] = true;
+  blocked_core_registers_[R12] = true;
+  blocked_core_registers_[R13] = true;
+  blocked_core_registers_[R14] = true;
+  blocked_core_registers_[R15] = true;
+
+  blocked_fpu_registers_[XMM12] = true;
+  blocked_fpu_registers_[XMM13] = true;
+  blocked_fpu_registers_[XMM14] = true;
+  blocked_fpu_registers_[XMM15] = true;
 }
 
 void CodeGeneratorX86_64::GenerateFrameEntry() {
@@ -208,25 +440,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) {
+    SlowPathCodeX86_64* 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));
@@ -237,27 +470,25 @@
           Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
 }
 
-void CodeGeneratorX86_64::Bind(Label* label) {
-  __ Bind(label);
+void CodeGeneratorX86_64::Bind(HBasicBlock* block) {
+  __ Bind(GetLabelOf(block));
 }
 
-void InstructionCodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) {
+void CodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) {
   __ movl(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset));
 }
 
 Location CodeGeneratorX86_64::GetStackLocation(HLoadLocal* load) const {
   switch (load->GetType()) {
     case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
       return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
       break;
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
-      return Location::StackSlot(GetStackSlot(load->GetLocal()));
-
     case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented type " << load->GetType();
+      return Location::StackSlot(GetStackSlot(load->GetLocal()));
 
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -277,16 +508,37 @@
   }
   if (destination.IsRegister()) {
     if (source.IsRegister()) {
-      __ movq(destination.AsX86_64().AsCpuRegister(), source.AsX86_64().AsCpuRegister());
+      __ movq(destination.As<CpuRegister>(), source.As<CpuRegister>());
+    } else if (source.IsFpuRegister()) {
+      __ movd(destination.As<CpuRegister>(), source.As<XmmRegister>());
     } else if (source.IsStackSlot()) {
-      __ movl(destination.AsX86_64().AsCpuRegister(), Address(CpuRegister(RSP), source.GetStackIndex()));
+      __ movl(destination.As<CpuRegister>(),
+              Address(CpuRegister(RSP), source.GetStackIndex()));
     } else {
       DCHECK(source.IsDoubleStackSlot());
-      __ movq(destination.AsX86_64().AsCpuRegister(), Address(CpuRegister(RSP), source.GetStackIndex()));
+      __ movq(destination.As<CpuRegister>(),
+              Address(CpuRegister(RSP), source.GetStackIndex()));
+    }
+  } else if (destination.IsFpuRegister()) {
+    if (source.IsRegister()) {
+      __ movd(destination.As<XmmRegister>(), source.As<CpuRegister>());
+    } else if (source.IsFpuRegister()) {
+      __ movaps(destination.As<XmmRegister>(), source.As<XmmRegister>());
+    } else if (source.IsStackSlot()) {
+      __ movss(destination.As<XmmRegister>(),
+              Address(CpuRegister(RSP), source.GetStackIndex()));
+    } else {
+      DCHECK(source.IsDoubleStackSlot());
+      __ movsd(destination.As<XmmRegister>(),
+               Address(CpuRegister(RSP), source.GetStackIndex()));
     }
   } else if (destination.IsStackSlot()) {
     if (source.IsRegister()) {
-      __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), source.AsX86_64().AsCpuRegister());
+      __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()),
+              source.As<CpuRegister>());
+    } else if (source.IsFpuRegister()) {
+      __ movss(Address(CpuRegister(RSP), destination.GetStackIndex()),
+               source.As<XmmRegister>());
     } else {
       DCHECK(source.IsStackSlot());
       __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex()));
@@ -295,7 +547,11 @@
   } else {
     DCHECK(destination.IsDoubleStackSlot());
     if (source.IsRegister()) {
-      __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), source.AsX86_64().AsCpuRegister());
+      __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()),
+              source.As<CpuRegister>());
+    } else if (source.IsFpuRegister()) {
+      __ movsd(Address(CpuRegister(RSP), destination.GetStackIndex()),
+               source.As<XmmRegister>());
     } else {
       DCHECK(source.IsDoubleStackSlot());
       __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex()));
@@ -307,22 +563,28 @@
 void CodeGeneratorX86_64::Move(HInstruction* instruction,
                                Location location,
                                HInstruction* move_for) {
-  if (instruction->AsIntConstant() != nullptr) {
+  if (instruction->IsIntConstant()) {
     Immediate imm(instruction->AsIntConstant()->GetValue());
     if (location.IsRegister()) {
-      __ movl(location.AsX86_64().AsCpuRegister(), imm);
-    } else {
+      __ movl(location.As<CpuRegister>(), imm);
+    } else if (location.IsStackSlot()) {
       __ movl(Address(CpuRegister(RSP), location.GetStackIndex()), imm);
+    } else {
+      DCHECK(location.IsConstant());
+      DCHECK_EQ(location.GetConstant(), instruction);
     }
-  } else if (instruction->AsLongConstant() != nullptr) {
+  } else if (instruction->IsLongConstant()) {
     int64_t value = instruction->AsLongConstant()->GetValue();
     if (location.IsRegister()) {
-      __ movq(location.AsX86_64().AsCpuRegister(), Immediate(value));
-    } else {
+      __ movq(location.As<CpuRegister>(), Immediate(value));
+    } else if (location.IsDoubleStackSlot()) {
       __ movq(CpuRegister(TMP), Immediate(value));
       __ movq(Address(CpuRegister(RSP), location.GetStackIndex()), CpuRegister(TMP));
+    } else {
+      DCHECK(location.IsConstant());
+      DCHECK_EQ(location.GetConstant(), instruction);
     }
-  } else if (instruction->AsLoadLocal() != nullptr) {
+  } else if (instruction->IsLoadLocal()) {
     switch (instruction->GetType()) {
       case Primitive::kPrimBoolean:
       case Primitive::kPrimByte:
@@ -330,16 +592,21 @@
       case Primitive::kPrimShort:
       case Primitive::kPrimInt:
       case Primitive::kPrimNot:
+      case Primitive::kPrimFloat:
         Move(location, Location::StackSlot(GetStackSlot(instruction->AsLoadLocal()->GetLocal())));
         break;
 
       case Primitive::kPrimLong:
+      case Primitive::kPrimDouble:
         Move(location, Location::DoubleStackSlot(GetStackSlot(instruction->AsLoadLocal()->GetLocal())));
         break;
 
       default:
-        LOG(FATAL) << "Unimplemented local type " << instruction->GetType();
+        LOG(FATAL) << "Unexpected local type " << instruction->GetType();
     }
+  } else if (instruction->IsTemporary()) {
+    Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
+    Move(location, temp_location);
   } else {
     DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
     switch (instruction->GetType()) {
@@ -350,11 +617,13 @@
       case Primitive::kPrimInt:
       case Primitive::kPrimNot:
       case Primitive::kPrimLong:
+      case Primitive::kPrimFloat:
+      case Primitive::kPrimDouble:
         Move(location, instruction->GetLocations()->Out());
         break;
 
       default:
-        LOG(FATAL) << "Unimplemented type " << instruction->GetType();
+        LOG(FATAL) << "Unexpected type " << instruction->GetType();
     }
   }
 }
@@ -365,9 +634,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));
   }
 }
@@ -377,6 +659,7 @@
 }
 
 void InstructionCodeGeneratorX86_64::VisitExit(HExit* exit) {
+  UNUSED(exit);
   if (kIsDebugBuild) {
     __ Comment("Unreachable");
     __ int3();
@@ -384,44 +667,69 @@
 }
 
 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()) {
+  if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
     locations->SetInAt(0, Location::Any());
   }
-  if_instr->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) {
   HInstruction* cond = if_instr->InputAt(0);
-  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));
+  if (cond->IsIntConstant()) {
+    // Constant condition, statically compared against 1.
+    int32_t cond_value = cond->AsIntConstant()->GetValue();
+    if (cond_value == 1) {
+      if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                     if_instr->IfTrueSuccessor())) {
+        __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      }
+      return;
     } else {
-      __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), Immediate(0));
+      DCHECK_EQ(cond_value, 0);
     }
-    __ j(kEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
   } else {
-    Location lhs = condition->GetLocations()->InAt(0);
-    Location rhs = condition->GetLocations()->InAt(1);
-    if (rhs.IsRegister()) {
-      __ cmpl(lhs.AsX86_64().AsCpuRegister(), rhs.AsX86_64().AsCpuRegister());
-    } else if (rhs.IsConstant()) {
-      __ cmpl(lhs.AsX86_64().AsCpuRegister(),
-              Immediate(rhs.GetConstant()->AsIntConstant()->GetValue()));
+    bool materialized =
+        !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
+    // 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.
+    bool eflags_set = cond->IsCondition()
+        && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr);
+    if (materialized) {
+      if (!eflags_set) {
+        // Materialized condition, compare against 0.
+        Location lhs = if_instr->GetLocations()->InAt(0);
+        if (lhs.IsRegister()) {
+          __ cmpl(lhs.As<CpuRegister>(), Immediate(0));
+        } else {
+          __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()),
+                  Immediate(0));
+        }
+        __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      } else {
+        __ j(X86_64Condition(cond->AsCondition()->GetCondition()),
+             codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+      }
     } else {
-      __ cmpl(lhs.AsX86_64().AsCpuRegister(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
+      Location lhs = cond->GetLocations()->InAt(0);
+      Location rhs = cond->GetLocations()->InAt(1);
+      if (rhs.IsRegister()) {
+        __ cmpl(lhs.As<CpuRegister>(), rhs.As<CpuRegister>());
+      } else if (rhs.IsConstant()) {
+        __ cmpl(lhs.As<CpuRegister>(),
+                Immediate(rhs.GetConstant()->AsIntConstant()->GetValue()));
+      } else {
+        __ cmpl(lhs.As<CpuRegister>(),
+                Address(CpuRegister(RSP), rhs.GetStackIndex()));
+      }
+      __ j(X86_64Condition(cond->AsCondition()->GetCondition()),
+           codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
     }
-    __ j(X86_64Condition(condition->GetCondition()),
-         codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
   }
-  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
+  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(),
+                                 if_instr->IfFalseSuccessor())) {
     __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
   }
 }
@@ -440,10 +748,12 @@
 
 void InstructionCodeGeneratorX86_64::VisitLoadLocal(HLoadLocal* load) {
   // Nothing to do, this is driven by the code generator.
+  UNUSED(load);
 }
 
 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:
@@ -451,47 +761,51 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
+    case Primitive::kPrimFloat:
       locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
       break;
 
     case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
       locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
       break;
 
     default:
-      LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
+      LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType();
   }
-  store->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) {
+  UNUSED(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().As<CpuRegister>();
+    // 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());
+      __ cmpl(locations->InAt(0).As<CpuRegister>(),
+              locations->InAt(1).As<CpuRegister>());
     } else if (locations->InAt(1).IsConstant()) {
-      __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
+      __ cmpl(locations->InAt(0).As<CpuRegister>(),
               Immediate(locations->InAt(1).GetConstant()->AsIntConstant()->GetValue()));
     } else {
-      __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
+      __ cmpl(locations->InAt(0).As<CpuRegister>(),
               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 +858,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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) {
@@ -556,42 +870,69 @@
   LocationSummary* locations = compare->GetLocations();
   switch (compare->InputAt(0)->GetType()) {
     case Primitive::kPrimLong:
-      __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
-              locations->InAt(1).AsX86_64().AsCpuRegister());
+      __ cmpq(locations->InAt(0).As<CpuRegister>(),
+              locations->InAt(1).As<CpuRegister>());
       break;
     default:
       LOG(FATAL) << "Unimplemented compare type " << compare->InputAt(0)->GetType();
   }
 
-  __ movl(locations->Out().AsX86_64().AsCpuRegister(), Immediate(0));
+  CpuRegister output = locations->Out().As<CpuRegister>();
+  __ movl(output, Immediate(0));
   __ j(kEqual, &done);
   __ j(kGreater, &greater);
 
-  __ movl(locations->Out().AsX86_64().AsCpuRegister(), Immediate(-1));
+  __ movl(output, Immediate(-1));
   __ jmp(&done);
 
   __ Bind(&greater);
-  __ movl(locations->Out().AsX86_64().AsCpuRegister(), Immediate(1));
+  __ movl(output, Immediate(1));
 
   __ Bind(&done);
 }
 
 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) {
+  // Will be generated at use site.
+  UNUSED(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) {
+  // Will be generated at use site.
+  UNUSED(constant);
+}
+
+void LocationsBuilderX86_64::VisitFloatConstant(HFloatConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorX86_64::VisitFloatConstant(HFloatConstant* constant) {
+  // Will be generated at use site.
+  UNUSED(constant);
+}
+
+void LocationsBuilderX86_64::VisitDoubleConstant(HDoubleConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorX86_64::VisitDoubleConstant(HDoubleConstant* constant) {
+  // Will be generated at use site.
+  UNUSED(constant);
 }
 
 void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) {
@@ -599,12 +940,14 @@
 }
 
 void InstructionCodeGeneratorX86_64::VisitReturnVoid(HReturnVoid* ret) {
+  UNUSED(ret);
   codegen_->GenerateFrameExit();
   __ ret();
 }
 
 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:
@@ -613,13 +956,18 @@
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
     case Primitive::kPrimLong:
-      locations->SetInAt(0, X86_64CpuLocation(RAX));
+      locations->SetInAt(0, Location::RegisterLocation(RAX));
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0,
+          Location::FpuRegisterLocation(XMM0));
       break;
 
     default:
-      LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
+      LOG(FATAL) << "Unexpected return type " << ret->InputAt(0)->GetType();
   }
-  ret->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitReturn(HReturn* ret) {
@@ -632,11 +980,17 @@
       case Primitive::kPrimInt:
       case Primitive::kPrimNot:
       case Primitive::kPrimLong:
-        DCHECK_EQ(ret->GetLocations()->InAt(0).AsX86_64().AsCpuRegister().AsRegister(), RAX);
+        DCHECK_EQ(ret->GetLocations()->InAt(0).As<CpuRegister>().AsRegister(), RAX);
+        break;
+
+      case Primitive::kPrimFloat:
+      case Primitive::kPrimDouble:
+        DCHECK_EQ(ret->GetLocations()->InAt(0).As<XmmRegister>().AsFloatRegister(),
+                  XMM0);
         break;
 
       default:
-        LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
+        LOG(FATAL) << "Unexpected return type " << ret->InputAt(0)->GetType();
     }
   }
   codegen_->GenerateFrameExit();
@@ -654,7 +1008,7 @@
       uint32_t index = gp_index_++;
       stack_index_++;
       if (index < calling_convention.GetNumberOfRegisters()) {
-        return X86_64CpuLocation(calling_convention.GetRegisterAt(index));
+        return Location::RegisterLocation(calling_convention.GetRegisterAt(index));
       } else {
         return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 1));
       }
@@ -665,17 +1019,32 @@
       stack_index_ += 2;
       if (index < calling_convention.GetNumberOfRegisters()) {
         gp_index_ += 1;
-        return X86_64CpuLocation(calling_convention.GetRegisterAt(index));
+        return Location::RegisterLocation(calling_convention.GetRegisterAt(index));
       } else {
         gp_index_ += 2;
         return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 2));
       }
     }
 
-    case Primitive::kPrimDouble:
-    case Primitive::kPrimFloat:
-      LOG(FATAL) << "Unimplemented parameter type " << type;
-      break;
+    case Primitive::kPrimFloat: {
+      uint32_t index = fp_index_++;
+      stack_index_++;
+      if (index < calling_convention.GetNumberOfFpuRegisters()) {
+        return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(index));
+      } else {
+        return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 1));
+      }
+    }
+
+    case Primitive::kPrimDouble: {
+      uint32_t index = fp_index_++;
+      stack_index_ += 2;
+      if (index < calling_convention.GetNumberOfFpuRegisters()) {
+        return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(index));
+      } else {
+        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 2));
+      }
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unexpected parameter type " << type;
@@ -685,12 +1054,38 @@
 }
 
 void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
-  locations->AddTemp(X86_64CpuLocation(RDI));
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
+  CpuRegister temp = invoke->GetLocations()->GetTemp(0).As<CpuRegister>();
+  // 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;
+  codegen_->LoadCurrentMethod(temp);
+  // temp = temp->dex_cache_resolved_methods_;
+  __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
+  // temp = temp[index_in_cache]
+  __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
+  // (temp + offset_of_quick_compiled_code)()
+  __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+
+  DCHECK(!codegen_->IsLeafMethod());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
+  locations->AddTemp(Location::RegisterLocation(RDI));
 
   InvokeDexCallingConventionVisitor calling_convention_visitor;
-  for (size_t i = 0; i < invoke->InputCount(); ++i) {
+  for (size_t i = 0; i < invoke->InputCount(); i++) {
     HInstruction* input = invoke->InputAt(i);
     locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
   }
@@ -703,7 +1098,7 @@
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
     case Primitive::kPrimLong:
-      locations->SetOut(X86_64CpuLocation(RAX));
+      locations->SetOut(Location::RegisterLocation(RAX));
       break;
 
     case Primitive::kPrimVoid:
@@ -711,41 +1106,244 @@
 
     case Primitive::kPrimDouble:
     case Primitive::kPrimFloat:
-      LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
+      locations->SetOut(Location::FpuRegisterLocation(XMM0));
       break;
   }
-
-  invoke->SetLocations(locations);
 }
 
-void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
-  CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsX86_64().AsCpuRegister();
-  uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
-  size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).SizeValue() +
-      invoke->GetIndexInDexCache() * heap_reference_size;
+void LocationsBuilderX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  HandleInvoke(invoke);
+}
 
-  // 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().SizeValue()));
-  // temp = temp[index_in_cache]
-  __ movl(temp, Address(temp, index_in_cache));
-  // (temp + offset_of_quick_compiled_code)()
+void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  CpuRegister temp = invoke->GetLocations()->GetTemp(0).As<CpuRegister>();
+  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()) {
+    __ movl(temp, Address(CpuRegister(RSP), receiver.GetStackIndex()));
+    __ movl(temp, Address(temp, class_offset));
+  } else {
+    __ movl(temp, Address(receiver.As<CpuRegister>(), 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->GetDexPc());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderX86_64::VisitInvokeInterface(HInvokeInterface* invoke) {
+  HandleInvoke(invoke);
+  // Add the hidden argument.
+  invoke->GetLocations()->AddTemp(Location::RegisterLocation(RAX));
+}
+
+void InstructionCodeGeneratorX86_64::VisitInvokeInterface(HInvokeInterface* invoke) {
+  // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
+  CpuRegister temp = invoke->GetLocations()->GetTemp(0).As<CpuRegister>();
+  uint32_t method_offset = mirror::Class::EmbeddedImTableOffset().Uint32Value() +
+          (invoke->GetImtIndex() % mirror::Class::kImtSize) * sizeof(mirror::Class::ImTableEntry);
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  size_t class_offset = mirror::Object::ClassOffset().SizeValue();
+
+  // Set the hidden argument.
+  __ movq(invoke->GetLocations()->GetTemp(1).As<CpuRegister>(),
+          Immediate(invoke->GetDexMethodIndex()));
+
+  // temp = object->GetClass();
+  if (receiver.IsStackSlot()) {
+    __ movl(temp, Address(CpuRegister(RSP), receiver.GetStackIndex()));
+    __ movl(temp, Address(temp, class_offset));
+  } else {
+    __ movl(temp, Address(receiver.As<CpuRegister>(), class_offset));
+  }
+  // temp = temp->GetImtEntryAt(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::VisitNeg(HNeg* neg) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
+  switch (neg->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      // Output overlaps as we need a fresh (zero-initialized)
+      // register to perform subtraction from zero.
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitNeg(HNeg* neg) {
+  LocationSummary* locations = neg->GetLocations();
+  Location out = locations->Out();
+  Location in = locations->InAt(0);
+  switch (neg->GetResultType()) {
+    case Primitive::kPrimInt:
+      DCHECK(in.IsRegister());
+      DCHECK(in.Equals(out));
+      __ negl(out.As<CpuRegister>());
+      break;
+
+    case Primitive::kPrimLong:
+      DCHECK(in.IsRegister());
+      DCHECK(in.Equals(out));
+      __ negq(out.As<CpuRegister>());
+      break;
+
+    case Primitive::kPrimFloat:
+      DCHECK(in.IsFpuRegister());
+      DCHECK(out.IsFpuRegister());
+      DCHECK(!in.Equals(out));
+      // TODO: Instead of computing negation as a subtraction from
+      // zero, implement it with an exclusive or with value 0x80000000
+      // (mask for bit 31, representing the sign of a single-precision
+      // floating-point number), fetched from a constant pool:
+      //
+      //   xorps out, [RIP:...] // value at RIP is 0x80 00 00 00
+
+      // out = 0
+      __ xorps(out.As<XmmRegister>(), out.As<XmmRegister>());
+      // out = out - in
+      __ subss(out.As<XmmRegister>(), in.As<XmmRegister>());
+      break;
+
+    case Primitive::kPrimDouble:
+      DCHECK(in.IsFpuRegister());
+      DCHECK(out.IsFpuRegister());
+      DCHECK(!in.Equals(out));
+      // TODO: Instead of computing negation as a subtraction from
+      // zero, implement it with an exclusive or with value
+      // 0x8000000000000000 (mask for bit 63, representing the sign of
+      // a double-precision floating-point number), fetched from a
+      // constant pool:
+      //
+      //   xorpd out, [RIP:...] // value at RIP is 0x80 00 00 00 00 00 00 00
+
+      // out = 0
+      __ xorpd(out.As<XmmRegister>(), out.As<XmmRegister>());
+      // out = out - in
+      __ subsd(out.As<XmmRegister>(), in.As<XmmRegister>());
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
+  }
+}
+
+void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall);
+  Primitive::Type result_type = conversion->GetResultType();
+  Primitive::Type input_type = conversion->GetInputType();
+  switch (result_type) {
+    case Primitive::kPrimLong:
+      switch (input_type) {
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // int-to-long conversion.
+          // TODO: We would benefit from a (to-be-implemented)
+          // Location::RegisterOrStackSlot requirement for this input.
+          locations->SetInAt(0, Location::RequiresRegister());
+          locations->SetOut(Location::RequiresRegister());
+          break;
+
+        case Primitive::kPrimFloat:
+        case Primitive::kPrimDouble:
+          LOG(FATAL) << "Type conversion from " << input_type << " to "
+                     << result_type << " not yet implemented";
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Type conversion from " << input_type
+                 << " to " << result_type << " not yet implemented";
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected type conversion from " << input_type
+                 << " to " << result_type;
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conversion) {
+  LocationSummary* locations = conversion->GetLocations();
+  Location out = locations->Out();
+  Location in = locations->InAt(0);
+  Primitive::Type result_type = conversion->GetResultType();
+  Primitive::Type input_type = conversion->GetInputType();
+  switch (result_type) {
+    case Primitive::kPrimLong:
+      switch (input_type) {
+        DCHECK(out.IsRegister());
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // int-to-long conversion.
+          DCHECK(in.IsRegister());
+          __ movsxd(out.As<CpuRegister>(), in.As<CpuRegister>());
+          break;
+
+        case Primitive::kPrimFloat:
+        case Primitive::kPrimDouble:
+          LOG(FATAL) << "Type conversion from " << input_type << " to "
+                     << result_type << " not yet implemented";
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Type conversion from " << input_type
+                 << " to " << result_type << " not yet implemented";
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected type conversion from " << input_type
+                 << " to " << result_type;
+  }
 }
 
 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());
@@ -753,6 +1351,7 @@
       locations->SetOut(Location::SameAsFirstInput());
       break;
     }
+
     case Primitive::kPrimLong: {
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RequiresRegister());
@@ -760,58 +1359,61 @@
       break;
     }
 
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+    case Primitive::kPrimDouble:
+    case Primitive::kPrimFloat: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
       break;
+    }
 
     default:
-      LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
+      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
   }
-  add->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) {
   LocationSummary* locations = add->GetLocations();
-  DCHECK_EQ(locations->InAt(0).AsX86_64().AsCpuRegister().AsRegister(),
-            locations->Out().AsX86_64().AsCpuRegister().AsRegister());
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  DCHECK(first.Equals(locations->Out()));
+
   switch (add->GetResultType()) {
     case Primitive::kPrimInt: {
-      if (locations->InAt(1).IsRegister()) {
-        __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(),
-                locations->InAt(1).AsX86_64().AsCpuRegister());
-      } else if (locations->InAt(1).IsConstant()) {
-        HConstant* instruction = locations->InAt(1).GetConstant();
-        Immediate imm(instruction->AsIntConstant()->GetValue());
-        __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(), imm);
+      if (second.IsRegister()) {
+        __ addl(first.As<CpuRegister>(), second.As<CpuRegister>());
+      } else if (second.IsConstant()) {
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+        __ addl(first.As<CpuRegister>(), imm);
       } else {
-        __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(),
-                Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex()));
+        __ addl(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex()));
       }
       break;
     }
+
     case Primitive::kPrimLong: {
-      __ addq(locations->InAt(0).AsX86_64().AsCpuRegister(),
-              locations->InAt(1).AsX86_64().AsCpuRegister());
+      __ addq(first.As<CpuRegister>(), second.As<CpuRegister>());
       break;
     }
 
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+    case Primitive::kPrimFloat: {
+      __ addss(first.As<XmmRegister>(), second.As<XmmRegister>());
       break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ addsd(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
 
     default:
-      LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
+      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
   }
 }
 
 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());
@@ -825,78 +1427,281 @@
       locations->SetOut(Location::SameAsFirstInput());
       break;
     }
-
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
       break;
-
+    }
     default:
-      LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
+      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
   }
-  sub->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) {
   LocationSummary* locations = sub->GetLocations();
-  DCHECK_EQ(locations->InAt(0).AsX86_64().AsCpuRegister().AsRegister(),
-            locations->Out().AsX86_64().AsCpuRegister().AsRegister());
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  DCHECK(first.Equals(locations->Out()));
   switch (sub->GetResultType()) {
     case Primitive::kPrimInt: {
-      if (locations->InAt(1).IsRegister()) {
-        __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(),
-                locations->InAt(1).AsX86_64().AsCpuRegister());
-      } else if (locations->InAt(1).IsConstant()) {
-        HConstant* instruction = locations->InAt(1).GetConstant();
-        Immediate imm(instruction->AsIntConstant()->GetValue());
-        __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(), imm);
+      if (second.IsRegister()) {
+        __ subl(first.As<CpuRegister>(), second.As<CpuRegister>());
+      } else if (second.IsConstant()) {
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+        __ subl(first.As<CpuRegister>(), imm);
       } else {
-        __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(),
-                Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex()));
+        __ subl(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex()));
       }
       break;
     }
     case Primitive::kPrimLong: {
-      __ subq(locations->InAt(0).AsX86_64().AsCpuRegister(),
-              locations->InAt(1).AsX86_64().AsCpuRegister());
+      __ subq(first.As<CpuRegister>(), second.As<CpuRegister>());
       break;
     }
 
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
+    case Primitive::kPrimFloat: {
+      __ subss(first.As<XmmRegister>(), second.As<XmmRegister>());
       break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ subsd(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
 
     default:
-      LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
+      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
   }
 }
 
+void LocationsBuilderX86_64::VisitMul(HMul* mul) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::Any());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    }
+    case Primitive::kPrimLong: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    }
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) {
+  LocationSummary* locations = mul->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  DCHECK(first.Equals(locations->Out()));
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt: {
+      if (second.IsRegister()) {
+        __ imull(first.As<CpuRegister>(), second.As<CpuRegister>());
+      } else if (second.IsConstant()) {
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+        __ imull(first.As<CpuRegister>(), imm);
+      } else {
+        DCHECK(second.IsStackSlot());
+        __ imull(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex()));
+      }
+      break;
+    }
+    case Primitive::kPrimLong: {
+      __ imulq(first.As<CpuRegister>(), second.As<CpuRegister>());
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ mulss(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ mulsd(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void LocationsBuilderX86_64::VisitDiv(HDiv* div) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall);
+  switch (div->GetResultType()) {
+    case Primitive::kPrimInt: {
+      locations->SetInAt(0, Location::RegisterLocation(RAX));
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      // Intel uses edx:eax as the dividend.
+      locations->AddTemp(Location::RegisterLocation(RDX));
+      break;
+    }
+    case Primitive::kPrimLong: {
+      LOG(FATAL) << "Not implemented div type" << div->GetResultType();
+      break;
+    }
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitDiv(HDiv* div) {
+  LocationSummary* locations = div->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  DCHECK(first.Equals(locations->Out()));
+
+  switch (div->GetResultType()) {
+    case Primitive::kPrimInt: {
+      CpuRegister first_reg = first.As<CpuRegister>();
+      CpuRegister second_reg = second.As<CpuRegister>();
+      DCHECK_EQ(RAX,  first_reg.AsRegister());
+      DCHECK_EQ(RDX, locations->GetTemp(0).As<CpuRegister>().AsRegister());
+
+      SlowPathCodeX86_64* slow_path =
+          new (GetGraph()->GetArena()) DivMinusOneSlowPathX86_64(first_reg.AsRegister());
+      codegen_->AddSlowPath(slow_path);
+
+      // 0x80000000/-1 triggers an arithmetic exception!
+      // Dividing by -1 is actually negation and -0x800000000 = 0x80000000 so
+      // it's safe to just use negl instead of more complex comparisons.
+
+      __ cmpl(second_reg, Immediate(-1));
+      __ j(kEqual, slow_path->GetEntryLabel());
+
+      // edx:eax <- sign-extended of eax
+      __ cdq();
+      // eax = quotient, edx = remainder
+      __ idivl(second_reg);
+
+      __ Bind(slow_path->GetExitLabel());
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      LOG(FATAL) << "Not implemented div type" << div->GetResultType();
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ divss(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ divsd(first.As<XmmRegister>(), second.As<XmmRegister>());
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
+  }
+}
+
+void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::Any());
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+  SlowPathCodeX86_64* slow_path =
+      new (GetGraph()->GetArena()) DivZeroCheckSlowPathX86_64(instruction);
+  codegen_->AddSlowPath(slow_path);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location value = locations->InAt(0);
+
+  if (value.IsRegister()) {
+    __ testl(value.As<CpuRegister>(), value.As<CpuRegister>());
+  } else if (value.IsStackSlot()) {
+    __ cmpl(Address(CpuRegister(RSP), value.GetStackIndex()), Immediate(0));
+  } else {
+    DCHECK(value.IsConstant()) << value;
+    if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
+      __ jmp(slow_path->GetEntryLabel());
+    }
+    return;
+  }
+  __ j(kEqual, slow_path->GetEntryLabel());
+}
+
 void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
-  locations->SetOut(X86_64CpuLocation(RAX));
-  instruction->SetLocations(locations);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetOut(Location::RegisterLocation(RAX));
 }
 
 void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) {
   InvokeRuntimeCallingConvention calling_convention;
-  LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+  codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
   __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
 
   __ gs()->call(Address::Absolute(
       QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
 
   DCHECK(!codegen_->IsLeafMethod());
-  codegen_->RecordPcInfo(instruction->GetDexPc());
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetOut(Location::RegisterLocation(RAX));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+}
+
+void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {
+  InvokeRuntimeCallingConvention calling_convention;
+  codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+  __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
+
+  __ gs()->call(Address::Absolute(
+      QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocArrayWithAccessCheck), true));
+
+  DCHECK(!codegen_->IsLeafMethod());
+  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,235 +1709,284 @@
     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   }
   locations->SetOut(location);
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitParameterValue(HParameterValue* instruction) {
   // Nothing to do, the parameter is already at its location.
+  UNUSED(instruction);
 }
 
-void LocationsBuilderX86_64::VisitNot(HNot* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+void LocationsBuilderX86_64::VisitNot(HNot* not_) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
-void InstructionCodeGeneratorX86_64::VisitNot(HNot* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  DCHECK_EQ(locations->InAt(0).AsX86_64().AsCpuRegister().AsRegister(),
-            locations->Out().AsX86_64().AsCpuRegister().AsRegister());
-  __ xorq(locations->Out().AsX86_64().AsCpuRegister(), Immediate(1));
+void InstructionCodeGeneratorX86_64::VisitNot(HNot* not_) {
+  LocationSummary* locations = not_->GetLocations();
+  DCHECK_EQ(locations->InAt(0).As<CpuRegister>().AsRegister(),
+            locations->Out().As<CpuRegister>().AsRegister());
+  Location out = locations->Out();
+  switch (not_->InputAt(0)->GetType()) {
+    case Primitive::kPrimBoolean:
+      __ xorq(out.As<CpuRegister>(), Immediate(1));
+      break;
+
+    case Primitive::kPrimInt:
+      __ notl(out.As<CpuRegister>());
+      break;
+
+    case Primitive::kPrimLong:
+      __ notq(out.As<CpuRegister>());
+      break;
+
+    default:
+      LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
+  }
 }
 
 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) {
+  UNUSED(instruction);
   LOG(FATAL) << "Unimplemented";
 }
 
 void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  Primitive::Type field_type = instruction->GetFieldType();
+  bool is_object_type = field_type == Primitive::kPrimNot;
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
-  // Temporary registers for the write barrier.
-  if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+  if (is_object_type) {
+    // Temporary registers for the write barrier.
     locations->AddTemp(Location::RequiresRegister());
     locations->AddTemp(Location::RequiresRegister());
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
-  CpuRegister value = locations->InAt(1).AsX86_64().AsCpuRegister();
+  CpuRegister obj = locations->InAt(0).As<CpuRegister>();
   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:
     case Primitive::kPrimByte: {
+      CpuRegister value = locations->InAt(1).As<CpuRegister>();
       __ movb(Address(obj, offset), value);
       break;
     }
 
     case Primitive::kPrimShort:
     case Primitive::kPrimChar: {
+      CpuRegister value = locations->InAt(1).As<CpuRegister>();
       __ movw(Address(obj, offset), value);
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
+      CpuRegister value = locations->InAt(1).As<CpuRegister>();
       __ movl(Address(obj, offset), value);
       if (field_type == Primitive::kPrimNot) {
-        CpuRegister temp = locations->GetTemp(0).AsX86_64().AsCpuRegister();
-        CpuRegister card = locations->GetTemp(1).AsX86_64().AsCpuRegister();
+        CpuRegister temp = locations->GetTemp(0).As<CpuRegister>();
+        CpuRegister card = locations->GetTemp(1).As<CpuRegister>();
         codegen_->MarkGCCard(temp, card, obj, value);
       }
       break;
     }
 
     case Primitive::kPrimLong: {
+      CpuRegister value = locations->InAt(1).As<CpuRegister>();
       __ movq(Address(obj, offset), value);
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << field_type;
+    case Primitive::kPrimFloat: {
+      XmmRegister value = locations->InAt(1).As<XmmRegister>();
+      __ movss(Address(obj, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      XmmRegister value = locations->InAt(1).As<XmmRegister>();
+      __ movsd(Address(obj, offset), value);
+      break;
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << field_type;
+      UNREACHABLE();
   }
 }
 
 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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
-  CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+  CpuRegister obj = locations->InAt(0).As<CpuRegister>();
   size_t offset = instruction->GetFieldOffset().SizeValue();
 
   switch (instruction->GetType()) {
     case Primitive::kPrimBoolean: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
       __ movzxb(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimByte: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
       __ movsxb(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimShort: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
       __ movsxw(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimChar: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
       __ movzxw(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
       __ movl(out, Address(obj, offset));
       break;
     }
 
     case Primitive::kPrimLong: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
       __ movq(out, Address(obj, offset));
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+    case Primitive::kPrimFloat: {
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      __ movss(out, Address(obj, offset));
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      __ movsd(out, Address(obj, offset));
+      break;
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
   }
 }
 
 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);
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
 }
 
 void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction->GetDexPc());
+  SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
   Location obj = locations->InAt(0);
-  DCHECK(obj.Equals(locations->Out()));
 
   if (obj.IsRegister()) {
-    __ cmpl(obj.AsX86_64().AsCpuRegister(), Immediate(0));
-  } else {
-    DCHECK(locations->InAt(0).IsStackSlot());
+    __ cmpl(obj.As<CpuRegister>(), Immediate(0));
+  } else if (obj.IsStackSlot()) {
     __ cmpl(Address(CpuRegister(RSP), obj.GetStackIndex()), Immediate(0));
+  } else {
+    DCHECK(obj.IsConstant()) << obj;
+    DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
+    __ jmp(slow_path->GetEntryLabel());
+    return;
   }
   __ j(kEqual, slow_path->GetEntryLabel());
 }
 
 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);
+  locations->SetInAt(
+      1, Location::RegisterOrConstant(instruction->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
+  CpuRegister obj = locations->InAt(0).As<CpuRegister>();
   Location index = locations->InAt(1);
 
   switch (instruction->GetType()) {
     case Primitive::kPrimBoolean: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
-      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      CpuRegister out = locations->Out().As<CpuRegister>();
       if (index.IsConstant()) {
         __ movzxb(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
       } else {
-        __ movzxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset));
+        __ movzxb(out, Address(obj, index.As<CpuRegister>(), TIMES_1, data_offset));
       }
       break;
     }
 
     case Primitive::kPrimByte: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
-      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      CpuRegister out = locations->Out().As<CpuRegister>();
       if (index.IsConstant()) {
         __ movsxb(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
       } else {
-        __ movsxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset));
+        __ movsxb(out, Address(obj, index.As<CpuRegister>(), TIMES_1, data_offset));
       }
       break;
     }
 
     case Primitive::kPrimShort: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
-      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      CpuRegister out = locations->Out().As<CpuRegister>();
       if (index.IsConstant()) {
         __ movsxw(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
       } else {
-        __ movsxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset));
+        __ movsxw(out, Address(obj, index.As<CpuRegister>(), TIMES_2, data_offset));
       }
       break;
     }
 
     case Primitive::kPrimChar: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
-      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      CpuRegister out = locations->Out().As<CpuRegister>();
       if (index.IsConstant()) {
         __ movzxw(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
       } else {
-        __ movzxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset));
+        __ movzxw(out, Address(obj, index.As<CpuRegister>(), TIMES_2, data_offset));
       }
       break;
     }
@@ -1141,70 +1995,109 @@
     case Primitive::kPrimNot: {
       DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
-      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      CpuRegister out = locations->Out().As<CpuRegister>();
       if (index.IsConstant()) {
         __ movl(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
       } else {
-        __ movl(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset));
+        __ movl(out, Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset));
       }
       break;
     }
 
     case Primitive::kPrimLong: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
-      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      CpuRegister out = locations->Out().As<CpuRegister>();
       if (index.IsConstant()) {
         __ movq(out, Address(obj,
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset));
       } else {
-        __ movq(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset));
+        __ movq(out, Address(obj, index.As<CpuRegister>(), TIMES_8, data_offset));
       }
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+    case Primitive::kPrimFloat: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      if (index.IsConstant()) {
+        __ movss(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
+      } else {
+        __ movss(out, Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      if (index.IsConstant()) {
+        __ movsd(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset));
+      } else {
+        __ movsd(out, Address(obj, index.As<CpuRegister>(), TIMES_8, data_offset));
+      }
+      break;
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
   }
 }
 
 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();
+    locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+    locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
   } else {
     locations->SetInAt(0, Location::RequiresRegister());
-    locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+    locations->SetInAt(
+        1, Location::RegisterOrConstant(instruction->InputAt(1)));
     locations->SetInAt(2, Location::RequiresRegister());
+    if (value_type == Primitive::kPrimLong) {
+      locations->SetInAt(2, Location::RequiresRegister());
+    } else if (value_type == Primitive::kPrimFloat || value_type == Primitive::kPrimDouble) {
+      locations->SetInAt(2, Location::RequiresFpuRegister());
+    } else {
+      locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
+    }
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
+  CpuRegister obj = locations->InAt(0).As<CpuRegister>();
   Location index = locations->InAt(1);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Location value = locations->InAt(2);
+  Primitive::Type value_type = instruction->GetComponentType();
 
   switch (value_type) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
-      CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
-        __ movb(Address(obj, offset), value);
+        if (value.IsRegister()) {
+          __ movb(Address(obj, offset), value.As<CpuRegister>());
+        } else {
+          __ movb(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       } else {
-        __ movb(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset), value);
+        if (value.IsRegister()) {
+          __ movb(Address(obj, index.As<CpuRegister>(), TIMES_1, data_offset),
+                  value.As<CpuRegister>());
+        } else {
+          __ movb(Address(obj, index.As<CpuRegister>(), TIMES_1, data_offset),
+                  Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       }
       break;
     }
@@ -1212,24 +2105,43 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimChar: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
-      CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
-        __ movw(Address(obj, offset), value);
+        if (value.IsRegister()) {
+          __ movw(Address(obj, offset), value.As<CpuRegister>());
+        } else {
+          __ movw(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       } else {
-        __ movw(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset), value);
+        if (value.IsRegister()) {
+          __ movw(Address(obj, index.As<CpuRegister>(), TIMES_2, data_offset),
+                  value.As<CpuRegister>());
+        } else {
+          __ movw(Address(obj, index.As<CpuRegister>(), TIMES_2, data_offset),
+                  Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       }
       break;
     }
 
     case Primitive::kPrimInt: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
-      CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-        __ movl(Address(obj, offset), value);
+        if (value.IsRegister()) {
+          __ movl(Address(obj, offset), value.As<CpuRegister>());
+        } else {
+          __ movl(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       } else {
-        __ movl(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset), value);
+        if (value.IsRegister()) {
+          __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset),
+                  value.As<CpuRegister>());
+        } else {
+          DCHECK(value.IsConstant()) << value;
+          __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset),
+                  Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+        }
       }
       break;
     }
@@ -1237,63 +2149,91 @@
     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;
     }
 
     case Primitive::kPrimLong: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
-      CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ movq(Address(obj, offset), value);
+        DCHECK(value.IsRegister());
+        __ movq(Address(obj, offset), value.As<CpuRegister>());
       } else {
-        __ movq(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset), value);
+        DCHECK(value.IsRegister());
+        __ movq(Address(obj, index.As<CpuRegister>(), TIMES_8, data_offset),
+                value.As<CpuRegister>());
       }
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+    case Primitive::kPrimFloat: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        DCHECK(value.IsFpuRegister());
+        __ movss(Address(obj, offset), value.As<XmmRegister>());
+      } else {
+        DCHECK(value.IsFpuRegister());
+        __ movss(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset),
+                value.As<XmmRegister>());
+      }
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        DCHECK(value.IsFpuRegister());
+        __ movsd(Address(obj, offset), value.As<XmmRegister>());
+      } else {
+        DCHECK(value.IsFpuRegister());
+        __ movsd(Address(obj, index.As<CpuRegister>(), TIMES_8, data_offset),
+                value.As<XmmRegister>());
+      }
+      break;
+    }
 
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
   }
 }
 
 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);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
-  CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
-  CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+  CpuRegister obj = locations->InAt(0).As<CpuRegister>();
+  CpuRegister out = locations->Out().As<CpuRegister>();
   __ movl(out, Address(obj, offset));
 }
 
 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);
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
 }
 
 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));
+  SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
-  CpuRegister index = locations->InAt(0).AsX86_64().AsCpuRegister();
-  CpuRegister length = locations->InAt(1).AsX86_64().AsCpuRegister();
+  CpuRegister index = locations->InAt(0).As<CpuRegister>();
+  CpuRegister length = locations->InAt(1).As<CpuRegister>();
 
   __ cmpl(index, length);
   __ j(kAboveEqual, slow_path->GetEntryLabel());
@@ -1320,9 +2260,11 @@
 
 void InstructionCodeGeneratorX86_64::VisitTemporary(HTemporary* temp) {
   // Nothing to do, this is driven by the code generator.
+  UNUSED(temp);
 }
 
 void LocationsBuilderX86_64::VisitParallelMove(HParallelMove* instruction) {
+  UNUSED(instruction);
   LOG(FATAL) << "Unimplemented";
 }
 
@@ -1330,6 +2272,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();
 }
@@ -1341,18 +2317,21 @@
 
   if (source.IsRegister()) {
     if (destination.IsRegister()) {
-      __ movq(destination.AsX86_64().AsCpuRegister(), source.AsX86_64().AsCpuRegister());
+      __ movq(destination.As<CpuRegister>(), source.As<CpuRegister>());
     } else if (destination.IsStackSlot()) {
       __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()),
-              source.AsX86_64().AsCpuRegister());
+              source.As<CpuRegister>());
     } else {
       DCHECK(destination.IsDoubleStackSlot());
       __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()),
-              source.AsX86_64().AsCpuRegister());
+              source.As<CpuRegister>());
     }
   } else if (source.IsStackSlot()) {
     if (destination.IsRegister()) {
-      __ movl(destination.AsX86_64().AsX86_64().AsCpuRegister(),
+      __ movl(destination.As<CpuRegister>(),
+              Address(CpuRegister(RSP), source.GetStackIndex()));
+    } else if (destination.IsFpuRegister()) {
+      __ movss(destination.As<XmmRegister>(),
               Address(CpuRegister(RSP), source.GetStackIndex()));
     } else {
       DCHECK(destination.IsStackSlot());
@@ -1361,10 +2340,12 @@
     }
   } else if (source.IsDoubleStackSlot()) {
     if (destination.IsRegister()) {
-      __ movq(destination.AsX86_64().AsX86_64().AsCpuRegister(),
+      __ movq(destination.As<CpuRegister>(),
               Address(CpuRegister(RSP), source.GetStackIndex()));
+    } else if (destination.IsFpuRegister()) {
+      __ movsd(destination.As<XmmRegister>(), Address(CpuRegister(RSP), source.GetStackIndex()));
     } else {
-      DCHECK(destination.IsDoubleStackSlot());
+      DCHECK(destination.IsDoubleStackSlot()) << destination;
       __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex()));
       __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));
     }
@@ -1373,23 +2354,52 @@
     if (constant->IsIntConstant()) {
       Immediate imm(constant->AsIntConstant()->GetValue());
       if (destination.IsRegister()) {
-        __ movl(destination.AsX86_64().AsCpuRegister(), imm);
+        __ movl(destination.As<CpuRegister>(), imm);
       } else {
+        DCHECK(destination.IsStackSlot()) << destination;
         __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm);
       }
     } else if (constant->IsLongConstant()) {
       int64_t value = constant->AsLongConstant()->GetValue();
       if (destination.IsRegister()) {
-        __ movq(destination.AsX86_64().AsCpuRegister(), Immediate(value));
+        __ movq(destination.As<CpuRegister>(), Immediate(value));
       } else {
+        DCHECK(destination.IsDoubleStackSlot()) << destination;
         __ movq(CpuRegister(TMP), Immediate(value));
         __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));
       }
+    } else if (constant->IsFloatConstant()) {
+      Immediate imm(bit_cast<float, int32_t>(constant->AsFloatConstant()->GetValue()));
+      if (destination.IsFpuRegister()) {
+        __ movl(CpuRegister(TMP), imm);
+        __ movd(destination.As<XmmRegister>(), CpuRegister(TMP));
+      } else {
+        DCHECK(destination.IsStackSlot()) << destination;
+        __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm);
+      }
     } else {
-      LOG(FATAL) << "Unimplemented constant type";
+      DCHECK(constant->IsDoubleConstant()) << constant->DebugName();
+      Immediate imm(bit_cast<double, int64_t>(constant->AsDoubleConstant()->GetValue()));
+      if (destination.IsFpuRegister()) {
+        __ movq(CpuRegister(TMP), imm);
+        __ movd(destination.As<XmmRegister>(), CpuRegister(TMP));
+      } else {
+        DCHECK(destination.IsDoubleStackSlot()) << destination;
+        __ movq(CpuRegister(TMP), imm);
+        __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));
+      }
     }
-  } else {
-    LOG(FATAL) << "Unimplemented";
+  } else if (source.IsFpuRegister()) {
+    if (destination.IsFpuRegister()) {
+      __ movaps(destination.As<XmmRegister>(), source.As<XmmRegister>());
+    } else if (destination.IsStackSlot()) {
+      __ movss(Address(CpuRegister(RSP), destination.GetStackIndex()),
+               source.As<XmmRegister>());
+    } else {
+      DCHECK(destination.IsDoubleStackSlot());
+      __ movsd(Address(CpuRegister(RSP), destination.GetStackIndex()),
+               source.As<XmmRegister>());
+    }
   }
 }
 
@@ -1431,27 +2441,51 @@
           CpuRegister(ensure_scratch.GetRegister()));
 }
 
+void ParallelMoveResolverX86_64::Exchange32(XmmRegister reg, int mem) {
+  __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), mem));
+  __ movss(Address(CpuRegister(RSP), mem), reg);
+  __ movd(reg, CpuRegister(TMP));
+}
+
+void ParallelMoveResolverX86_64::Exchange64(XmmRegister reg, int mem) {
+  __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), mem));
+  __ movsd(Address(CpuRegister(RSP), mem), reg);
+  __ movd(reg, CpuRegister(TMP));
+}
+
 void ParallelMoveResolverX86_64::EmitSwap(size_t index) {
   MoveOperands* move = moves_.Get(index);
   Location source = move->GetSource();
   Location destination = move->GetDestination();
 
   if (source.IsRegister() && destination.IsRegister()) {
-    __ xchgq(destination.AsX86_64().AsCpuRegister(), source.AsX86_64().AsCpuRegister());
+    __ xchgq(destination.As<CpuRegister>(), source.As<CpuRegister>());
   } else if (source.IsRegister() && destination.IsStackSlot()) {
-    Exchange32(source.AsX86_64().AsCpuRegister(), destination.GetStackIndex());
+    Exchange32(source.As<CpuRegister>(), destination.GetStackIndex());
   } else if (source.IsStackSlot() && destination.IsRegister()) {
-    Exchange32(destination.AsX86_64().AsCpuRegister(), source.GetStackIndex());
+    Exchange32(destination.As<CpuRegister>(), source.GetStackIndex());
   } else if (source.IsStackSlot() && destination.IsStackSlot()) {
     Exchange32(destination.GetStackIndex(), source.GetStackIndex());
   } else if (source.IsRegister() && destination.IsDoubleStackSlot()) {
-    Exchange64(source.AsX86_64().AsCpuRegister(), destination.GetStackIndex());
+    Exchange64(source.As<CpuRegister>(), destination.GetStackIndex());
   } else if (source.IsDoubleStackSlot() && destination.IsRegister()) {
-    Exchange64(destination.AsX86_64().AsCpuRegister(), source.GetStackIndex());
+    Exchange64(destination.As<CpuRegister>(), source.GetStackIndex());
   } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
     Exchange64(destination.GetStackIndex(), source.GetStackIndex());
+  } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
+    __ movd(CpuRegister(TMP), source.As<XmmRegister>());
+    __ movaps(source.As<XmmRegister>(), destination.As<XmmRegister>());
+    __ movd(destination.As<XmmRegister>(), CpuRegister(TMP));
+  } else if (source.IsFpuRegister() && destination.IsStackSlot()) {
+    Exchange32(source.As<XmmRegister>(), destination.GetStackIndex());
+  } else if (source.IsStackSlot() && destination.IsFpuRegister()) {
+    Exchange32(destination.As<XmmRegister>(), source.GetStackIndex());
+  } else if (source.IsFpuRegister() && destination.IsDoubleStackSlot()) {
+    Exchange64(source.As<XmmRegister>(), destination.GetStackIndex());
+  } else if (source.IsDoubleStackSlot() && destination.IsFpuRegister()) {
+    Exchange64(destination.As<XmmRegister>(), source.GetStackIndex());
   } else {
-    LOG(FATAL) << "Unimplemented";
+    LOG(FATAL) << "Unimplemented swap between " << source << " and " << destination;
   }
 }
 
@@ -1465,5 +2499,303 @@
   __ popq(CpuRegister(reg));
 }
 
+void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck(
+    SlowPathCodeX86_64* slow_path, CpuRegister class_reg) {
+  __ cmpl(Address(class_reg,  mirror::Class::StatusOffset().Int32Value()),
+          Immediate(mirror::Class::kStatusInitialized));
+  __ j(kLess, slow_path->GetEntryLabel());
+  __ Bind(slow_path->GetExitLabel());
+  // No need for memory fence, thanks to the X86_64 memory model.
+}
+
+void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
+  LocationSummary::CallKind call_kind = cls->CanCallRuntime()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
+  CpuRegister out = cls->GetLocations()->Out().As<CpuRegister>();
+  if (cls->IsReferrersClass()) {
+    DCHECK(!cls->CanCallRuntime());
+    DCHECK(!cls->MustGenerateClinitCheck());
+    codegen_->LoadCurrentMethod(out);
+    __ movl(out, Address(out, mirror::ArtMethod::DeclaringClassOffset().Int32Value()));
+  } else {
+    DCHECK(cls->CanCallRuntime());
+    codegen_->LoadCurrentMethod(out);
+    __ movl(out, Address(out, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+    __ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+    SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86_64(
+        cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+    codegen_->AddSlowPath(slow_path);
+    __ testl(out, out);
+    __ j(kEqual, slow_path->GetEntryLabel());
+    if (cls->MustGenerateClinitCheck()) {
+      GenerateClassInitializationCheck(slow_path, out);
+    } else {
+      __ Bind(slow_path->GetExitLabel());
+    }
+  }
+}
+
+void LocationsBuilderX86_64::VisitClinitCheck(HClinitCheck* check) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
+  locations->SetInAt(0, Location::RequiresRegister());
+  if (check->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) {
+  // We assume the class to not be null.
+  SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86_64(
+      check->GetLoadClass(), check, check->GetDexPc(), true);
+  codegen_->AddSlowPath(slow_path);
+  GenerateClassInitializationCheck(slow_path, check->GetLocations()->InAt(0).As<CpuRegister>());
+}
+
+void LocationsBuilderX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  CpuRegister cls = locations->InAt(0).As<CpuRegister>();
+  size_t offset = instruction->GetFieldOffset().SizeValue();
+
+  switch (instruction->GetType()) {
+    case Primitive::kPrimBoolean: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
+      __ movzxb(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimByte: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
+      __ movsxb(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimShort: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
+      __ movsxw(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimChar: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
+      __ movzxw(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
+      __ movl(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      CpuRegister out = locations->Out().As<CpuRegister>();
+      __ movq(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      __ movss(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      XmmRegister out = locations->Out().As<XmmRegister>();
+      __ movsd(out, Address(cls, offset));
+      break;
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  Primitive::Type field_type = instruction->GetFieldType();
+  bool is_object_type = field_type == Primitive::kPrimNot;
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  if (is_object_type) {
+    // Temporary registers for the write barrier.
+    locations->AddTemp(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  CpuRegister cls = locations->InAt(0).As<CpuRegister>();
+  size_t offset = instruction->GetFieldOffset().SizeValue();
+  Primitive::Type field_type = instruction->GetFieldType();
+
+  switch (field_type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte: {
+      CpuRegister value = locations->InAt(1).As<CpuRegister>();
+      __ movb(Address(cls, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar: {
+      CpuRegister value = locations->InAt(1).As<CpuRegister>();
+      __ movw(Address(cls, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      CpuRegister value = locations->InAt(1).As<CpuRegister>();
+      __ movl(Address(cls, offset), value);
+      if (field_type == Primitive::kPrimNot) {
+        CpuRegister temp = locations->GetTemp(0).As<CpuRegister>();
+        CpuRegister card = locations->GetTemp(1).As<CpuRegister>();
+        codegen_->MarkGCCard(temp, card, cls, value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      CpuRegister value = locations->InAt(1).As<CpuRegister>();
+      __ movq(Address(cls, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      XmmRegister value = locations->InAt(1).As<XmmRegister>();
+      __ movss(Address(cls, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      XmmRegister value = locations->InAt(1).As<XmmRegister>();
+      __ movsd(Address(cls, offset), value);
+      break;
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << field_type;
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
+  SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
+  codegen_->AddSlowPath(slow_path);
+
+  CpuRegister out = load->GetLocations()->Out().As<CpuRegister>();
+  codegen_->LoadCurrentMethod(CpuRegister(out));
+  __ movl(out, Address(out, mirror::ArtMethod::DexCacheStringsOffset().Int32Value()));
+  __ movl(out, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
+  __ testl(out, out);
+  __ j(kEqual, slow_path->GetEntryLabel());
+  __ Bind(slow_path->GetExitLabel());
+}
+
+void LocationsBuilderX86_64::VisitLoadException(HLoadException* load) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86_64::VisitLoadException(HLoadException* load) {
+  Address address = Address::Absolute(
+      Thread::ExceptionOffset<kX86_64WordSize>().Int32Value(), true);
+  __ gs()->movl(load->GetLocations()->Out().As<CpuRegister>(), address);
+  __ gs()->movl(address, Immediate(0));
+}
+
+void LocationsBuilderX86_64::VisitThrow(HThrow* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+}
+
+void InstructionCodeGeneratorX86_64::VisitThrow(HThrow* instruction) {
+  __ gs()->call(
+        Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pDeliverException), true));
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void LocationsBuilderX86_64::VisitTypeCheck(HTypeCheck* instruction) {
+  LocationSummary::CallKind call_kind = instruction->IsClassFinal()
+      ? LocationSummary::kNoCall
+      : LocationSummary::kCallOnSlowPath;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::Any());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86_64::VisitTypeCheck(HTypeCheck* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  CpuRegister obj = locations->InAt(0).As<CpuRegister>();
+  Location cls = locations->InAt(1);
+  CpuRegister out = locations->Out().As<CpuRegister>();
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+  Label done, zero;
+  SlowPathCodeX86_64* slow_path = nullptr;
+
+  // Return 0 if `obj` is null.
+  // TODO: avoid this check if we know obj is not null.
+  __ testl(obj, obj);
+  __ j(kEqual, &zero);
+  // Compare the class of `obj` with `cls`.
+  __ movl(out, Address(obj, class_offset));
+  if (cls.IsRegister()) {
+    __ cmpl(out, cls.As<CpuRegister>());
+  } else {
+    DCHECK(cls.IsStackSlot()) << cls;
+    __ cmpl(out, Address(CpuRegister(RSP), cls.GetStackIndex()));
+  }
+  if (instruction->IsClassFinal()) {
+    // Classes must be equal for the instanceof to succeed.
+    __ j(kNotEqual, &zero);
+    __ movl(out, Immediate(1));
+    __ jmp(&done);
+  } else {
+    // If the classes are not equal, we go into a slow path.
+    DCHECK(locations->OnlyCallsOnSlowPath());
+    slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathX86_64(
+        instruction, Location::RegisterLocation(out.AsRegister()));
+    codegen_->AddSlowPath(slow_path);
+    __ j(kNotEqual, slow_path->GetEntryLabel());
+    __ movl(out, Immediate(1));
+    __ jmp(&done);
+  }
+  __ Bind(&zero);
+  __ movl(out, Immediate(0));
+  if (slow_path != nullptr) {
+    __ Bind(slow_path->GetExitLabel());
+  }
+  __ Bind(&done);
+}
+
 }  // namespace x86_64
 }  // namespace art
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 44552ea..9565b6f 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -28,13 +28,19 @@
 static constexpr size_t kX86_64WordSize = 8;
 
 static constexpr Register kParameterCoreRegisters[] = { RSI, RDX, RCX, R8, R9 };
+static constexpr FloatRegister kParameterFloatRegisters[] =
+    { XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7 };
 
 static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
+static constexpr size_t kParameterFloatRegistersLength = arraysize(kParameterFloatRegisters);
 
-class InvokeDexCallingConvention : public CallingConvention<Register> {
+class InvokeDexCallingConvention : public CallingConvention<Register, FloatRegister> {
  public:
-  InvokeDexCallingConvention()
-      : CallingConvention(kParameterCoreRegisters, kParameterCoreRegistersLength) {}
+  InvokeDexCallingConvention() : CallingConvention(
+      kParameterCoreRegisters,
+      kParameterCoreRegistersLength,
+      kParameterFloatRegisters,
+      kParameterFloatRegistersLength) {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention);
@@ -42,36 +48,43 @@
 
 class InvokeDexCallingConventionVisitor {
  public:
-  InvokeDexCallingConventionVisitor() : gp_index_(0), stack_index_(0) {}
+  InvokeDexCallingConventionVisitor() : gp_index_(0), fp_index_(0), stack_index_(0) {}
 
   Location GetNextLocation(Primitive::Type type);
 
  private:
   InvokeDexCallingConvention calling_convention;
+  // The current index for cpu registers.
   uint32_t gp_index_;
+  // The current index for fpu registers.
+  uint32_t fp_index_;
+  // The current stack index.
   uint32_t stack_index_;
 
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor);
 };
 
 class CodeGeneratorX86_64;
+class SlowPathCodeX86_64;
 
 class ParallelMoveResolverX86_64 : public ParallelMoveResolver {
  public:
   ParallelMoveResolverX86_64(ArenaAllocator* allocator, CodeGeneratorX86_64* codegen)
       : ParallelMoveResolver(allocator), codegen_(codegen) {}
 
-  virtual void EmitMove(size_t index) OVERRIDE;
-  virtual void EmitSwap(size_t index) OVERRIDE;
-  virtual void SpillScratch(int reg) OVERRIDE;
-  virtual void RestoreScratch(int reg) OVERRIDE;
+  void EmitMove(size_t index) OVERRIDE;
+  void EmitSwap(size_t index) OVERRIDE;
+  void SpillScratch(int reg) OVERRIDE;
+  void RestoreScratch(int reg) OVERRIDE;
 
   X86_64Assembler* GetAssembler() const;
 
  private:
   void Exchange32(CpuRegister reg, int mem);
+  void Exchange32(XmmRegister reg, int mem);
   void Exchange32(int mem1, int mem2);
   void Exchange64(CpuRegister reg, int mem);
+  void Exchange64(XmmRegister reg, int mem);
   void Exchange64(int mem1, int mem2);
 
   CodeGeneratorX86_64* const codegen_;
@@ -84,13 +97,15 @@
   LocationsBuilderX86_64(HGraph* graph, CodeGeneratorX86_64* codegen)
       : HGraphVisitor(graph), codegen_(codegen) {}
 
-#define DECLARE_VISIT_INSTRUCTION(name)     \
-  virtual void Visit##name(H##name* instr);
+#define DECLARE_VISIT_INSTRUCTION(name, super)     \
+  void Visit##name(H##name* instr) OVERRIDE;
 
   FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
+  void HandleInvoke(HInvoke* invoke);
+
  private:
   CodeGeneratorX86_64* const codegen_;
   InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -102,18 +117,22 @@
  public:
   InstructionCodeGeneratorX86_64(HGraph* graph, CodeGeneratorX86_64* codegen);
 
-#define DECLARE_VISIT_INSTRUCTION(name)     \
-  virtual void Visit##name(H##name* instr);
+#define DECLARE_VISIT_INSTRUCTION(name, super)     \
+  void Visit##name(H##name* instr) OVERRIDE;
 
   FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
-  void LoadCurrentMethod(CpuRegister reg);
-
   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);
+  void GenerateClassInitializationCheck(SlowPathCodeX86_64* slow_path, CpuRegister class_reg);
+
   X86_64Assembler* const assembler_;
   CodeGeneratorX86_64* const codegen_;
 
@@ -125,26 +144,30 @@
   explicit CodeGeneratorX86_64(HGraph* graph);
   virtual ~CodeGeneratorX86_64() {}
 
-  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;
+  void GenerateFrameEntry() OVERRIDE;
+  void GenerateFrameExit() OVERRIDE;
+  void Bind(HBasicBlock* block) OVERRIDE;
+  void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+  size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
 
-  virtual size_t GetWordSize() const OVERRIDE {
+  size_t GetWordSize() const OVERRIDE {
     return kX86_64WordSize;
   }
 
-  virtual size_t FrameEntrySpillSize() const OVERRIDE;
+  size_t FrameEntrySpillSize() const OVERRIDE;
 
-  virtual HGraphVisitor* GetLocationBuilder() OVERRIDE {
+  HGraphVisitor* GetLocationBuilder() OVERRIDE {
     return &location_builder_;
   }
 
-  virtual HGraphVisitor* GetInstructionVisitor() OVERRIDE {
+  HGraphVisitor* GetInstructionVisitor() OVERRIDE {
     return &instruction_visitor_;
   }
 
-  virtual X86_64Assembler* GetAssembler() OVERRIDE {
+  X86_64Assembler* GetAssembler() OVERRIDE {
     return &assembler_;
   }
 
@@ -152,27 +175,18 @@
     return &move_resolver_;
   }
 
-  virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
-
-  virtual size_t GetNumberOfRegisters() const OVERRIDE {
-    return kNumberOfRegIds;
+  uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+    return GetLabelOf(block)->Position();
   }
 
-  virtual size_t GetNumberOfCoreRegisters() const OVERRIDE {
-    return kNumberOfCpuRegisters;
-  }
+  Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
 
-  virtual size_t GetNumberOfFloatingPointRegisters() const OVERRIDE {
-    return kNumberOfFloatRegisters;
-  }
+  void SetupBlockedRegisters() const OVERRIDE;
+  Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
+  void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
+  void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
 
-  virtual void SetupBlockedRegisters(bool* blocked_registers) const OVERRIDE;
-  virtual ManagedRegister AllocateFreeRegister(
-      Primitive::Type type, bool* blocked_registers) const OVERRIDE;
-  virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
-  virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
-
-  virtual InstructionSet GetInstructionSet() const OVERRIDE {
+  InstructionSet GetInstructionSet() const OVERRIDE {
     return InstructionSet::kX86_64;
   }
 
@@ -182,7 +196,19 @@
   // Helper method to move a value between two locations.
   void Move(Location destination, Location source);
 
+  void LoadCurrentMethod(CpuRegister reg);
+
+  Label* GetLabelOf(HBasicBlock* block) const {
+    return block_labels_.GetRawStorage() + block->GetBlockId();
+  }
+
+  void Initialize() OVERRIDE {
+    block_labels_.SetSize(GetGraph()->GetBlocks().Size());
+  }
+
  private:
+  // Labels for each block that will be compiled.
+  GrowableArray<Label> block_labels_;
   LocationsBuilderX86_64 location_builder_;
   InstructionCodeGeneratorX86_64 instruction_visitor_;
   ParallelMoveResolverX86_64 move_resolver_;
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index d7ac10d..ecee443 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -14,14 +14,24 @@
  * limitations under the License.
  */
 
+#include <functional>
+
+#include "base/macros.h"
 #include "builder.h"
-#include "code_generator.h"
+#include "code_generator_arm.h"
+#include "code_generator_arm64.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"
 #include "instruction_set.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
+#include "prepare_for_register_allocation.h"
+#include "register_allocator.h"
+#include "ssa_liveness_analysis.h"
+#include "utils.h"
 
 #include "gtest/gtest.h"
 
@@ -47,24 +57,89 @@
   DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator);
 };
 
-#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
+template <typename Expected>
 static void Run(const InternalCodeAllocator& allocator,
                 const CodeGenerator& codegen,
                 bool has_result,
-                int32_t expected) {
-  typedef int32_t (*fptr)();
+                Expected expected) {
+  typedef Expected (*fptr)();
   CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize());
   fptr f = reinterpret_cast<fptr>(allocator.GetMemory());
   if (codegen.GetInstructionSet() == kThumb2) {
     // For thumb we need the bottom bit set.
     f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1);
   }
-  int32_t result = f();
+  Expected result = f();
   if (has_result) {
-    CHECK_EQ(result, expected);
+    ASSERT_EQ(result, expected);
   }
 }
-#endif
+
+template <typename Expected>
+static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
+  InternalCodeAllocator allocator;
+
+  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.
+  codegenX86.CompileBaseline(&allocator, true);
+  if (kRuntimeISA == kX86) {
+    Run(allocator, codegenX86, has_result, expected);
+  }
+
+  arm::CodeGeneratorARM codegenARM(graph);
+  codegenARM.CompileBaseline(&allocator, true);
+  if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
+    Run(allocator, codegenARM, has_result, expected);
+  }
+
+  x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+  codegenX86_64.CompileBaseline(&allocator, true);
+  if (kRuntimeISA == kX86_64) {
+    Run(allocator, codegenX86_64, has_result, expected);
+  }
+
+  arm64::CodeGeneratorARM64 codegenARM64(graph);
+  codegenARM64.CompileBaseline(&allocator, true);
+  if (kRuntimeISA == kArm64) {
+    Run(allocator, codegenARM64, has_result, expected);
+  }
+}
+
+template <typename Expected>
+static void RunCodeOptimized(CodeGenerator* codegen,
+                             HGraph* graph,
+                             std::function<void(HGraph*)> hook_before_codegen,
+                             bool has_result,
+                             Expected expected) {
+  SsaLivenessAnalysis liveness(*graph, codegen);
+  liveness.Analyze();
+
+  RegisterAllocator register_allocator(graph->GetArena(), codegen, liveness);
+  register_allocator.AllocateRegisters();
+  hook_before_codegen(graph);
+
+  InternalCodeAllocator allocator;
+  codegen->CompileOptimized(&allocator);
+  Run(allocator, *codegen, has_result, expected);
+}
+
+template <typename Expected>
+static void RunCodeOptimized(HGraph* graph,
+                             std::function<void(HGraph*)> hook_before_codegen,
+                             bool has_result,
+                             Expected expected) {
+  if (kRuntimeISA == kX86) {
+    x86::CodeGeneratorX86 codegenX86(graph);
+    RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected);
+  } else if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
+    arm::CodeGeneratorARM codegenARM(graph);
+    RunCodeOptimized(&codegenARM, graph, hook_before_codegen, has_result, expected);
+  } else if (kRuntimeISA == kX86_64) {
+    x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+    RunCodeOptimized(&codegenX86_64, graph, hook_before_codegen, has_result, expected);
+  }
+}
 
 static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) {
   ArenaPool pool;
@@ -73,27 +148,21 @@
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
   HGraph* graph = builder.BuildGraph(*item);
   ASSERT_NE(graph, nullptr);
-  InternalCodeAllocator allocator;
+  // Remove suspend checks, they cannot be executed in this context.
+  RemoveSuspendChecks(graph);
+  RunCodeBaseline(graph, has_result, expected);
+}
 
-  CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, kX86);
-  // 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
-
-  codegen = CodeGenerator::Create(&arena, graph, kArm);
-  codegen->CompileBaseline(&allocator, true);
-#if defined(__arm__)
-  Run(allocator, *codegen, has_result, expected);
-#endif
-
-  codegen = CodeGenerator::Create(&arena, graph, kX86_64);
-  codegen->CompileBaseline(&allocator, true);
-#if defined(__x86_64__)
-  Run(allocator, *codegen, has_result, expected);
-#endif
+static void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected) {
+  ArenaPool pool;
+  ArenaAllocator arena(&pool);
+  HGraphBuilder builder(&arena, Primitive::kPrimLong);
+  const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
+  HGraph* graph = builder.BuildGraph(*item);
+  ASSERT_NE(graph, nullptr);
+  // Remove suspend checks, they cannot be executed in this context.
+  RemoveSuspendChecks(graph);
+  RunCodeBaseline(graph, has_result, expected);
 }
 
 TEST(CodegenTest, ReturnVoid) {
@@ -216,6 +285,83 @@
   TestCode(data, true, 0);
 }
 
+// Exercise bit-wise (one's complement) not-int instruction.
+#define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \
+TEST(CodegenTest, TEST_NAME) {                          \
+  const int32_t input = INPUT;                          \
+  const uint16_t input_lo = Low16Bits(input);           \
+  const uint16_t input_hi = High16Bits(input);          \
+  const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(      \
+      Instruction::CONST | 0 << 8, input_lo, input_hi,  \
+      Instruction::NOT_INT | 1 << 8 | 0 << 12 ,         \
+      Instruction::RETURN | 1 << 8);                    \
+                                                        \
+  TestCode(data, true, EXPECTED_OUTPUT);                \
+}
+
+NOT_INT_TEST(ReturnNotIntMinus2, -2, 1)
+NOT_INT_TEST(ReturnNotIntMinus1, -1, 0)
+NOT_INT_TEST(ReturnNotInt0, 0, -1)
+NOT_INT_TEST(ReturnNotInt1, 1, -2)
+NOT_INT_TEST(ReturnNotIntINT32_MIN, -2147483648, 2147483647)  // (2^31) - 1
+NOT_INT_TEST(ReturnNotIntINT32_MINPlus1, -2147483647, 2147483646)  // (2^31) - 2
+NOT_INT_TEST(ReturnNotIntINT32_MAXMinus1, 2147483646, -2147483647)  // -(2^31) - 1
+NOT_INT_TEST(ReturnNotIntINT32_MAX, 2147483647, -2147483648)  // -(2^31)
+
+#undef NOT_INT_TEST
+
+// Exercise bit-wise (one's complement) not-long instruction.
+#define NOT_LONG_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT)                 \
+TEST(CodegenTest, TEST_NAME) {                                           \
+  const int64_t input = INPUT;                                           \
+  const uint16_t word0 = Low16Bits(Low32Bits(input));   /* LSW. */       \
+  const uint16_t word1 = High16Bits(Low32Bits(input));                   \
+  const uint16_t word2 = Low16Bits(High32Bits(input));                   \
+  const uint16_t word3 = High16Bits(High32Bits(input)); /* MSW. */       \
+  const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM(                      \
+      Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3,      \
+      Instruction::NOT_LONG | 2 << 8 | 0 << 12,                          \
+      Instruction::RETURN_WIDE | 2 << 8);                                \
+                                                                         \
+  TestCodeLong(data, true, EXPECTED_OUTPUT);                             \
+}
+
+NOT_LONG_TEST(ReturnNotLongMinus2, INT64_C(-2), INT64_C(1))
+NOT_LONG_TEST(ReturnNotLongMinus1, INT64_C(-1), INT64_C(0))
+NOT_LONG_TEST(ReturnNotLong0, INT64_C(0), INT64_C(-1))
+NOT_LONG_TEST(ReturnNotLong1, INT64_C(1), INT64_C(-2))
+
+NOT_LONG_TEST(ReturnNotLongINT32_MIN,
+              INT64_C(-2147483648),
+              INT64_C(2147483647))  // (2^31) - 1
+NOT_LONG_TEST(ReturnNotLongINT32_MINPlus1,
+              INT64_C(-2147483647),
+              INT64_C(2147483646))  // (2^31) - 2
+NOT_LONG_TEST(ReturnNotLongINT32_MAXMinus1,
+              INT64_C(2147483646),
+              INT64_C(-2147483647))  // -(2^31) - 1
+NOT_LONG_TEST(ReturnNotLongINT32_MAX,
+              INT64_C(2147483647),
+              INT64_C(-2147483648))  // -(2^31)
+
+// Note that the C++ compiler won't accept
+// INT64_C(-9223372036854775808) (that is, INT64_MIN) as a valid
+// int64_t literal, so we use INT64_C(-9223372036854775807)-1 instead.
+NOT_LONG_TEST(ReturnNotINT64_MIN,
+              INT64_C(-9223372036854775807)-1,
+              INT64_C(9223372036854775807));  // (2^63) - 1
+NOT_LONG_TEST(ReturnNotINT64_MINPlus1,
+              INT64_C(-9223372036854775807),
+              INT64_C(9223372036854775806));  // (2^63) - 2
+NOT_LONG_TEST(ReturnNotLongINT64_MAXMinus1,
+              INT64_C(9223372036854775806),
+              INT64_C(-9223372036854775807));  // -(2^63) - 1
+NOT_LONG_TEST(ReturnNotLongINT64_MAX,
+              INT64_C(9223372036854775807),
+              INT64_C(-9223372036854775807)-1);  // -(2^63)
+
+#undef NOT_LONG_TEST
+
 TEST(CodegenTest, ReturnAdd1) {
   const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
     Instruction::CONST_4 | 3 << 12 | 0,
@@ -254,4 +400,243 @@
   TestCode(data, true, 7);
 }
 
+TEST(CodegenTest, NonMaterializedCondition) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  entry->AddInstruction(new (&allocator) HGoto());
+
+  HBasicBlock* first_block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(first_block);
+  entry->AddSuccessor(first_block);
+  HIntConstant* constant0 = new (&allocator) HIntConstant(0);
+  entry->AddInstruction(constant0);
+  HIntConstant* constant1 = new (&allocator) HIntConstant(1);
+  entry->AddInstruction(constant1);
+  HEqual* equal = new (&allocator) HEqual(constant0, constant0);
+  first_block->AddInstruction(equal);
+  first_block->AddInstruction(new (&allocator) HIf(equal));
+
+  HBasicBlock* then = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* else_ = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+
+  graph->AddBlock(then);
+  graph->AddBlock(else_);
+  graph->AddBlock(exit);
+  first_block->AddSuccessor(then);
+  first_block->AddSuccessor(else_);
+  then->AddSuccessor(exit);
+  else_->AddSuccessor(exit);
+
+  exit->AddInstruction(new (&allocator) HExit());
+  then->AddInstruction(new (&allocator) HReturn(constant0));
+  else_->AddInstruction(new (&allocator) HReturn(constant1));
+
+  ASSERT_TRUE(equal->NeedsMaterialization());
+  graph->BuildDominatorTree();
+  PrepareForRegisterAllocation(graph).Run();
+  ASSERT_FALSE(equal->NeedsMaterialization());
+
+  auto hook_before_codegen = [](HGraph* graph_in) {
+    HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0);
+    HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
+    block->InsertInstructionBefore(move, block->GetLastInstruction());
+  };
+
+  RunCodeOptimized(graph, hook_before_codegen, true, 0);
+}
+
+#define MUL_TEST(TYPE, TEST_NAME)                     \
+  TEST(CodegenTest, Return ## TEST_NAME) {            \
+    const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(  \
+      Instruction::CONST_4 | 3 << 12 | 0,             \
+      Instruction::CONST_4 | 4 << 12 | 1 << 8,        \
+      Instruction::MUL_ ## TYPE, 1 << 8 | 0,          \
+      Instruction::RETURN);                           \
+                                                      \
+    TestCode(data, true, 12);                         \
+  }                                                   \
+                                                      \
+  TEST(CodegenTest, Return ## TEST_NAME ## 2addr) {   \
+    const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(  \
+      Instruction::CONST_4 | 3 << 12 | 0,             \
+      Instruction::CONST_4 | 4 << 12 | 1 << 8,        \
+      Instruction::MUL_ ## TYPE ## _2ADDR | 1 << 12,  \
+      Instruction::RETURN);                           \
+                                                      \
+    TestCode(data, true, 12);                         \
+  }
+
+#if !defined(__aarch64__)
+MUL_TEST(INT, MulInt);
+MUL_TEST(LONG, MulLong);
+#endif
+
+TEST(CodegenTest, ReturnMulIntLit8) {
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 4 << 12 | 0 << 8,
+    Instruction::MUL_INT_LIT8, 3 << 8 | 0,
+    Instruction::RETURN);
+
+  TestCode(data, true, 12);
+}
+
+TEST(CodegenTest, ReturnMulIntLit16) {
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 4 << 12 | 0 << 8,
+    Instruction::MUL_INT_LIT16, 3,
+    Instruction::RETURN);
+
+  TestCode(data, true, 12);
+}
+
+TEST(CodegenTest, MaterializedCondition1) {
+  // Check that condition are materialized correctly. A materialized condition
+  // should yield `1` if it evaluated to true, and `0` otherwise.
+  // We force the materialization of comparisons for different combinations of
+  // inputs and check the results.
+
+  int lhs[] = {1, 2, -1, 2, 0xabc};
+  int rhs[] = {2, 1, 2, -1, 0xabc};
+
+  for (size_t i = 0; i < arraysize(lhs); i++) {
+    ArenaPool pool;
+    ArenaAllocator allocator(&pool);
+    HGraph* graph = new (&allocator) HGraph(&allocator);
+
+    HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph);
+    graph->AddBlock(entry_block);
+    graph->SetEntryBlock(entry_block);
+    entry_block->AddInstruction(new (&allocator) HGoto());
+    HBasicBlock* code_block = new (&allocator) HBasicBlock(graph);
+    graph->AddBlock(code_block);
+    HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
+    graph->AddBlock(exit_block);
+    exit_block->AddInstruction(new (&allocator) HExit());
+
+    entry_block->AddSuccessor(code_block);
+    code_block->AddSuccessor(exit_block);
+    graph->SetExitBlock(exit_block);
+
+    HIntConstant cst_lhs(lhs[i]);
+    code_block->AddInstruction(&cst_lhs);
+    HIntConstant cst_rhs(rhs[i]);
+    code_block->AddInstruction(&cst_rhs);
+    HLessThan cmp_lt(&cst_lhs, &cst_rhs);
+    code_block->AddInstruction(&cmp_lt);
+    HReturn ret(&cmp_lt);
+    code_block->AddInstruction(&ret);
+
+    auto hook_before_codegen = [](HGraph* graph_in) {
+      HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0);
+      HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
+      block->InsertInstructionBefore(move, block->GetLastInstruction());
+    };
+
+    RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]);
+  }
+}
+
+TEST(CodegenTest, MaterializedCondition2) {
+  // Check that HIf correctly interprets a materialized condition.
+  // We force the materialization of comparisons for different combinations of
+  // inputs. An HIf takes the materialized combination as input and returns a
+  // value that we verify.
+
+  int lhs[] = {1, 2, -1, 2, 0xabc};
+  int rhs[] = {2, 1, 2, -1, 0xabc};
+
+
+  for (size_t i = 0; i < arraysize(lhs); i++) {
+    ArenaPool pool;
+    ArenaAllocator allocator(&pool);
+    HGraph* graph = new (&allocator) HGraph(&allocator);
+
+    HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph);
+    graph->AddBlock(entry_block);
+    graph->SetEntryBlock(entry_block);
+    entry_block->AddInstruction(new (&allocator) HGoto());
+
+    HBasicBlock* if_block = new (&allocator) HBasicBlock(graph);
+    graph->AddBlock(if_block);
+    HBasicBlock* if_true_block = new (&allocator) HBasicBlock(graph);
+    graph->AddBlock(if_true_block);
+    HBasicBlock* if_false_block = new (&allocator) HBasicBlock(graph);
+    graph->AddBlock(if_false_block);
+    HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
+    graph->AddBlock(exit_block);
+    exit_block->AddInstruction(new (&allocator) HExit());
+
+    graph->SetEntryBlock(entry_block);
+    entry_block->AddSuccessor(if_block);
+    if_block->AddSuccessor(if_true_block);
+    if_block->AddSuccessor(if_false_block);
+    if_true_block->AddSuccessor(exit_block);
+    if_false_block->AddSuccessor(exit_block);
+    graph->SetExitBlock(exit_block);
+
+    HIntConstant cst_lhs(lhs[i]);
+    if_block->AddInstruction(&cst_lhs);
+    HIntConstant cst_rhs(rhs[i]);
+    if_block->AddInstruction(&cst_rhs);
+    HLessThan cmp_lt(&cst_lhs, &cst_rhs);
+    if_block->AddInstruction(&cmp_lt);
+    // We insert a temporary to separate the HIf from the HLessThan and force
+    // the materialization of the condition.
+    HTemporary force_materialization(0);
+    if_block->AddInstruction(&force_materialization);
+    HIf if_lt(&cmp_lt);
+    if_block->AddInstruction(&if_lt);
+
+    HIntConstant cst_lt(1);
+    if_true_block->AddInstruction(&cst_lt);
+    HReturn ret_lt(&cst_lt);
+    if_true_block->AddInstruction(&ret_lt);
+    HIntConstant cst_ge(0);
+    if_false_block->AddInstruction(&cst_ge);
+    HReturn ret_ge(&cst_ge);
+    if_false_block->AddInstruction(&ret_ge);
+
+    auto hook_before_codegen = [](HGraph* graph_in) {
+      HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0);
+      HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
+      block->InsertInstructionBefore(move, block->GetLastInstruction());
+    };
+
+    RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]);
+  }
+}
+
+#if defined(__aarch64__)
+TEST(CodegenTest, DISABLED_ReturnDivIntLit8) {
+#else
+TEST(CodegenTest, ReturnDivIntLit8) {
+#endif
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 4 << 12 | 0 << 8,
+    Instruction::DIV_INT_LIT8, 3 << 8 | 0,
+    Instruction::RETURN);
+
+  TestCode(data, true, 1);
+}
+
+#if defined(__aarch64__)
+TEST(CodegenTest, DISABLED_ReturnDivInt2Addr) {
+#else
+TEST(CodegenTest, ReturnDivInt2Addr) {
+#endif
+  const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 4 << 12 | 0,
+    Instruction::CONST_4 | 2 << 12 | 1 << 8,
+    Instruction::DIV_INT_2ADDR | 1 << 12,
+    Instruction::RETURN);
+
+  TestCode(data, true, 2);
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
new file mode 100644
index 0000000..fca9933
--- /dev/null
+++ b/compiler/optimizing/constant_folding.cc
@@ -0,0 +1,55 @@
+/*
+ * 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_folding.h"
+
+namespace art {
+
+void HConstantFolding::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 inst_it(block->GetInstructions());
+         !inst_it.Done(); inst_it.Advance()) {
+      HInstruction* inst = inst_it.Current();
+      if (inst->IsBinaryOperation()) {
+        // Constant folding: replace `op(a, b)' with a constant at
+        // compile time if `a' and `b' are both constants.
+        HConstant* constant =
+            inst->AsBinaryOperation()->TryStaticEvaluation();
+        if (constant != nullptr) {
+          inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant);
+        }
+      } else if (inst->IsUnaryOperation()) {
+        // Constant folding: replace `op(a)' with a constant at compile
+        // time if `a' is a constant.
+        HConstant* constant =
+            inst->AsUnaryOperation()->TryStaticEvaluation();
+        if (constant != nullptr) {
+          inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant);
+        }
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h
new file mode 100644
index 0000000..d2acfa6
--- /dev/null
+++ b/compiler/optimizing/constant_folding.h
@@ -0,0 +1,48 @@
+/*
+ * 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_FOLDING_H_
+#define ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+/**
+ * Optimization pass performing a simple constant-expression
+ * evaluation on the SSA form.
+ *
+ * This class is named art::HConstantFolding to avoid name
+ * clashes with the art::ConstantPropagation class defined in
+ * compiler/dex/post_opt_passes.h.
+ */
+class HConstantFolding : public HOptimization {
+ public:
+  HConstantFolding(HGraph* graph, const HGraphVisualizer& visualizer)
+      : HOptimization(graph, true, kConstantFoldingPassName, visualizer) {}
+
+  virtual void Run() OVERRIDE;
+
+  static constexpr const char* kConstantFoldingPassName = "constant_folding";
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HConstantFolding);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
new file mode 100644
index 0000000..856c516
--- /dev/null
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -0,0 +1,643 @@
+/*
+ * 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 <functional>
+
+#include "code_generator_x86.h"
+#include "constant_folding.h"
+#include "dead_code_elimination.h"
+#include "graph_checker.h"
+#include "optimizing_unit_test.h"
+#include "pretty_printer.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+static void TestCode(const uint16_t* data,
+                     const std::string& expected_before,
+                     const std::string& expected_after_cf,
+                     const std::string& expected_after_dce,
+                     std::function<void(HGraph*)> check_after_cf,
+                     Primitive::Type return_type = Primitive::kPrimInt) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = CreateCFG(&allocator, data, return_type);
+  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);
+
+  x86::CodeGeneratorX86 codegen(graph);
+  HGraphVisualizer visualizer(nullptr, graph, codegen, "");
+  HConstantFolding(graph, visualizer).Run();
+  SSAChecker ssa_checker(&allocator, graph);
+  ssa_checker.Run();
+  ASSERT_TRUE(ssa_checker.IsValid());
+
+  StringPrettyPrinter printer_after_cf(graph);
+  printer_after_cf.VisitInsertionOrder();
+  std::string actual_after_cf = printer_after_cf.str();
+  ASSERT_EQ(expected_after_cf, actual_after_cf);
+
+  check_after_cf(graph);
+
+  HDeadCodeElimination(graph, visualizer).Run();
+  ssa_checker.Run();
+  ASSERT_TRUE(ssa_checker.IsValid());
+
+  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);
+}
+
+
+/**
+ * Tiny three-register program exercising int constant folding on negation.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     v0 <- 1                  0.      const/4 v0, #+1
+ *     v1 <- -v0                1.      neg-int v0, v1
+ *     return v1                2.      return v1
+ */
+TEST(ConstantFolding, IntConstantFoldingNegation) {
+  const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 0 << 8 | 1 << 12,
+    Instruction::NEG_INT | 1 << 8 | 0 << 12,
+    Instruction::RETURN | 1 << 8);
+
+  std::string expected_before =
+      "BasicBlock 0, succ: 1\n"
+      "  2: IntConstant [5]\n"
+      "  10: SuspendCheck\n"
+      "  11: Goto 1\n"
+      "BasicBlock 1, pred: 0, succ: 2\n"
+      "  5: Neg(2) [8]\n"
+      "  8: Return(5)\n"
+      "BasicBlock 2, pred: 1\n"
+      "  9: Exit\n";
+
+  // Expected difference after constant folding.
+  diff_t expected_cf_diff = {
+    { "  2: IntConstant [5]\n", "  2: IntConstant\n" },
+    { "  5: Neg(2) [8]\n",      "  12: IntConstant [8]\n" },
+    { "  8: Return(5)\n",       "  8: Return(12)\n" }
+  };
+  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
+
+  // Check the value of the computed constant.
+  auto check_after_cf = [](HGraph* graph) {
+    HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+    ASSERT_TRUE(inst->IsIntConstant());
+    ASSERT_EQ(inst->AsIntConstant()->GetValue(), -1);
+  };
+
+  // Expected difference after dead code elimination.
+  diff_t expected_dce_diff = {
+    { "  2: IntConstant\n", removed },
+  };
+  std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
+
+  TestCode(data,
+           expected_before,
+           expected_after_cf,
+           expected_after_dce,
+           check_after_cf);
+}
+
+/**
+ * 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(ConstantFolding, 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 folding.
+  diff_t expected_cf_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_cf = Patch(expected_before, expected_cf_diff);
+
+  // Check the value of the computed constant.
+  auto check_after_cf = [](HGraph* graph) {
+    HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+    ASSERT_TRUE(inst->IsIntConstant());
+    ASSERT_EQ(inst->AsIntConstant()->GetValue(), 3);
+  };
+
+  // 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_cf, expected_dce_diff);
+
+  TestCode(data,
+           expected_before,
+           expected_after_cf,
+           expected_after_dce,
+           check_after_cf);
+}
+
+/**
+ * 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(ConstantFolding, 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 folding.
+  diff_t expected_cf_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_cf = Patch(expected_before, expected_cf_diff);
+
+  // Check the values of the computed constants.
+  auto check_after_cf = [](HGraph* graph) {
+    HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction();
+    ASSERT_TRUE(inst1->IsIntConstant());
+    ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 3);
+    HInstruction* inst2 = inst1->GetNext();
+    ASSERT_TRUE(inst2->IsIntConstant());
+    ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 7);
+    HInstruction* inst3 = inst2->GetNext();
+    ASSERT_TRUE(inst3->IsIntConstant());
+    ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 10);
+  };
+
+  // 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_cf, expected_dce_diff);
+
+  TestCode(data,
+           expected_before,
+           expected_after_cf,
+           expected_after_dce,
+           check_after_cf);
+}
+
+/**
+ * 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(ConstantFolding, 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 folding.
+  diff_t expected_cf_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_cf = Patch(expected_before, expected_cf_diff);
+
+  // Check the value of the computed constant.
+  auto check_after_cf = [](HGraph* graph) {
+    HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+    ASSERT_TRUE(inst->IsIntConstant());
+    ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1);
+  };
+
+  // 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_cf, expected_dce_diff);
+
+  TestCode(data,
+           expected_before,
+           expected_after_cf,
+           expected_after_dce,
+           check_after_cf);
+}
+
+/**
+ * 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(ConstantFolding, 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 folding.
+  diff_t expected_cf_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_cf = Patch(expected_before, expected_cf_diff);
+
+  // Check the value of the computed constant.
+  auto check_after_cf = [](HGraph* graph) {
+    HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+    ASSERT_TRUE(inst->IsLongConstant());
+    ASSERT_EQ(inst->AsLongConstant()->GetValue(), 3);
+  };
+
+  // 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_cf, expected_dce_diff);
+
+  TestCode(data,
+           expected_before,
+           expected_after_cf,
+           expected_after_dce,
+           check_after_cf,
+           Primitive::kPrimLong);
+}
+
+/**
+ * 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(ConstantFolding, 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 folding.
+  diff_t expected_cf_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_cf = Patch(expected_before, expected_cf_diff);
+
+  // Check the value of the computed constant.
+  auto check_after_cf = [](HGraph* graph) {
+    HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+    ASSERT_TRUE(inst->IsLongConstant());
+    ASSERT_EQ(inst->AsLongConstant()->GetValue(), 1);
+  };
+
+  // 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_cf, expected_dce_diff);
+
+  TestCode(data,
+           expected_before,
+           expected_after_cf,
+           expected_after_dce,
+           check_after_cf,
+           Primitive::kPrimLong);
+}
+
+/**
+ * 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(ConstantFolding, 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"            // v0 <- 0
+    "  5: IntConstant [9]\n"            // v1 <- 1
+    "  13: IntConstant [14]\n"          // const 3
+    "  18: IntConstant [19]\n"          // const 2
+    "  24: IntConstant [25]\n"          // const 4
+    "  30: SuspendCheck\n"
+    "  31: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 3\n"
+    "  9: Add(3, 5) [19]\n"             // v2 <- v0 + v1 = 0 + 1 = 1
+    "  11: Goto 3\n"                    // goto L2
+    "BasicBlock 2, pred: 3, succ: 4\n"  // L1:
+    "  14: Add(19, 13) [25]\n"          // v1 <- v0 + 3 = 3 + 3 = 6
+    "  16: Goto 4\n"                    // goto L3
+    "BasicBlock 3, pred: 1, succ: 2\n"  // L2:
+    "  19: Add(9, 18) [14]\n"           // v0 <- v2 + 2 = 1 + 2 = 3
+    "  21: SuspendCheck\n"
+    "  22: Goto 2\n"                    // goto L1
+    "BasicBlock 4, pred: 2, succ: 5\n"  // L3:
+    "  25: Add(14, 24) [28]\n"          // v2 <- v1 + 4 = 6 + 4 = 10
+    "  28: Return(25)\n"                // return v2
+    "BasicBlock 5, pred: 4\n"
+    "  29: Exit\n";
+
+  // Expected difference after constant folding.
+  diff_t expected_cf_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_cf = Patch(expected_before, expected_cf_diff);
+
+  // Check the values of the computed constants.
+  auto check_after_cf = [](HGraph* graph) {
+    HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction();
+    ASSERT_TRUE(inst1->IsIntConstant());
+    ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 1);
+    HInstruction* inst2 = graph->GetBlock(2)->GetFirstInstruction();
+    ASSERT_TRUE(inst2->IsIntConstant());
+    ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 6);
+    HInstruction* inst3 = graph->GetBlock(3)->GetFirstInstruction();
+    ASSERT_TRUE(inst3->IsIntConstant());
+    ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 3);
+    HInstruction* inst4 = graph->GetBlock(4)->GetFirstInstruction();
+    ASSERT_TRUE(inst4->IsIntConstant());
+    ASSERT_EQ(inst4->AsIntConstant()->GetValue(), 10);
+  };
+
+  // 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_cf, expected_dce_diff);
+
+  TestCode(data,
+           expected_before,
+           expected_after_cf,
+           expected_after_dce,
+           check_after_cf);
+}
+
+
+/**
+ * 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(ConstantFolding, 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 folding.
+  diff_t expected_cf_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_cf = Patch(expected_before, expected_cf_diff);
+
+  // Check the values of the computed constants.
+  auto check_after_cf = [](HGraph* graph) {
+    HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+    ASSERT_TRUE(inst->IsIntConstant());
+    ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1);
+  };
+
+  // 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_cf, expected_dce_diff);
+
+  TestCode(data,
+           expected_before,
+           expected_after_cf,
+           expected_after_dce,
+           check_after_cf);
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
new file mode 100644
index 0000000..fc3dd01
--- /dev/null
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -0,0 +1,48 @@
+/*
+ * 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 HDeadCodeElimination::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->CanThrow()
+          && !inst->IsSuspendCheck()
+          && !inst->HasUses()) {
+        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..a4446ae
--- /dev/null
+++ b/compiler/optimizing/dead_code_elimination.h
@@ -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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
+#define ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+/**
+ * Optimization pass performing dead code elimination (removal of
+ * unused variables/instructions) on the SSA form.
+ */
+class HDeadCodeElimination : public HOptimization {
+ public:
+  HDeadCodeElimination(HGraph* graph, const HGraphVisualizer& visualizer)
+      : HOptimization(graph, true, kDeadCodeEliminationPassName, visualizer) {}
+
+  virtual void Run() OVERRIDE;
+
+  static constexpr const char* kDeadCodeEliminationPassName =
+    "dead_code_elimination";
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HDeadCodeElimination);
+};
+
+}  // 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..0c68074
--- /dev/null
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -0,0 +1,188 @@
+/*
+ * 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 "code_generator_x86.h"
+#include "dead_code_elimination.h"
+#include "graph_checker.h"
+#include "optimizing_unit_test.h"
+#include "pretty_printer.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);
+
+  x86::CodeGeneratorX86 codegen(graph);
+  HGraphVisualizer visualizer(nullptr, graph, codegen, "");
+  HDeadCodeElimination(graph, visualizer).Run();
+  SSAChecker ssa_checker(&allocator, graph);
+  ssa_checker.Run();
+  ASSERT_TRUE(ssa_checker.IsValid());
+
+  StringPrettyPrinter printer_after(graph);
+  printer_after.VisitInsertionOrder();
+  std::string actual_after = printer_after.str();
+  ASSERT_EQ(actual_after, expected_after);
+}
+
+
+/**
+ * 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";
+
+  // Expected difference after dead code elimination.
+  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 dead code elimination.
+  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..1953241
--- /dev/null
+++ b/compiler/optimizing/graph_checker.cc
@@ -0,0 +1,345 @@
+/*
+ * 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_.push_back(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_.push_back(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_.push_back(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_.push_back(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_.push_back(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_.push_back(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_.push_back(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_.push_back(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_.push_back(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_.push_back(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_.push_back(error.str());
+  } else if (loop_header->GetPredecessors().Size() > 2) {
+    std::stringstream error;
+    error << "Loop header " << id << " has more than two predecessors.";
+    errors_.push_back(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_.push_back(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_.push_back(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_.push_back(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_.push_back(error.str());
+    }
+  }
+}
+
+void SSAChecker::VisitInstruction(HInstruction* instruction) {
+  super_type::VisitInstruction(instruction);
+
+  // Ensure an instruction dominates all its uses.
+  for (HUseIterator<HInstruction> use_it(instruction->GetUses());
+       !use_it.Done(); use_it.Advance()) {
+    HInstruction* use = use_it.Current()->GetUser();
+    if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
+      std::stringstream error;
+      error << "Instruction " << instruction->GetId()
+            << " in block " << current_block_->GetBlockId()
+            << " does not dominate use " << use->GetId()
+            << " in block " << use->GetBlock()->GetBlockId() << ".";
+      errors_.push_back(error.str());
+    }
+  }
+
+  // Ensure an instruction having an environment is dominated by the
+  // instructions contained in the environment.
+  HEnvironment* environment = instruction->GetEnvironment();
+  if (environment != nullptr) {
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      HInstruction* env_instruction = environment->GetInstructionAt(i);
+      if (env_instruction != nullptr
+          && !env_instruction->StrictlyDominates(instruction)) {
+        std::stringstream error;
+        error << "Instruction " << env_instruction->GetId()
+              << " in environment of instruction " << instruction->GetId()
+              << " from block " << current_block_->GetBlockId()
+              << " does not dominate instruction " << instruction->GetId()
+              << ".";
+        errors_.push_back(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_.push_back(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_.push_back(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_.push_back(error.str());
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
new file mode 100644
index 0000000..8ba8cb1
--- /dev/null
+++ b/compiler/optimizing/graph_checker.h
@@ -0,0 +1,107 @@
+/*
+ * 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"
+
+#include <ostream>
+
+namespace art {
+
+// A control-flow graph visitor performing various checks.
+class GraphChecker : public HGraphVisitor {
+ public:
+  GraphChecker(ArenaAllocator* allocator, HGraph* graph,
+               const char* dump_prefix = "art::GraphChecker: ")
+    : HGraphVisitor(graph),
+      allocator_(allocator),
+      dump_prefix_(dump_prefix) {}
+
+  // Check the whole graph (in insertion order).
+  virtual void Run() { VisitInsertionOrder(); }
+
+  // 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_.empty();
+  }
+
+  // Get the list of detected errors.
+  const std::vector<std::string>& GetErrors() const {
+    return errors_;
+  }
+
+  // Print detected errors on output stream `os`.
+  void Dump(std::ostream& os) const {
+    for (size_t i = 0, e = errors_.size(); i < e; ++i) {
+      os << dump_prefix_ << errors_[i] << std::endl;
+    }
+  }
+
+ protected:
+  ArenaAllocator* const allocator_;
+  // The block currently visited.
+  HBasicBlock* current_block_ = nullptr;
+  // Errors encountered while checking the graph.
+  std::vector<std::string> errors_;
+
+ private:
+  // String displayed before dumped errors.
+  const char* const dump_prefix_;
+
+  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, "art::SSAChecker: ") {}
+
+  // Check the whole graph (in reverse post-order).
+  virtual void Run() {
+    // VisitReversePostOrder is used instead of VisitInsertionOrder,
+    // as the latter might visit dead blocks removed by the dominator
+    // computation.
+    VisitReversePostOrder();
+  }
+
+  // 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..39def82
--- /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.Run();
+  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.Run();
+  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.Run();
+  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.Run();
+  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.Run();
+  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.Run();
+  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..4ed2156 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";
@@ -101,15 +120,19 @@
     output_<< std::endl;
   }
 
-  void DumpLocation(Location location, Primitive::Type type) {
+  void DumpLocation(Location location) {
     if (location.IsRegister()) {
-      if (type == Primitive::kPrimDouble || type == Primitive::kPrimFloat) {
-        codegen_.DumpFloatingPointRegister(output_, location.reg().RegId());
-      } else {
-        codegen_.DumpCoreRegister(output_, location.reg().RegId());
-      }
+      codegen_.DumpCoreRegister(output_, location.reg());
+    } else if (location.IsFpuRegister()) {
+      codegen_.DumpFloatingPointRegister(output_, location.reg());
     } 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()) {
@@ -125,14 +148,15 @@
     output_ << " (";
     for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) {
       MoveOperands* move = instruction->MoveOperandsAt(i);
-      DumpLocation(move->GetSource(), Primitive::kPrimInt);
+      DumpLocation(move->GetSource());
       output_ << " -> ";
-      DumpLocation(move->GetDestination(), Primitive::kPrimInt);
+      DumpLocation(move->GetDestination());
       if (i + 1 != e) {
         output_ << ", ";
       }
     }
     output_ << ")";
+    output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
   }
 
   void VisitInstruction(HInstruction* instruction) {
@@ -140,7 +164,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_ << "]";
     }
@@ -157,15 +181,16 @@
       if (locations != nullptr) {
         output_ << " ( ";
         for (size_t i = 0; i < instruction->InputCount(); ++i) {
-          DumpLocation(locations->InAt(i), instruction->InputAt(i)->GetType());
+          DumpLocation(locations->InAt(i));
           output_ << " ";
         }
         output_ << ")";
         if (locations->Out().IsValid()) {
           output_ << " -> ";
-          DumpLocation(locations->Out(), instruction->GetType());
+          DumpLocation(locations->Out());
         }
       }
+      output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
     }
   }
 
@@ -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() << " ";
       }
@@ -280,7 +307,7 @@
   printer.EndTag("compilation");
 }
 
-void HGraphVisualizer::DumpGraph(const char* pass_name) {
+void HGraphVisualizer::DumpGraph(const char* pass_name) const {
   if (!is_enabled_) {
     return;
   }
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 7cd74e9..4d8bec2 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -17,7 +17,9 @@
 #ifndef ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_
 #define ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_
 
-#include "utils/allocation.h"
+#include <ostream>
+
+#include "base/value_object.h"
 
 namespace art {
 
@@ -25,8 +27,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
@@ -59,7 +63,7 @@
    * If this visualizer is enabled, emit the compilation information
    * in `output_`.
    */
-  void DumpGraph(const char* pass_name);
+  void DumpGraph(const char* pass_name) const;
 
  private:
   std::ostream* const output_;
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
new file mode 100644
index 0000000..25168b5
--- /dev/null
+++ b/compiler/optimizing/gvn.cc
@@ -0,0 +1,187 @@
+/*
+ * 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 inst_it(block->GetInstructions()); !inst_it.Done();
+         inst_it.Advance()) {
+      HInstruction* instruction = inst_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..8d2c774
--- /dev/null
+++ b/compiler/optimizing/gvn.h
@@ -0,0 +1,229 @@
+/*
+ * 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 "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<kArenaAllocMisc> {
+ 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<kArenaAllocMisc> {
+ 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_;
+
+  ART_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..29eabe7
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -0,0 +1,58 @@
+/*
+ * 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);
+}
+
+void InstructionSimplifier::VisitEqual(HEqual* equal) {
+  HInstruction* input1 = equal->InputAt(0);
+  HInstruction* input2 = equal->InputAt(1);
+  if (input1->GetType() == Primitive::kPrimBoolean && input2->IsIntConstant()) {
+    if (input2->AsIntConstant()->GetValue() == 1) {
+      // Replace (bool_value == 1) with bool_value
+      equal->ReplaceWith(equal->InputAt(0));
+      equal->GetBlock()->RemoveInstruction(equal);
+    } else {
+      // Replace (bool_value == 0) with !bool_value
+      DCHECK_EQ(input2->AsIntConstant()->GetValue(), 0);
+      equal->GetBlock()->ReplaceAndRemoveInstructionWith(
+          equal, new (GetGraph()->GetArena()) HNot(Primitive::kPrimBoolean, input1));
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
new file mode 100644
index 0000000..d74b624
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -0,0 +1,40 @@
+/*
+ * 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;
+  virtual void VisitEqual(HEqual* equal) 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..89c9495 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -16,10 +16,12 @@
 
 #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 "prepare_for_register_allocation.h"
 #include "ssa_liveness_analysis.h"
 #include "utils/arena_allocator.h"
 
@@ -31,9 +33,14 @@
   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();
+  // `Inline` conditions into ifs.
+  PrepareForRegisterAllocation(graph).Run();
   return graph;
 }
 
@@ -58,17 +65,17 @@
   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();
   LiveRange* range = interval->GetFirstRange();
   ASSERT_EQ(2u, range->GetStart());
   // Last use is the return instruction.
-  ASSERT_EQ(9u, range->GetEnd());
+  ASSERT_EQ(8u, range->GetEnd());
   HBasicBlock* block = graph->GetBlocks().Get(1);
-  ASSERT_TRUE(block->GetLastInstruction()->AsReturn() != nullptr);
+  ASSERT_TRUE(block->GetLastInstruction()->IsReturn());
   ASSERT_EQ(8u, block->GetLastInstruction()->GetLifetimePosition());
   ASSERT_TRUE(range->GetNext() == nullptr);
 }
@@ -104,17 +111,17 @@
   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();
   LiveRange* range = interval->GetFirstRange();
   ASSERT_EQ(2u, range->GetStart());
   // Last use is the return instruction.
-  ASSERT_EQ(23u, range->GetEnd());
+  ASSERT_EQ(22u, range->GetEnd());
   HBasicBlock* block = graph->GetBlocks().Get(3);
-  ASSERT_TRUE(block->GetLastInstruction()->AsReturn() != nullptr);
+  ASSERT_TRUE(block->GetLastInstruction()->IsReturn());
   ASSERT_EQ(22u, block->GetLastInstruction()->GetLifetimePosition());
   ASSERT_TRUE(range->GetNext() == nullptr);
 }
@@ -142,7 +149,7 @@
    *       22: phi
    *       24: return
    *         |
-   *       38: exit
+   *       28: exit
    */
   const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
     Instruction::CONST_4 | 0 | 0,
@@ -153,8 +160,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.
@@ -186,11 +193,11 @@
   range = interval->GetFirstRange();
   ASSERT_EQ(22u, liveness.GetInstructionFromSsaIndex(2)->GetLifetimePosition());
   ASSERT_EQ(22u, range->GetStart());
-  ASSERT_EQ(25u, range->GetEnd());
+  ASSERT_EQ(24u, range->GetEnd());
   ASSERT_TRUE(range->GetNext() == nullptr);
 }
 
-TEST(LiveRangesTest, Loop) {
+TEST(LiveRangesTest, Loop1) {
   /*
    * Test the following snippet:
    *  var a = 0;
@@ -229,8 +236,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.
@@ -255,7 +263,7 @@
   range = interval->GetFirstRange();
   // The instruction is live until the return instruction after the loop.
   ASSERT_EQ(6u, range->GetStart());
-  ASSERT_EQ(27u, range->GetEnd());
+  ASSERT_EQ(26u, range->GetEnd());
   ASSERT_TRUE(range->GetNext() == nullptr);
 
   // Test for the phi.
@@ -267,4 +275,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(26u, 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(28u, 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..246e7ef 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -16,10 +16,12 @@
 
 #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 "prepare_for_register_allocation.h"
 #include "ssa_liveness_analysis.h"
 #include "utils/arena_allocator.h"
 
@@ -49,8 +51,10 @@
   graph->BuildDominatorTree();
   graph->TransformToSSA();
   graph->FindNaturalLoops();
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  // `Inline` conditions into ifs.
+  PrepareForRegisterAllocation(graph).Run();
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
   std::ostringstream buffer;
@@ -545,4 +549,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..ed5e260 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -20,13 +20,30 @@
 
 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()),
+      output_overlaps_(true),
+      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);
+  }
 }
 
 
@@ -36,4 +53,15 @@
       : Location::RequiresRegister();
 }
 
+Location Location::ByteRegisterOrConstant(int reg, HInstruction* instruction) {
+  return instruction->IsConstant()
+      ? Location::ConstantLocation(instruction->AsConstant())
+      : Location::RegisterLocation(reg);
+}
+
+std::ostream& operator<<(std::ostream& os, const Location& location) {
+  os << location.DebugString();
+  return os;
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index aaddb09..d1555d4 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -18,14 +18,18 @@
 #define ART_COMPILER_OPTIMIZING_LOCATIONS_H_
 
 #include "base/bit_field.h"
-#include "utils/allocation.h"
+#include "base/bit_vector.h"
+#include "base/value_object.h"
+#include "utils/arena_object.h"
 #include "utils/growable_array.h"
-#include "utils/managed_register.h"
 
 namespace art {
 
 class HConstant;
 class HInstruction;
+class Location;
+
+std::ostream& operator<<(std::ostream& os, const Location& location);
 
 /**
  * A Location is an abstraction over the potential location
@@ -33,34 +37,53 @@
  */
 class Location : public ValueObject {
  public:
+  static constexpr bool kNoOutputOverlap = false;
+
   enum Kind {
     kInvalid = 0,
     kConstant = 1,
-    kStackSlot = 2,  // Word size slot.
+    kStackSlot = 2,  // 32bit stack slot.
     kDoubleStackSlot = 3,  // 64bit stack slot.
-    kRegister = 4,
+
+    kRegister = 4,  // Core register.
+
+    // We do not use the value 5 because it conflicts with kLocationConstantMask.
+    kDoNotUse5 = 5,
+
+    kFpuRegister = 6,  // Float register.
+
+    kRegisterPair = 7,  // Long register.
+
+    kFpuRegisterPair = 8,  // Double register.
+
+    // We do not use the value 9 because it conflicts with kLocationConstantMask.
+    kDoNotUse9 = 9,
+
     // On 32bits architectures, quick can pass a long where the
     // 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 = 10,
 
     // 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 = 11,
   };
 
   Location() : value_(kInvalid) {
-    // Verify that non-tagged location kinds do not interfere with kConstantTag.
-    COMPILE_ASSERT((kInvalid & kLocationTagMask) != kConstant, TagError);
-    COMPILE_ASSERT((kUnallocated & kLocationTagMask) != kConstant, TagError);
-    COMPILE_ASSERT((kStackSlot & kLocationTagMask) != kConstant, TagError);
-    COMPILE_ASSERT((kDoubleStackSlot & kLocationTagMask) != kConstant, TagError);
-    COMPILE_ASSERT((kRegister & kLocationTagMask) != kConstant, TagError);
-    COMPILE_ASSERT((kConstant & kLocationTagMask) == kConstant, TagError);
-    COMPILE_ASSERT((kQuickParameter & kLocationTagMask) == kConstant, TagError);
+    // Verify that non-constant location kinds do not interfere with kConstant.
+    static_assert((kInvalid & kLocationConstantMask) != kConstant, "TagError");
+    static_assert((kUnallocated & kLocationConstantMask) != kConstant, "TagError");
+    static_assert((kStackSlot & kLocationConstantMask) != kConstant, "TagError");
+    static_assert((kDoubleStackSlot & kLocationConstantMask) != kConstant, "TagError");
+    static_assert((kRegister & kLocationConstantMask) != kConstant, "TagError");
+    static_assert((kQuickParameter & kLocationConstantMask) != kConstant, "TagError");
+    static_assert((kFpuRegister & kLocationConstantMask) != kConstant, "TagError");
+    static_assert((kRegisterPair & kLocationConstantMask) != kConstant, "TagError");
+    static_assert((kFpuRegisterPair & kLocationConstantMask) != kConstant, "TagError");
+    static_assert((kConstant & kLocationConstantMask) == kConstant, "TagError");
 
     DCHECK(!IsValid());
   }
@@ -73,17 +96,17 @@
   }
 
   bool IsConstant() const {
-    return (value_ & kLocationTagMask) == kConstant;
+    return (value_ & kLocationConstantMask) == kConstant;
   }
 
   static Location ConstantLocation(HConstant* constant) {
     DCHECK(constant != nullptr);
-    return Location(kConstant | reinterpret_cast<uword>(constant));
+    return Location(kConstant | reinterpret_cast<uintptr_t>(constant));
   }
 
   HConstant* GetConstant() const {
     DCHECK(IsConstant());
-    return reinterpret_cast<HConstant*>(value_ & ~kLocationTagMask);
+    return reinterpret_cast<HConstant*>(value_ & ~kLocationConstantMask);
   }
 
   bool IsValid() const {
@@ -100,27 +123,80 @@
   }
 
   // Register locations.
-  static Location RegisterLocation(ManagedRegister reg) {
-    return Location(kRegister, reg.RegId());
+  static Location RegisterLocation(int reg) {
+    return Location(kRegister, reg);
+  }
+
+  static Location FpuRegisterLocation(int reg) {
+    return Location(kFpuRegister, reg);
+  }
+
+  static Location RegisterPairLocation(int low, int high) {
+    return Location(kRegisterPair, low << 16 | high);
+  }
+
+  static Location FpuRegisterPairLocation(int low, int high) {
+    return Location(kFpuRegisterPair, low << 16 | high);
   }
 
   bool IsRegister() const {
     return GetKind() == kRegister;
   }
 
-  ManagedRegister reg() const {
-    DCHECK(IsRegister());
-    return static_cast<ManagedRegister>(GetPayload());
+  bool IsFpuRegister() const {
+    return GetKind() == kFpuRegister;
   }
 
-  static uword EncodeStackIndex(intptr_t stack_index) {
+  bool IsRegisterPair() const {
+    return GetKind() == kRegisterPair;
+  }
+
+  bool IsFpuRegisterPair() const {
+    return GetKind() == kFpuRegisterPair;
+  }
+
+  int reg() const {
+    DCHECK(IsRegister() || IsFpuRegister());
+    return GetPayload();
+  }
+
+  template <typename T>
+  T As() const {
+    return static_cast<T>(reg());
+  }
+
+  template <typename T>
+  T AsRegisterPairLow() const {
+    DCHECK(IsRegisterPair());
+    return static_cast<T>(GetPayload() >> 16);
+  }
+
+  template <typename T>
+  T AsRegisterPairHigh() const {
+    DCHECK(IsRegisterPair());
+    return static_cast<T>(GetPayload() & 0xFFFF);
+  }
+
+  template <typename T>
+  T AsFpuRegisterPairLow() const {
+    DCHECK(IsFpuRegisterPair());
+    return static_cast<T>(GetPayload() >> 16);
+  }
+
+  template <typename T>
+  T AsFpuRegisterPairHigh() const {
+    DCHECK(IsFpuRegisterPair());
+    return static_cast<T>(GetPayload() & 0xFFFF);
+  }
+
+  static uintptr_t EncodeStackIndex(intptr_t stack_index) {
     DCHECK(-kStackIndexBias <= stack_index);
     DCHECK(stack_index < kStackIndexBias);
-    return static_cast<uword>(kStackIndexBias + stack_index);
+    return static_cast<uintptr_t>(kStackIndexBias + stack_index);
   }
 
   static Location StackSlot(intptr_t stack_index) {
-    uword payload = EncodeStackIndex(stack_index);
+    uintptr_t payload = EncodeStackIndex(stack_index);
     Location loc(kStackSlot, payload);
     // Ensure that sign is preserved.
     DCHECK_EQ(loc.GetStackIndex(), stack_index);
@@ -132,7 +208,7 @@
   }
 
   static Location DoubleStackSlot(intptr_t stack_index) {
-    uword payload = EncodeStackIndex(stack_index);
+    uintptr_t payload = EncodeStackIndex(stack_index);
     Location loc(kDoubleStackSlot, payload);
     // Ensure that sign is preserved.
     DCHECK_EQ(loc.GetStackIndex(), stack_index);
@@ -155,25 +231,26 @@
     return GetPayload() - kStackIndexBias + word_size;
   }
 
-  static Location QuickParameter(uint32_t parameter_index) {
-    return Location(kQuickParameter, parameter_index);
+  static Location QuickParameter(uint16_t register_index, uint16_t stack_index) {
+    return Location(kQuickParameter, register_index << 16 | stack_index);
   }
 
-  uint32_t GetQuickParameterIndex() const {
+  uint32_t GetQuickParameterRegisterIndex() const {
     DCHECK(IsQuickParameter());
-    return GetPayload();
+    return GetPayload() >> 16;
+  }
+
+  uint32_t GetQuickParameterStackIndex() const {
+    DCHECK(IsQuickParameter());
+    return GetPayload() & 0xFFFF;
   }
 
   bool IsQuickParameter() const {
     return GetKind() == kQuickParameter;
   }
 
-  arm::ArmManagedRegister AsArm() const;
-  x86::X86ManagedRegister AsX86() const;
-  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 {
@@ -189,7 +266,14 @@
       case kQuickParameter: return "Q";
       case kUnallocated: return "U";
       case kConstant: return "C";
+      case kFpuRegister: return "F";
+      case kRegisterPair: return "RP";
+      case kFpuRegisterPair: return "FP";
+      case kDoNotUse5:  // fall-through
+      case kDoNotUse9:
+        LOG(FATAL) << "Should not use this location kind";
     }
+    UNREACHABLE();
     return "?";
   }
 
@@ -197,6 +281,7 @@
   enum Policy {
     kAny,
     kRequiresRegister,
+    kRequiresFpuRegister,
     kSameAsFirstInput,
   };
 
@@ -217,7 +302,12 @@
     return UnallocatedLocation(kRequiresRegister);
   }
 
+  static Location RequiresFpuRegister() {
+    return UnallocatedLocation(kRequiresFpuRegister);
+  }
+
   static Location RegisterOrConstant(HInstruction* instruction);
+  static Location ByteRegisterOrConstant(int reg, HInstruction* instruction);
 
   // The location of the first input to the instruction will be
   // used to replace this unallocated location.
@@ -230,27 +320,27 @@
     return PolicyField::Decode(GetPayload());
   }
 
-  uword GetEncoding() const {
+  uintptr_t GetEncoding() const {
     return GetPayload();
   }
 
  private:
   // Number of bits required to encode Kind value.
   static constexpr uint32_t kBitsForKind = 4;
-  static constexpr uint32_t kBitsForPayload = kWordSize * kBitsPerByte - kBitsForKind;
-  static constexpr uword kLocationTagMask = 0x3;
+  static constexpr uint32_t kBitsForPayload = kBitsPerIntPtrT - kBitsForKind;
+  static constexpr uintptr_t kLocationConstantMask = 0x3;
 
-  explicit Location(uword value) : value_(value) {}
+  explicit Location(uintptr_t value) : value_(value) {}
 
-  Location(Kind kind, uword payload)
+  Location(Kind kind, uintptr_t payload)
       : value_(KindField::Encode(kind) | PayloadField::Encode(payload)) {}
 
-  uword GetPayload() const {
+  uintptr_t GetPayload() const {
     return PayloadField::Decode(value_);
   }
 
   typedef BitField<Kind, 0, kBitsForKind> KindField;
-  typedef BitField<uword, kBitsForKind, kBitsForPayload> PayloadField;
+  typedef BitField<uintptr_t, kBitsForKind, kBitsForPayload> PayloadField;
 
   // Layout for kUnallocated locations payload.
   typedef BitField<Policy, 0, 3> PolicyField;
@@ -262,7 +352,50 @@
   // Location either contains kind and payload fields or a tagged handle for
   // a constant locations. Values of enumeration Kind are selected in such a
   // way that none of them can be interpreted as a kConstant tag.
-  uword value_;
+  uintptr_t value_;
+};
+std::ostream& operator<<(std::ostream& os, const Location::Kind& rhs);
+std::ostream& operator<<(std::ostream& os, const Location::Policy& rhs);
+
+class RegisterSet : public ValueObject {
+ public:
+  RegisterSet() : core_registers_(0), floating_point_registers_(0) {}
+
+  void Add(Location loc) {
+    if (loc.IsRegister()) {
+      core_registers_ |= (1 << loc.reg());
+    } else {
+      DCHECK(loc.IsFpuRegister());
+      floating_point_registers_ |= (1 << loc.reg());
+    }
+  }
+
+  void Remove(Location loc) {
+    if (loc.IsRegister()) {
+      core_registers_ &= ~(1 << loc.reg());
+    } else {
+      DCHECK(loc.IsFpuRegister()) << loc;
+      floating_point_registers_ &= ~(1 << loc.reg());
+    }
+  }
+
+  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);
 };
 
 /**
@@ -273,11 +406,18 @@
  * The intent is to have the code for generating the instruction independent of
  * register allocation. A register allocator just has to provide a LocationSummary.
  */
-class LocationSummary : public ArenaObject {
+class LocationSummary : public ArenaObject<kArenaAllocMisc> {
  public:
-  explicit LocationSummary(HInstruction* instruction);
+  enum CallKind {
+    kNoCall,
+    kCallOnSlowPath,
+    kCall
+  };
+
+  LocationSummary(HInstruction* instruction, CallKind call_kind = kNoCall);
 
   void SetInAt(uint32_t at, Location location) {
+    DCHECK(inputs_.Get(at).IsUnallocated() || inputs_.Get(at).IsInvalid());
     inputs_.Put(at, location);
   }
 
@@ -289,8 +429,18 @@
     return inputs_.Size();
   }
 
-  void SetOut(Location location) {
-    output_ = Location(location);
+  void SetOut(Location location, bool overlaps = true) {
+    DCHECK(output_.IsUnallocated() || output_.IsInvalid());
+    output_overlaps_ = overlaps;
+    output_ = location;
+  }
+
+  void UpdateOut(Location location) {
+    // The only reason for updating an output is for parameters where
+    // we only know the exact stack slot after doing full register
+    // allocation.
+    DCHECK(output_.IsStackSlot() || output_.IsDoubleStackSlot());
+    output_ = location;
   }
 
   void AddTemp(Location location) {
@@ -302,6 +452,7 @@
   }
 
   void SetTempAt(uint32_t at, Location location) {
+    DCHECK(temps_.Get(at).IsUnallocated() || temps_.Get(at).IsInvalid());
     temps_.Put(at, location);
   }
 
@@ -309,13 +460,87 @@
     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_index, bool is_environment) const {
+    if (is_environment) return true;
+    if ((input_index == 0)
+        && output_.IsUnallocated()
+        && (output_.GetPolicy() == Location::kSameAsFirstInput)) {
+      return false;
+    }
+    if (inputs_.Get(input_index).IsRegister() || inputs_.Get(input_index).IsFpuRegister()) {
+      return false;
+    }
+    return true;
+  }
+
+  bool OutputOverlapsWithInputs() const {
+    return output_overlaps_;
+  }
+
  private:
   GrowableArray<Location> inputs_;
   GrowableArray<Location> temps_;
+  GrowableArray<Location> environment_;
+  // Whether the output overlaps with any of the inputs. If it overlaps, then it cannot
+  // share the same register as the inputs.
+  bool output_overlaps_;
   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_;
+
+  ART_FRIEND_TEST(RegisterAllocatorTest, ExpectedInRegisterHint);
+  ART_FRIEND_TEST(RegisterAllocatorTest, SameAsFirstInputHint);
   DISALLOW_COPY_AND_ASSIGN(LocationSummary);
 };
 
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 490d345..8cb2ef6 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() {
@@ -288,9 +308,17 @@
   return false;
 }
 
+static void UpdateInputsUsers(HInstruction* instruction) {
+  for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
+    instruction->InputAt(i)->AddUseAt(instruction, i);
+  }
+  // Environment should be created later.
+  DCHECK(!instruction->HasEnvironment());
+}
+
 void HBasicBlock::InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor) {
-  DCHECK(cursor->AsPhi() == nullptr);
-  DCHECK(instruction->AsPhi() == nullptr);
+  DCHECK(!cursor->IsPhi());
+  DCHECK(!instruction->IsPhi());
   DCHECK_EQ(instruction->GetId(), -1);
   DCHECK_NE(cursor->GetId(), -1);
   DCHECK_EQ(cursor->GetBlock(), this);
@@ -305,6 +333,15 @@
   }
   instruction->SetBlock(this);
   instruction->SetId(GetGraph()->GetNextInstructionId());
+  UpdateInputsUsers(instruction);
+}
+
+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,
@@ -314,6 +351,7 @@
   DCHECK_EQ(instruction->GetId(), -1);
   instruction->SetBlock(block);
   instruction->SetId(block->GetGraph()->GetNextInstructionId());
+  UpdateInputsUsers(instruction);
   instruction_list->AddInstruction(instruction);
 }
 
@@ -325,6 +363,25 @@
   Add(&phis_, this, phi);
 }
 
+void HBasicBlock::InsertPhiAfter(HPhi* phi, HPhi* cursor) {
+  DCHECK_EQ(phi->GetId(), -1);
+  DCHECK_NE(cursor->GetId(), -1);
+  DCHECK_EQ(cursor->GetBlock(), this);
+  if (cursor->next_ == nullptr) {
+    cursor->next_ = phi;
+    phi->previous_ = cursor;
+    DCHECK(phi->next_ == nullptr);
+  } else {
+    phi->next_ = cursor->next_;
+    phi->previous_ = cursor;
+    cursor->next_ = phi;
+    phi->next_->previous_ = phi;
+  }
+  phi->SetBlock(this);
+  phi->SetId(GetGraph()->GetNextInstructionId());
+  UpdateInputsUsers(phi);
+}
+
 static void Remove(HInstructionList* instruction_list,
                    HBasicBlock* block,
                    HInstruction* instruction) {
@@ -337,6 +394,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 +414,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 +433,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);
@@ -372,9 +450,6 @@
     instruction->previous_ = last_instruction_;
     last_instruction_ = instruction;
   }
-  for (size_t i = 0; i < instruction->InputCount(); i++) {
-    instruction->InputAt(i)->AddUseAt(instruction, i);
-  }
 }
 
 void HInstructionList::RemoveInstruction(HInstruction* instruction) {
@@ -392,6 +467,67 @@
   }
 }
 
+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::StrictlyDominates(HInstruction* other_instruction) const {
+  if (other_instruction == this) {
+    // An instruction does not strictly dominate itself.
+    return false;
+  }
+  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,13 +550,23 @@
   env_uses_ = nullptr;
 }
 
+void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) {
+  InputAt(index)->RemoveUser(this, index);
+  SetRawInputAt(index, replacement);
+  replacement->AddUseAt(this, index);
+}
+
+size_t HInstruction::EnvironmentSize() const {
+  return HasEnvironment() ? environment_->Size() : 0;
+}
+
 void HPhi::AddInput(HInstruction* input) {
   DCHECK(input->GetBlock() != nullptr);
   inputs_.Add(input);
   input->AddUseAt(this, inputs_.Size() - 1);
 }
 
-#define DEFINE_ACCEPT(name)                                                    \
+#define DEFINE_ACCEPT(name, super)                                             \
 void H##name::Accept(HGraphVisitor* visitor) {                                 \
   visitor->Visit##name(this);                                                  \
 }
@@ -436,6 +582,12 @@
   }
 }
 
+void HGraphVisitor::VisitReversePostOrder() {
+  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    VisitBasicBlock(it.Current());
+  }
+}
+
 void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) {
   for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
     it.Current()->Accept(this);
@@ -445,23 +597,66 @@
   }
 }
 
+HConstant* HUnaryOperation::TryStaticEvaluation() const {
+  if (GetInput()->IsIntConstant()) {
+    int32_t value = Evaluate(GetInput()->AsIntConstant()->GetValue());
+    return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value);
+  } else if (GetInput()->IsLongConstant()) {
+    // TODO: Implement static evaluation of long unary operations.
+    //
+    // Do not exit with a fatal condition here.  Instead, simply
+    // return `nullptr' to notify the caller that this instruction
+    // cannot (yet) be statically evaluated.
+    return nullptr;
+  }
+  return nullptr;
+}
 
-bool HCondition::NeedsMaterialization() const {
-  if (!HasOnlyOneUse()) {
-    return true;
+HConstant* HBinaryOperation::TryStaticEvaluation() const {
+  if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) {
+    int32_t value = Evaluate(GetLeft()->AsIntConstant()->GetValue(),
+                             GetRight()->AsIntConstant()->GetValue());
+    return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value);
+  } else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) {
+    int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(),
+                             GetRight()->AsLongConstant()->GetValue());
+    return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
   }
-  HUseListNode<HInstruction>* uses = GetUses();
-  HInstruction* user = uses->GetUser();
-  if (!user->IsIf()) {
-    return true;
-  }
+  return nullptr;
+}
 
-  // TODO: should we allow intervening instructions with no side-effect between this condition
-  // and the If instruction?
-  if (GetNext() != user) {
-    return true;
+bool HCondition::IsBeforeWhenDisregardMoves(HIf* if_) const {
+  HInstruction* previous = if_->GetPrevious();
+  while (previous != nullptr && previous->IsParallelMove()) {
+    previous = previous->GetPrevious();
   }
-  return false;
+  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;
+}
+
+std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs) {
+#define DECLARE_CASE(type, super) case HInstruction::k##type: os << #type; break;
+  switch (rhs) {
+    FOR_EACH_INSTRUCTION(DECLARE_CASE)
+    default:
+      os << "Unknown instruction kind " << static_cast<int>(rhs);
+      break;
+  }
+#undef DECLARE_CASE
+  return os;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index cb3dd0f..2dab605 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -20,7 +20,7 @@
 #include "locations.h"
 #include "offsets.h"
 #include "primitive.h"
-#include "utils/allocation.h"
+#include "utils/arena_object.h"
 #include "utils/arena_bit_vector.h"
 #include "utils/growable_array.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_;
@@ -68,12 +79,14 @@
 };
 
 // Control-flow graph of a method. Contains a list of basic blocks.
-class HGraph : public ArenaObject {
+class HGraph : public ArenaObject<kArenaAllocMisc> {
  public:
   explicit HGraph(ArenaAllocator* arena)
       : arena_(arena),
         blocks_(arena, kDefaultNumberOfBlocks),
         reverse_post_order_(arena, kDefaultNumberOfBlocks),
+        entry_block_(nullptr),
+        exit_block_(nullptr),
         maximum_number_of_out_vregs_(0),
         number_of_vregs_(0),
         number_of_in_vregs_(0),
@@ -82,6 +95,7 @@
 
   ArenaAllocator* GetArena() const { return arena_; }
   const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; }
+  HBasicBlock* GetBlock(size_t id) const { return blocks_.Get(id); }
 
   HBasicBlock* GetEntryBlock() const { return entry_block_; }
   HBasicBlock* GetExitBlock() const { return exit_block_; }
@@ -187,17 +201,23 @@
   DISALLOW_COPY_AND_ASSIGN(HGraph);
 };
 
-class HLoopInformation : public ArenaObject {
+class HLoopInformation : public ArenaObject<kArenaAllocMisc> {
  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 +266,7 @@
   void PopulateRecursive(HBasicBlock* block);
 
   HBasicBlock* header_;
+  HSuspendCheck* suspend_check_;
   GrowableArray<HBasicBlock*> back_edges_;
   ArenaBitVector blocks_;
 
@@ -253,21 +274,26 @@
 };
 
 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 {
+
+class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
  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) {}
+        lifetime_end_(kNoLifetime),
+        is_catch_block_(false) {}
 
   const GrowableArray<HBasicBlock*>& GetPredecessors() const {
     return predecessors_;
@@ -277,6 +303,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 +330,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 +370,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,13 +398,23 @@
   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 InsertPhiAfter(HPhi* instruction, HPhi* cursor);
   void RemovePhi(HPhi* phi);
 
   bool IsLoopHeader() const {
     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 +449,11 @@
   void SetLifetimeStart(size_t start) { lifetime_start_ = start; }
   void SetLifetimeEnd(size_t end) { lifetime_end_ = end; }
 
+  uint32_t GetDexPc() const { return dex_pc_; }
+
+  bool IsCatchBlock() const { return is_catch_block_; }
+  void SetIsCatchBlock() { is_catch_block_ = true; }
+
  private:
   HGraph* const graph_;
   GrowableArray<HBasicBlock*> predecessors_;
@@ -401,64 +462,95 @@
   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_;
+  bool is_catch_block_;
 
   DISALLOW_COPY_AND_ASSIGN(HBasicBlock);
 };
 
-#define FOR_EACH_CONCRETE_INSTRUCTION(M)                   \
-  M(Add)                                                   \
-  M(Condition)                                             \
-  M(Equal)                                                 \
-  M(NotEqual)                                              \
-  M(LessThan)                                              \
-  M(LessThanOrEqual)                                       \
-  M(GreaterThan)                                           \
-  M(GreaterThanOrEqual)                                    \
-  M(Exit)                                                  \
-  M(Goto)                                                  \
-  M(If)                                                    \
-  M(IntConstant)                                           \
-  M(InvokeStatic)                                          \
-  M(LoadLocal)                                             \
-  M(Local)                                                 \
-  M(LongConstant)                                          \
-  M(NewInstance)                                           \
-  M(Not)                                                   \
-  M(ParameterValue)                                        \
-  M(ParallelMove)                                          \
-  M(Phi)                                                   \
-  M(Return)                                                \
-  M(ReturnVoid)                                            \
-  M(StoreLocal)                                            \
-  M(Sub)                                                   \
-  M(Compare)                                               \
-  M(InstanceFieldGet)                                      \
-  M(InstanceFieldSet)                                      \
-  M(ArrayGet)                                              \
-  M(ArraySet)                                              \
-  M(ArrayLength)                                           \
-  M(BoundsCheck)                                           \
-  M(NullCheck)                                             \
-  M(Temporary)                                             \
+#define FOR_EACH_CONCRETE_INSTRUCTION(M)                                \
+  M(Add, BinaryOperation)                                               \
+  M(ArrayGet, Instruction)                                              \
+  M(ArrayLength, Instruction)                                           \
+  M(ArraySet, Instruction)                                              \
+  M(BoundsCheck, Instruction)                                           \
+  M(ClinitCheck, Instruction)                                           \
+  M(Compare, BinaryOperation)                                           \
+  M(Condition, BinaryOperation)                                         \
+  M(Div, BinaryOperation)                                               \
+  M(DivZeroCheck, Instruction)                                          \
+  M(DoubleConstant, Constant)                                           \
+  M(Equal, Condition)                                                   \
+  M(Exit, Instruction)                                                  \
+  M(FloatConstant, Constant)                                            \
+  M(Goto, Instruction)                                                  \
+  M(GreaterThan, Condition)                                             \
+  M(GreaterThanOrEqual, Condition)                                      \
+  M(If, Instruction)                                                    \
+  M(InstanceFieldGet, Instruction)                                      \
+  M(InstanceFieldSet, Instruction)                                      \
+  M(IntConstant, Constant)                                              \
+  M(InvokeInterface, Invoke)                                            \
+  M(InvokeStatic, Invoke)                                               \
+  M(InvokeVirtual, Invoke)                                              \
+  M(LessThan, Condition)                                                \
+  M(LessThanOrEqual, Condition)                                         \
+  M(LoadClass, Instruction)                                             \
+  M(LoadException, Instruction)                                         \
+  M(LoadLocal, Instruction)                                             \
+  M(LoadString, Instruction)                                            \
+  M(Local, Instruction)                                                 \
+  M(LongConstant, Constant)                                             \
+  M(Mul, BinaryOperation)                                               \
+  M(Neg, UnaryOperation)                                                \
+  M(NewArray, Instruction)                                              \
+  M(NewInstance, Instruction)                                           \
+  M(Not, UnaryOperation)                                                \
+  M(NotEqual, Condition)                                                \
+  M(NullCheck, Instruction)                                             \
+  M(ParallelMove, Instruction)                                          \
+  M(ParameterValue, Instruction)                                        \
+  M(Phi, Instruction)                                                   \
+  M(Return, Instruction)                                                \
+  M(ReturnVoid, Instruction)                                            \
+  M(StaticFieldGet, Instruction)                                        \
+  M(StaticFieldSet, Instruction)                                        \
+  M(StoreLocal, Instruction)                                            \
+  M(Sub, BinaryOperation)                                               \
+  M(SuspendCheck, Instruction)                                          \
+  M(Temporary, Instruction)                                             \
+  M(Throw, Instruction)                                                 \
+  M(TypeCheck, Instruction)                                             \
+  M(TypeConversion, Instruction)                                        \
 
-#define FOR_EACH_INSTRUCTION(M)                            \
-  FOR_EACH_CONCRETE_INSTRUCTION(M)                         \
-  M(Constant)
+#define FOR_EACH_INSTRUCTION(M)                                         \
+  FOR_EACH_CONCRETE_INSTRUCTION(M)                                      \
+  M(Constant, Instruction)                                              \
+  M(UnaryOperation, Instruction)                                        \
+  M(BinaryOperation, Instruction)                                       \
+  M(Invoke, Instruction)
 
-#define FORWARD_DECLARATION(type) class H##type;
+#define FORWARD_DECLARATION(type, super) 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 {
+class HUseListNode : public ArenaObject<kArenaAllocMisc> {
  public:
   HUseListNode(T* user, size_t index, HUseListNode* tail)
       : user_(user), index_(index), tail_(tail) {}
@@ -477,9 +569,72 @@
   DISALLOW_COPY_AND_ASSIGN(HUseListNode);
 };
 
-class HInstruction : public ArenaObject {
+// Represents the side effects an instruction may have.
+class SideEffects : public ValueObject {
  public:
-  HInstruction()
+  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<kArenaAllocMisc> {
+ public:
+  explicit HInstruction(SideEffects side_effects)
       : previous_(nullptr),
         next_(nullptr),
         block_(nullptr),
@@ -490,10 +645,17 @@
         environment_(nullptr),
         locations_(nullptr),
         live_interval_(nullptr),
-        lifetime_position_(kNoLifetime) {}
+        lifetime_position_(kNoLifetime),
+        side_effects_(side_effects) {}
 
   virtual ~HInstruction() {}
 
+#define DECLARE_KIND(type, super) k##type,
+  enum InstructionKind {
+    FOR_EACH_INSTRUCTION(DECLARE_KIND)
+  };
+#undef DECLARE_KIND
+
   HInstruction* GetNext() const { return next_; }
   HInstruction* GetPrevious() const { return previous_; }
 
@@ -501,8 +663,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 +676,21 @@
 
   virtual bool NeedsEnvironment() const { return false; }
   virtual bool IsControlFlow() const { return false; }
+  virtual bool CanThrow() 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 +709,11 @@
     return result;
   }
 
+  // Does this instruction strictly dominate `other_instruction`?
+  // Returns false if this instruction and `other_instruction` are the same.
+  // Aborts if this instruction and `other_instruction` are both phis.
+  bool StrictlyDominates(HInstruction* other_instruction) const;
+
   int GetId() const { return id_; }
   void SetId(int id) { id_ = id; }
 
@@ -553,22 +725,62 @@
   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; }
 
   void ReplaceWith(HInstruction* instruction);
+  void ReplaceInput(HInstruction* replacement, size_t index);
 
   bool HasOnlyOneUse() const {
     return uses_ != nullptr && uses_->GetTail() == nullptr;
   }
 
-#define INSTRUCTION_TYPE_CHECK(type)                                           \
-  bool Is##type() { return (As##type() != nullptr); }                          \
+#define INSTRUCTION_TYPE_CHECK(type, super)                                    \
+  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 {
+    UNUSED(other);
+    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 {
+    UNUSED(other);
+    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 +794,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 +806,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,11 +820,14 @@
   // order of blocks where this instruction's live interval start.
   size_t lifetime_position_;
 
+  const SideEffects side_effects_;
+
   friend class HBasicBlock;
   friend class HInstructionList;
 
   DISALLOW_COPY_AND_ASSIGN(HInstruction);
 };
+std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs);
 
 template<typename T>
 class HUseIterator : public ValueObject {
@@ -636,7 +853,7 @@
 };
 
 // A HEnvironment object contains the values of virtual registers at a given location.
-class HEnvironment : public ArenaObject {
+class HEnvironment : public ArenaObject<kArenaAllocMisc> {
  public:
   HEnvironment(ArenaAllocator* arena, size_t number_of_vregs) : vregs_(arena, number_of_vregs) {
     vregs_.SetSize(number_of_vregs);
@@ -659,10 +876,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_;
 
@@ -762,21 +985,22 @@
  public:
   intptr_t length() const { return 0; }
   const T& operator[](intptr_t i) const {
+    UNUSED(i);
     LOG(FATAL) << "Unreachable";
-    static T sentinel = 0;
-    return sentinel;
+    UNREACHABLE();
   }
   T& operator[](intptr_t i) {
+    UNUSED(i);
     LOG(FATAL) << "Unreachable";
-    static T sentinel = 0;
-    return sentinel;
+    UNREACHABLE();
   }
 };
 
 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,22 +1018,23 @@
 };
 
 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_; }
 
- private:
-  const Primitive::Type type_;
+ protected:
+  Primitive::Type type_;
 };
 
 // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow
 // 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 +1048,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);
   }
 
@@ -836,11 +1061,11 @@
 };
 
 // The exit instruction is the only instruction of the exit block.
-// Instructions aborting the method (HTrow and HReturn) must branch to the
+// Instructions aborting the method (HThrow and HReturn) must branch to the
 // exit block.
 class HExit : public HTemplateInstruction<0> {
  public:
-  HExit() {}
+  HExit() : HTemplateInstruction(SideEffects::None()) {}
 
   virtual bool IsControlFlow() const { return true; }
 
@@ -853,14 +1078,14 @@
 // Jumps from one block to another.
 class HGoto : public HTemplateInstruction<0> {
  public:
-  HGoto() {}
+  HGoto() : HTemplateInstruction(SideEffects::None()) {}
+
+  bool IsControlFlow() const OVERRIDE { return true; }
 
   HBasicBlock* GetSuccessor() const {
     return GetBlock()->GetSuccessors().Get(0);
   }
 
-  virtual bool IsControlFlow() const { return true; }
-
   DECLARE_INSTRUCTION(Goto);
 
  private:
@@ -872,10 +1097,12 @@
 // two successors.
 class HIf : public HTemplateInstruction<1> {
  public:
-  explicit HIf(HInstruction* input) {
+  explicit HIf(HInstruction* input) : HTemplateInstruction(SideEffects::None()) {
     SetRawInputAt(0, input);
   }
 
+  bool IsControlFlow() const OVERRIDE { return true; }
+
   HBasicBlock* IfTrueSuccessor() const {
     return GetBlock()->GetSuccessors().Get(0);
   }
@@ -884,8 +1111,6 @@
     return GetBlock()->GetSuccessors().Get(1);
   }
 
-  virtual bool IsControlFlow() const { return true; }
-
   DECLARE_INSTRUCTION(If);
 
   virtual bool IsIfInstruction() const { return true; }
@@ -894,11 +1119,42 @@
   DISALLOW_COPY_AND_ASSIGN(HIf);
 };
 
+class HUnaryOperation : public HExpression<1> {
+ public:
+  HUnaryOperation(Primitive::Type result_type, HInstruction* input)
+      : HExpression(result_type, SideEffects::None()) {
+    SetRawInputAt(0, input);
+  }
+
+  HInstruction* GetInput() const { return InputAt(0); }
+  Primitive::Type GetResultType() const { return GetType(); }
+
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    UNUSED(other);
+    return true;
+  }
+
+  // Try to statically evaluate `operation` and return a HConstant
+  // containing the result of this evaluation.  If `operation` cannot
+  // be evaluated as a constant, return nullptr.
+  HConstant* TryStaticEvaluation() const;
+
+  // Apply this operation to `x`.
+  virtual int32_t Evaluate(int32_t x) const = 0;
+  virtual int64_t Evaluate(int64_t x) const = 0;
+
+  DECLARE_INSTRUCTION(UnaryOperation);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HUnaryOperation);
+};
+
 class HBinaryOperation : public HExpression<2> {
  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 +1165,23 @@
 
   virtual bool IsCommutative() { return false; }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    UNUSED(other);
+    return true;
+  }
+
+  // Try to statically evaluate `operation` and return a HConstant
+  // containing the result of this evaluation.  If `operation` cannot
+  // be evaluated as a constant, return nullptr.
+  HConstant* TryStaticEvaluation() 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);
 };
@@ -916,16 +1189,27 @@
 class HCondition : public HBinaryOperation {
  public:
   HCondition(HInstruction* first, HInstruction* second)
-      : HBinaryOperation(Primitive::kPrimBoolean, first, second) {}
+      : HBinaryOperation(Primitive::kPrimBoolean, first, second),
+        needs_materialization_(true) {}
 
   virtual bool IsCommutative() { return true; }
-  bool NeedsMaterialization() const;
+
+  bool NeedsMaterialization() const { return needs_materialization_; }
+  void ClearNeedsMaterialization() { needs_materialization_ = false; }
+
+  // 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;
 
  private:
+  // For register allocation purposes, returns whether this instruction needs to be
+  // materialized (that is, not just be in the processor flags).
+  bool needs_materialization_;
+
   DISALLOW_COPY_AND_ASSIGN(HCondition);
 };
 
@@ -935,6 +1219,13 @@
   HEqual(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
+    return x == y ? 1 : 0;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE {
+    return x == y ? 1 : 0;
+  }
+
   DECLARE_INSTRUCTION(Equal);
 
   virtual IfCondition GetCondition() const {
@@ -950,6 +1241,13 @@
   HNotEqual(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
+    return x != y ? 1 : 0;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE {
+    return x != y ? 1 : 0;
+  }
+
   DECLARE_INSTRUCTION(NotEqual);
 
   virtual IfCondition GetCondition() const {
@@ -965,6 +1263,13 @@
   HLessThan(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
+    return x < y ? 1 : 0;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE {
+    return x < y ? 1 : 0;
+  }
+
   DECLARE_INSTRUCTION(LessThan);
 
   virtual IfCondition GetCondition() const {
@@ -980,6 +1285,13 @@
   HLessThanOrEqual(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
+    return x <= y ? 1 : 0;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE {
+    return x <= y ? 1 : 0;
+  }
+
   DECLARE_INSTRUCTION(LessThanOrEqual);
 
   virtual IfCondition GetCondition() const {
@@ -995,6 +1307,13 @@
   HGreaterThan(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
+    return x > y ? 1 : 0;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE {
+    return x > y ? 1 : 0;
+  }
+
   DECLARE_INSTRUCTION(GreaterThan);
 
   virtual IfCondition GetCondition() const {
@@ -1010,6 +1329,13 @@
   HGreaterThanOrEqual(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
+    return x >= y ? 1 : 0;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE {
+    return x >= y ? 1 : 0;
+  }
+
   DECLARE_INSTRUCTION(GreaterThanOrEqual);
 
   virtual IfCondition GetCondition() const {
@@ -1031,6 +1357,19 @@
     DCHECK_EQ(type, second->GetType());
   }
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
+    return
+      x == y ? 0 :
+      x > y ? 1 :
+      -1;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE {
+    return
+      x == y ? 0 :
+      x > y ? 1 :
+      -1;
+  }
+
   DECLARE_INSTRUCTION(Compare);
 
  private:
@@ -1040,7 +1379,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 +1396,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 +1413,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 +1428,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);
 
@@ -1095,6 +1438,48 @@
   DISALLOW_COPY_AND_ASSIGN(HConstant);
 };
 
+class HFloatConstant : public HConstant {
+ public:
+  explicit HFloatConstant(float value) : HConstant(Primitive::kPrimFloat), value_(value) {}
+
+  float GetValue() const { return value_; }
+
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    return bit_cast<float, int32_t>(other->AsFloatConstant()->value_) ==
+        bit_cast<float, int32_t>(value_);
+  }
+
+  virtual size_t ComputeHashCode() const { return static_cast<size_t>(GetValue()); }
+
+  DECLARE_INSTRUCTION(FloatConstant);
+
+ private:
+  const float value_;
+
+  DISALLOW_COPY_AND_ASSIGN(HFloatConstant);
+};
+
+class HDoubleConstant : public HConstant {
+ public:
+  explicit HDoubleConstant(double value) : HConstant(Primitive::kPrimDouble), value_(value) {}
+
+  double GetValue() const { return value_; }
+
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    return bit_cast<double, int64_t>(other->AsDoubleConstant()->value_) ==
+        bit_cast<double, int64_t>(value_);
+  }
+
+  virtual size_t ComputeHashCode() const { return static_cast<size_t>(GetValue()); }
+
+  DECLARE_INSTRUCTION(DoubleConstant);
+
+ private:
+  const double value_;
+
+  DISALLOW_COPY_AND_ASSIGN(HDoubleConstant);
+};
+
 // Constants of the type int. Those can be from Dex instructions, or
 // synthesized (for example with the if-eqz instruction).
 class HIntConstant : public HConstant {
@@ -1103,6 +1488,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 +1508,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 +1528,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);
@@ -1156,6 +1554,8 @@
 
   uint32_t GetDexPc() const { return dex_pc_; }
 
+  DECLARE_INSTRUCTION(Invoke);
+
  protected:
   GrowableArray<HInstruction*> inputs_;
   const Primitive::Type return_type_;
@@ -1185,10 +1585,56 @@
   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 HInvokeInterface : public HInvoke {
+ public:
+  HInvokeInterface(ArenaAllocator* arena,
+                   uint32_t number_of_arguments,
+                   Primitive::Type return_type,
+                   uint32_t dex_pc,
+                   uint32_t dex_method_index,
+                   uint32_t imt_index)
+      : HInvoke(arena, number_of_arguments, return_type, dex_pc),
+        dex_method_index_(dex_method_index),
+        imt_index_(imt_index) {}
+
+  uint32_t GetImtIndex() const { return imt_index_; }
+  uint32_t GetDexMethodIndex() const { return dex_method_index_; }
+
+  DECLARE_INSTRUCTION(InvokeInterface);
+
+ private:
+  const uint32_t dex_method_index_;
+  const uint32_t imt_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HInvokeInterface);
+};
+
 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_; }
@@ -1205,6 +1651,44 @@
   DISALLOW_COPY_AND_ASSIGN(HNewInstance);
 };
 
+class HNeg : public HUnaryOperation {
+ public:
+  explicit HNeg(Primitive::Type result_type, HInstruction* input)
+      : HUnaryOperation(result_type, input) {}
+
+  virtual int32_t Evaluate(int32_t x) const OVERRIDE { return -x; }
+  virtual int64_t Evaluate(int64_t x) const OVERRIDE { return -x; }
+
+  DECLARE_INSTRUCTION(Neg);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HNeg);
+};
+
+class HNewArray : public HExpression<1> {
+ public:
+  HNewArray(HInstruction* length, uint32_t dex_pc, uint16_t type_index)
+      : HExpression(Primitive::kPrimNot, SideEffects::None()),
+        dex_pc_(dex_pc),
+        type_index_(type_index) {
+    SetRawInputAt(0, length);
+  }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+  uint16_t GetTypeIndex() const { return type_index_; }
+
+  // Calls runtime so needs an environment.
+  virtual bool NeedsEnvironment() const { return true; }
+
+  DECLARE_INSTRUCTION(NewArray);
+
+ private:
+  const uint32_t dex_pc_;
+  const uint16_t type_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HNewArray);
+};
+
 class HAdd : public HBinaryOperation {
  public:
   HAdd(Primitive::Type result_type, HInstruction* left, HInstruction* right)
@@ -1212,6 +1696,13 @@
 
   virtual bool IsCommutative() { return true; }
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
+    return x + y;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE {
+    return x + y;
+  }
+
   DECLARE_INSTRUCTION(Add);
 
  private:
@@ -1223,7 +1714,12 @@
   HSub(Primitive::Type result_type, HInstruction* left, HInstruction* right)
       : HBinaryOperation(result_type, left, right) {}
 
-  virtual bool IsCommutative() { return false; }
+  virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
+    return x - y;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE {
+    return x - y;
+  }
 
   DECLARE_INSTRUCTION(Sub);
 
@@ -1231,12 +1727,74 @@
   DISALLOW_COPY_AND_ASSIGN(HSub);
 };
 
+class HMul : public HBinaryOperation {
+ public:
+  HMul(Primitive::Type result_type, HInstruction* left, HInstruction* right)
+      : HBinaryOperation(result_type, left, right) {}
+
+  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(Mul);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HMul);
+};
+
+class HDiv : public HBinaryOperation {
+ public:
+  HDiv(Primitive::Type result_type, HInstruction* left, HInstruction* right)
+      : HBinaryOperation(result_type, left, right) {}
+
+  virtual int32_t Evaluate(int32_t x, int32_t y) const {
+    // Our graph structure ensures we never have 0 for `y` during constant folding.
+    DCHECK_NE(y, 0);
+    // Special case -1 to avoid getting a SIGFPE on x86.
+    return (y == -1) ? -x : x / y;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const { return x / y; }
+
+  DECLARE_INSTRUCTION(Div);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HDiv);
+};
+
+class HDivZeroCheck : public HExpression<1> {
+ public:
+  HDivZeroCheck(HInstruction* value, uint32_t dex_pc)
+      : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
+    SetRawInputAt(0, value);
+  }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+    UNUSED(other);
+    return true;
+  }
+
+  bool NeedsEnvironment() const OVERRIDE { return true; }
+  bool CanThrow() const OVERRIDE { return true; }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+
+  DECLARE_INSTRUCTION(DivZeroCheck);
+
+ private:
+  const uint32_t dex_pc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck);
+};
+
 // The value of a parameter in this method. Its location depends on
 // the calling convention.
 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_; }
 
@@ -1250,22 +1808,53 @@
   DISALLOW_COPY_AND_ASSIGN(HParameterValue);
 };
 
-class HNot : public HExpression<1> {
+class HNot : public HUnaryOperation {
  public:
-  explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean) {
-    SetRawInputAt(0, input);
+  explicit HNot(Primitive::Type result_type, HInstruction* input)
+      : HUnaryOperation(result_type, input) {}
+
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    UNUSED(other);
+    return true;
   }
 
+  virtual int32_t Evaluate(int32_t x) const OVERRIDE { return ~x; }
+  virtual int64_t Evaluate(int64_t x) const OVERRIDE { return ~x; }
+
   DECLARE_INSTRUCTION(Not);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(HNot);
 };
 
+class HTypeConversion : public HExpression<1> {
+ public:
+  // Instantiate a type conversion of `input` to `result_type`.
+  HTypeConversion(Primitive::Type result_type, HInstruction* input)
+      : HExpression(result_type, SideEffects::None()) {
+    SetRawInputAt(0, input);
+    DCHECK_NE(input->GetType(), result_type);
+  }
+
+  HInstruction* GetInput() const { return InputAt(0); }
+  Primitive::Type GetInputType() const { return GetInput()->GetType(); }
+  Primitive::Type GetResultType() const { return GetType(); }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+  bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
+
+  DECLARE_INSTRUCTION(TypeConversion);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HTypeConversion);
+};
+
 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,12 +1894,20 @@
 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 {
+    UNUSED(other);
+    return true;
+  }
+
   virtual bool NeedsEnvironment() const { return true; }
 
+  virtual bool CanThrow() const { return true; }
+
   uint32_t GetDexPc() const { return dex_pc_; }
 
   DECLARE_INSTRUCTION(NullCheck);
@@ -1323,13 +1920,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 +1936,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 +1966,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 +1988,18 @@
 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 {
+    UNUSED(other);
+    return true;
+  }
+  void SetType(Primitive::Type type) { type_ = type; }
+
   DECLARE_INSTRUCTION(ArrayGet);
 
  private:
@@ -1390,7 +2011,11 @@
   HArraySet(HInstruction* array,
             HInstruction* index,
             HInstruction* value,
-            uint32_t dex_pc) : dex_pc_(dex_pc) {
+            Primitive::Type expected_component_type,
+            uint32_t dex_pc)
+      : HTemplateInstruction(SideEffects::ChangesSomething()),
+        dex_pc_(dex_pc),
+        expected_component_type_(expected_component_type) {
     SetRawInputAt(0, array);
     SetRawInputAt(1, index);
     SetRawInputAt(2, value);
@@ -1404,20 +2029,43 @@
 
   uint32_t GetDexPc() const { return dex_pc_; }
 
+  HInstruction* GetValue() const { return InputAt(2); }
+
+  Primitive::Type GetComponentType() const {
+    // The Dex format does not type floating point index operations. Since the
+    // `expected_component_type_` is set during building and can therefore not
+    // be correct, we also check what is the value type. If it is a floating
+    // point type, we must use that type.
+    Primitive::Type value_type = GetValue()->GetType();
+    return ((value_type == Primitive::kPrimFloat) || (value_type == Primitive::kPrimDouble))
+        ? value_type
+        : expected_component_type_;
+  }
+
   DECLARE_INSTRUCTION(ArraySet);
 
  private:
   const uint32_t dex_pc_;
+  const Primitive::Type expected_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 {
+    UNUSED(other);
+    return true;
+  }
+
   DECLARE_INSTRUCTION(ArrayLength);
 
  private:
@@ -1427,14 +2075,22 @@
 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 {
+    UNUSED(other);
+    return true;
+  }
+
   virtual bool NeedsEnvironment() const { return true; }
 
+  virtual bool CanThrow() const { return true; }
+
   uint32_t GetDexPc() const { return dex_pc_; }
 
   DECLARE_INSTRUCTION(BoundsCheck);
@@ -1454,10 +2110,12 @@
  */
 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_; }
 
+  Primitive::Type GetType() const OVERRIDE { return GetPrevious()->GetType(); }
+
   DECLARE_INSTRUCTION(Temporary);
 
  private:
@@ -1466,10 +2124,276 @@
   DISALLOW_COPY_AND_ASSIGN(HTemporary);
 };
 
-class MoveOperands : public ArenaObject {
+class HSuspendCheck : public HTemplateInstruction<0> {
  public:
-  MoveOperands(Location source, Location destination)
-      : source_(source), destination_(destination) {}
+  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);
+};
+
+/**
+ * Instruction to load a Class object.
+ */
+class HLoadClass : public HExpression<0> {
+ public:
+  HLoadClass(uint16_t type_index,
+             bool is_referrers_class,
+             uint32_t dex_pc)
+      : HExpression(Primitive::kPrimNot, SideEffects::None()),
+        type_index_(type_index),
+        is_referrers_class_(is_referrers_class),
+        dex_pc_(dex_pc),
+        generate_clinit_check_(false) {}
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+    return other->AsLoadClass()->type_index_ == type_index_;
+  }
+
+  size_t ComputeHashCode() const OVERRIDE { return type_index_; }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+  uint16_t GetTypeIndex() const { return type_index_; }
+  bool IsReferrersClass() const { return is_referrers_class_; }
+
+  bool NeedsEnvironment() const OVERRIDE {
+    // Will call runtime and load the class if the class is not loaded yet.
+    // TODO: finer grain decision.
+    return !is_referrers_class_;
+  }
+
+  bool MustGenerateClinitCheck() const {
+    return generate_clinit_check_;
+  }
+
+  void SetMustGenerateClinitCheck() {
+    generate_clinit_check_ = true;
+  }
+
+  bool CanCallRuntime() const {
+    return MustGenerateClinitCheck() || !is_referrers_class_;
+  }
+
+  DECLARE_INSTRUCTION(LoadClass);
+
+ private:
+  const uint16_t type_index_;
+  const bool is_referrers_class_;
+  const uint32_t dex_pc_;
+  // Whether this instruction must generate the initialization check.
+  // Used for code generation.
+  bool generate_clinit_check_;
+
+  DISALLOW_COPY_AND_ASSIGN(HLoadClass);
+};
+
+class HLoadString : public HExpression<0> {
+ public:
+  HLoadString(uint32_t string_index, uint32_t dex_pc)
+      : HExpression(Primitive::kPrimNot, SideEffects::None()),
+        string_index_(string_index),
+        dex_pc_(dex_pc) {}
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+    return other->AsLoadString()->string_index_ == string_index_;
+  }
+
+  size_t ComputeHashCode() const OVERRIDE { return string_index_; }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+  uint32_t GetStringIndex() const { return string_index_; }
+
+  // TODO: Can we deopt or debug when we resolve a string?
+  bool NeedsEnvironment() const OVERRIDE { return false; }
+
+  DECLARE_INSTRUCTION(LoadString);
+
+ private:
+  const uint32_t string_index_;
+  const uint32_t dex_pc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HLoadString);
+};
+
+// TODO: Pass this check to HInvokeStatic nodes.
+/**
+ * Performs an initialization check on its Class object input.
+ */
+class HClinitCheck : public HExpression<1> {
+ public:
+  explicit HClinitCheck(HLoadClass* constant, uint32_t dex_pc)
+      : HExpression(Primitive::kPrimNot, SideEffects::All()),
+        dex_pc_(dex_pc) {
+    SetRawInputAt(0, constant);
+  }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+  bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+    UNUSED(other);
+    return true;
+  }
+
+  bool NeedsEnvironment() const OVERRIDE {
+    // May call runtime to initialize the class.
+    return true;
+  }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+
+  HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); }
+
+  DECLARE_INSTRUCTION(ClinitCheck);
+
+ private:
+  const uint32_t dex_pc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HClinitCheck);
+};
+
+class HStaticFieldGet : public HExpression<1> {
+ public:
+  HStaticFieldGet(HInstruction* cls,
+                  Primitive::Type field_type,
+                  MemberOffset field_offset)
+      : HExpression(field_type, SideEffects::DependsOnSomething()),
+        field_info_(field_offset, field_type) {
+    SetRawInputAt(0, cls);
+  }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+  bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+    size_t other_offset = other->AsStaticFieldGet()->GetFieldOffset().SizeValue();
+    return other_offset == GetFieldOffset().SizeValue();
+  }
+
+  size_t ComputeHashCode() const OVERRIDE {
+    return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue();
+  }
+
+  MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+  Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+
+  DECLARE_INSTRUCTION(StaticFieldGet);
+
+ private:
+  const FieldInfo field_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(HStaticFieldGet);
+};
+
+class HStaticFieldSet : public HTemplateInstruction<2> {
+ public:
+  HStaticFieldSet(HInstruction* cls,
+                  HInstruction* value,
+                  Primitive::Type field_type,
+                  MemberOffset field_offset)
+      : HTemplateInstruction(SideEffects::ChangesSomething()),
+        field_info_(field_offset, field_type) {
+    SetRawInputAt(0, cls);
+    SetRawInputAt(1, value);
+  }
+
+  MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+  Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+
+  DECLARE_INSTRUCTION(StaticFieldSet);
+
+ private:
+  const FieldInfo field_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet);
+};
+
+// Implement the move-exception DEX instruction.
+class HLoadException : public HExpression<0> {
+ public:
+  HLoadException() : HExpression(Primitive::kPrimNot, SideEffects::None()) {}
+
+  DECLARE_INSTRUCTION(LoadException);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HLoadException);
+};
+
+class HThrow : public HTemplateInstruction<1> {
+ public:
+  HThrow(HInstruction* exception, uint32_t dex_pc)
+      : HTemplateInstruction(SideEffects::None()), dex_pc_(dex_pc) {
+    SetRawInputAt(0, exception);
+  }
+
+  bool IsControlFlow() const OVERRIDE { return true; }
+
+  bool NeedsEnvironment() const OVERRIDE { return true; }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+
+  DECLARE_INSTRUCTION(Throw);
+
+ private:
+  uint32_t dex_pc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HThrow);
+};
+
+class HTypeCheck : public HExpression<2> {
+ public:
+  explicit HTypeCheck(HInstruction* object,
+                      HLoadClass* constant,
+                      bool class_is_final,
+                      uint32_t dex_pc)
+      : HExpression(Primitive::kPrimBoolean, SideEffects::None()),
+        class_is_final_(class_is_final),
+        dex_pc_(dex_pc) {
+    SetRawInputAt(0, object);
+    SetRawInputAt(1, constant);
+  }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+    UNUSED(other);
+    return true;
+  }
+
+  bool NeedsEnvironment() const OVERRIDE {
+    // TODO: Can we debug when doing a runtime instanceof check?
+    return false;
+  }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+
+  bool IsClassFinal() const { return class_is_final_; }
+
+  DECLARE_INSTRUCTION(TypeCheck);
+
+ private:
+  const bool class_is_final_;
+  const uint32_t dex_pc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HTypeCheck);
+};
+
+
+class MoveOperands : public ArenaObject<kArenaAllocMisc> {
+ public:
+  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 +2441,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 +2459,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);
   }
 
@@ -1553,15 +2491,19 @@
   explicit HGraphVisitor(HGraph* graph) : graph_(graph) {}
   virtual ~HGraphVisitor() {}
 
-  virtual void VisitInstruction(HInstruction* instruction) {}
+  virtual void VisitInstruction(HInstruction* instruction) { UNUSED(instruction); }
   virtual void VisitBasicBlock(HBasicBlock* block);
 
+  // Visit the graph following basic block insertion order.
   void VisitInsertionOrder();
 
+  // Visit the graph following dominator tree reverse post-order.
+  void VisitReversePostOrder();
+
   HGraph* GetGraph() const { return graph_; }
 
   // Visit functions for instruction classes.
-#define DECLARE_VISIT_INSTRUCTION(name)                                        \
+#define DECLARE_VISIT_INSTRUCTION(name, super)                                        \
   virtual void Visit##name(H##name* instr) { VisitInstruction(instr); }
 
   FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
@@ -1569,11 +2511,28 @@
 #undef DECLARE_VISIT_INSTRUCTION
 
  private:
-  HGraph* graph_;
+  HGraph* const graph_;
 
   DISALLOW_COPY_AND_ASSIGN(HGraphVisitor);
 };
 
+class HGraphDelegateVisitor : public HGraphVisitor {
+ public:
+  explicit HGraphDelegateVisitor(HGraph* graph) : HGraphVisitor(graph) {}
+  virtual ~HGraphDelegateVisitor() {}
+
+  // Visit functions that delegate to to super class.
+#define DECLARE_VISIT_INSTRUCTION(name, super)                                        \
+  virtual void Visit##name(H##name* instr) OVERRIDE { Visit##super(instr); }
+
+  FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HGraphDelegateVisitor);
+};
+
 class HInsertionOrderIterator : public ValueObject {
  public:
   explicit HInsertionOrderIterator(const HGraph& graph) : graph_(graph), index_(0) {}
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
new file mode 100644
index 0000000..70dd8d7
--- /dev/null
+++ b/compiler/optimizing/nodes_test.cc
@@ -0,0 +1,117 @@
+/*
+ * 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());
+}
+
+/**
+ * Test that inserting an instruction in the graph updates user lists.
+ */
+TEST(Node, InsertInstruction) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter1 = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+  HInstruction* parameter2 = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+  entry->AddInstruction(parameter1);
+  entry->AddInstruction(parameter2);
+  entry->AddInstruction(new (&allocator) HExit());
+
+  ASSERT_FALSE(parameter1->HasUses());
+  ASSERT_EQ(parameter1->NumberOfUses(), 0u);
+
+  HInstruction* to_insert = new (&allocator) HNullCheck(parameter1, 0);
+  entry->InsertInstructionBefore(to_insert, parameter2);
+
+  ASSERT_TRUE(parameter1->HasUses());
+  ASSERT_EQ(parameter1->NumberOfUses(), 1u);
+}
+
+/**
+ * Test that adding an instruction in the graph updates user lists.
+ */
+TEST(Node, AddInstruction) {
+  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);
+
+  ASSERT_FALSE(parameter->HasUses());
+  ASSERT_EQ(parameter->NumberOfUses(), 0u);
+
+  HInstruction* to_add = new (&allocator) HNullCheck(parameter, 0);
+  entry->AddInstruction(to_add);
+
+  ASSERT_TRUE(parameter->HasUses());
+  ASSERT_EQ(parameter->NumberOfUses(), 1u);
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
new file mode 100644
index 0000000..ea98186
--- /dev/null
+++ b/compiler/optimizing/optimization.cc
@@ -0,0 +1,48 @@
+/*
+ * 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 "optimization.h"
+
+#include "base/dumpable.h"
+#include "graph_checker.h"
+
+namespace art {
+
+void HOptimization::Execute() {
+  Run();
+  visualizer_.DumpGraph(pass_name_);
+  Check();
+}
+
+void HOptimization::Check() {
+  if (kIsDebugBuild) {
+    if (is_in_ssa_form_) {
+      SSAChecker checker(graph_->GetArena(), graph_);
+      checker.Run();
+      if (!checker.IsValid()) {
+        LOG(FATAL) << Dumpable<SSAChecker>(checker);
+      }
+    } else {
+      GraphChecker checker(graph_->GetArena(), graph_);
+      checker.Run();
+      if (!checker.IsValid()) {
+        LOG(FATAL) << Dumpable<GraphChecker>(checker);
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
new file mode 100644
index 0000000..59683e2
--- /dev/null
+++ b/compiler/optimizing/optimization.h
@@ -0,0 +1,71 @@
+/*
+ * 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_OPTIMIZATION_H_
+#define ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_
+
+#include "graph_visualizer.h"
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * Abstraction to implement an optimization pass.
+ */
+class HOptimization : public ValueObject {
+ public:
+  HOptimization(HGraph* graph,
+                bool is_in_ssa_form,
+                const char* pass_name,
+                const HGraphVisualizer& visualizer)
+      : graph_(graph),
+        is_in_ssa_form_(is_in_ssa_form),
+        pass_name_(pass_name),
+        visualizer_(visualizer) {}
+
+  virtual ~HOptimization() {}
+
+  // Execute the optimization pass.
+  void Execute();
+
+  // Return the name of the pass.
+  const char* GetPassName() const { return pass_name_; }
+
+  // Peform the analysis itself.
+  virtual void Run() = 0;
+
+ private:
+  // Verify the graph; abort if it is not valid.
+  void Check();
+
+ protected:
+  HGraph* const graph_;
+
+ private:
+  // Does the analyzed graph use the SSA form?
+  const bool is_in_ssa_form_;
+  // Optimization pass name.
+  const char* pass_name_;
+  // A graph visualiser invoked after the execution of the optimization
+  // pass if enabled.
+  const HGraphVisualizer& visualizer_;
+
+  DISALLOW_COPY_AND_ASSIGN(HOptimization);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8a5077b..6e3653a 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -14,16 +14,23 @@
  * 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 "constant_folding.h"
+#include "dead_code_elimination.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 "prepare_for_register_allocation.h"
 #include "register_allocator.h"
 #include "ssa_phi_elimination.h"
 #include "ssa_liveness_analysis.h"
@@ -36,7 +43,7 @@
  */
 class CodeVectorAllocator FINAL : public CodeAllocator {
  public:
-  CodeVectorAllocator() { }
+  CodeVectorAllocator() {}
 
   virtual uint8_t* Allocate(size_t size) {
     size_ = size;
@@ -65,12 +72,145 @@
  */
 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);
+}
+
+static bool IsInstructionSetSupported(InstructionSet instruction_set) {
+  return instruction_set == kArm64
+      || (instruction_set == kThumb2 && !kArm32QuickCodeUseSoftFloat)
+      || instruction_set == kX86
+      || instruction_set == kX86_64;
+}
+
+static bool CanOptimize(const DexFile::CodeItem& code_item) {
+  // TODO: We currently cannot optimize methods with try/catch.
+  return code_item.tries_size_ == 0;
+}
+
 CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_item,
                                                uint32_t access_flags,
                                                InvokeType invoke_type,
@@ -78,6 +218,8 @@
                                                uint32_t method_idx,
                                                jobject class_loader,
                                                const DexFile& dex_file) const {
+  UNUSED(invoke_type);
+  total_compiled_methods_++;
   InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet();
   // Always use the thumb2 assembler: some runtime functionality (like implicit stack
   // overflow checks) assume thumb2.
@@ -86,7 +228,11 @@
   }
 
   // Do not attempt to compile on architectures we do not support.
-  if (instruction_set != kX86 && instruction_set != kX86_64 && instruction_set != kThumb2) {
+  if (!IsInstructionSetSupported(instruction_set)) {
+    return nullptr;
+  }
+
+  if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
     return nullptr;
   }
 
@@ -107,17 +253,13 @@
 
   HGraph* graph = builder.BuildGraph(*code_item);
   if (graph == nullptr) {
-    if (shouldCompile) {
-      LOG(FATAL) << "Could not build graph in optimizing compiler";
-    }
+    CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
     return nullptr;
   }
 
   CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, instruction_set);
   if (codegen == nullptr) {
-    if (shouldCompile) {
-      LOG(FATAL) << "Could not find code generator for optimizing compiler";
-    }
+    CHECK(!shouldCompile) << "Could not find code generator for optimizing compiler";
     return nullptr;
   }
 
@@ -127,14 +269,24 @@
 
   CodeVectorAllocator allocator;
 
-  if (RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
+  if (run_optimizations_
+      && CanOptimize(*code_item)
+      && RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
+    optimized_compiled_methods_++;
     graph->BuildDominatorTree();
     graph->TransformToSSA();
     visualizer.DumpGraph("ssa");
     graph->FindNaturalLoops();
 
+    HDeadCodeElimination(graph, visualizer).Execute();
+    HConstantFolding(graph, visualizer).Execute();
+
     SsaRedundantPhiElimination(graph).Run();
     SsaDeadPhiElimination(graph).Run();
+    InstructionSimplifier(graph).Run();
+    GlobalValueNumberer(graph->GetArena(), graph).Run();
+    visualizer.DumpGraph(kGVNPassName);
+    PrepareForRegisterAllocation(graph).Run();
 
     SsaLivenessAnalysis liveness(*graph, codegen);
     liveness.Analyze();
@@ -145,38 +297,88 @@
 
     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";
+    UNREACHABLE();
   } else {
+    unoptimized_compiled_methods_++;
     codegen->CompileBaseline(&allocator);
 
-    // Run these phases to get some test coverage.
-    graph->BuildDominatorTree();
-    graph->TransformToSSA();
-    visualizer.DumpGraph("ssa");
-    graph->FindNaturalLoops();
-    SsaLivenessAnalysis liveness(*graph, codegen);
-    liveness.Analyze();
-    visualizer.DumpGraph(kLivenessPassName);
+    if (CanOptimize(*code_item)) {
+      // Run these phases to get some test coverage.
+      graph->BuildDominatorTree();
+      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/runtime/log_severity.h b/compiler/optimizing/optimizing_compiler.h
similarity index 62%
copy from runtime/log_severity.h
copy to compiler/optimizing/optimizing_compiler.h
index 31682df..a415eca 100644
--- a/runtime/log_severity.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
+#define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
 
-typedef int LogSeverity;
+namespace art {
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+class Compiler;
+class CompilerDriver;
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+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..c4106b7 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -17,30 +17,36 @@
 #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(...)  \
   (sizeof((uint16_t[]) {__VA_ARGS__}) /sizeof(uint16_t))
 
-#define ZERO_REGISTER_CODE_ITEM(...)                                       \
-    { 0, 0, 0, 0, 0, 0, NUM_INSTRUCTIONS(__VA_ARGS__), 0, __VA_ARGS__ }
+#define N_REGISTERS_CODE_ITEM(NUM_REGS, ...)                            \
+    { NUM_REGS, 0, 0, 0, 0, 0, NUM_INSTRUCTIONS(__VA_ARGS__), 0, __VA_ARGS__ }
 
-#define ONE_REGISTER_CODE_ITEM(...)                                        \
-    { 1, 0, 0, 0, 0, 0, NUM_INSTRUCTIONS(__VA_ARGS__), 0, __VA_ARGS__ }
+#define ZERO_REGISTER_CODE_ITEM(...)   N_REGISTERS_CODE_ITEM(0, __VA_ARGS__)
+#define ONE_REGISTER_CODE_ITEM(...)    N_REGISTERS_CODE_ITEM(1, __VA_ARGS__)
+#define TWO_REGISTERS_CODE_ITEM(...)   N_REGISTERS_CODE_ITEM(2, __VA_ARGS__)
+#define THREE_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(3, __VA_ARGS__)
+#define FOUR_REGISTERS_CODE_ITEM(...)  N_REGISTERS_CODE_ITEM(4, __VA_ARGS__)
+#define FIVE_REGISTERS_CODE_ITEM(...)  N_REGISTERS_CODE_ITEM(5, __VA_ARGS__)
+#define SIX_REGISTERS_CODE_ITEM(...)   N_REGISTERS_CODE_ITEM(6, __VA_ARGS__)
 
-#define TWO_REGISTERS_CODE_ITEM(...)                                       \
-    { 2, 0, 0, 0, 0, 0, NUM_INSTRUCTIONS(__VA_ARGS__), 0, __VA_ARGS__ }
-
-#define THREE_REGISTERS_CODE_ITEM(...)                                     \
-    { 3, 0, 0, 0, 0, 0, NUM_INSTRUCTIONS(__VA_ARGS__), 0, __VA_ARGS__ }
 
 LiveInterval* BuildInterval(const size_t ranges[][2],
                             size_t number_of_ranges,
                             ArenaAllocator* allocator,
                             int reg = -1) {
-  LiveInterval* interval = new (allocator) LiveInterval(allocator, Primitive::kPrimInt);
+  LiveInterval* interval = LiveInterval::MakeInterval(allocator, Primitive::kPrimInt);
   for (size_t i = number_of_ranges; i > 0; --i) {
     interval->AddRange(ranges[i - 1][0], ranges[i - 1][1]);
   }
@@ -48,6 +54,48 @@
   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,
+                         Primitive::Type return_type = Primitive::kPrimInt) {
+  HGraphBuilder builder(allocator, return_type);
+  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_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index cadd3c5..1e93ece 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -130,13 +130,13 @@
     // this move's source or destination needs to have their source
     // changed to reflect the state of affairs after the swap.
     Location source = move->GetSource();
-    Location destination = move->GetDestination();
+    Location swap_destination = move->GetDestination();
     move->Eliminate();
     for (size_t i = 0; i < moves_.Size(); ++i) {
       const MoveOperands& other_move = *moves_.Get(i);
       if (other_move.Blocks(source)) {
-        moves_.Get(i)->SetSource(destination);
-      } else if (other_move.Blocks(destination)) {
+        moves_.Get(i)->SetSource(swap_destination);
+      } else if (other_move.Blocks(swap_destination)) {
         moves_.Get(i)->SetSource(source);
       }
     }
@@ -170,8 +170,7 @@
   DCHECK_NE(blocked, if_scratch);
   int scratch = -1;
   for (int reg = 0; reg < register_count; ++reg) {
-    if ((blocked != reg) &&
-        IsScratchLocation(Location::RegisterLocation(ManagedRegister(reg)))) {
+    if ((blocked != reg) && IsScratchLocation(Location::RegisterLocation(reg))) {
       scratch = reg;
       break;
     }
diff --git a/compiler/optimizing/parallel_move_resolver.h b/compiler/optimizing/parallel_move_resolver.h
index fcc1de6..309425e 100644
--- a/compiler/optimizing/parallel_move_resolver.h
+++ b/compiler/optimizing/parallel_move_resolver.h
@@ -17,7 +17,7 @@
 #ifndef ART_COMPILER_OPTIMIZING_PARALLEL_MOVE_RESOLVER_H_
 #define ART_COMPILER_OPTIMIZING_PARALLEL_MOVE_RESOLVER_H_
 
-#include "utils/allocation.h"
+#include "base/value_object.h"
 #include "utils/growable_array.h"
 
 namespace art {
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 093856d..62629bc 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -32,9 +32,9 @@
       message_ << " ";
     }
     message_ << "("
-             << move->GetSource().reg().RegId()
+             << move->GetSource().reg()
              << " -> "
-             << move->GetDestination().reg().RegId()
+             << move->GetDestination().reg()
              << ")";
   }
 
@@ -44,14 +44,14 @@
       message_ << " ";
     }
     message_ << "("
-             << move->GetSource().reg().RegId()
+             << move->GetSource().reg()
              << " <-> "
-             << move->GetDestination().reg().RegId()
+             << move->GetDestination().reg()
              << ")";
   }
 
-  virtual void SpillScratch(int reg) {}
-  virtual void RestoreScratch(int reg) {}
+  virtual void SpillScratch(int reg ATTRIBUTE_UNUSED) {}
+  virtual void RestoreScratch(int reg ATTRIBUTE_UNUSED) {}
 
   std::string GetMessage() const {
     return  message_.str();
@@ -70,8 +70,9 @@
   HParallelMove* moves = new (allocator) HParallelMove(allocator);
   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(operands[i][0]),
+        Location::RegisterLocation(operands[i][1]),
+        nullptr));
   }
   return moves;
 }
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
new file mode 100644
index 0000000..7186dbe
--- /dev/null
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -0,0 +1,78 @@
+/*
+ * 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 "prepare_for_register_allocation.h"
+
+namespace art {
+
+void PrepareForRegisterAllocation::Run() {
+  // Order does not matter.
+  for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    // No need to visit the phis.
+    for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
+         inst_it.Advance()) {
+      inst_it.Current()->Accept(this);
+    }
+  }
+}
+
+void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) {
+  check->ReplaceWith(check->InputAt(0));
+}
+
+void PrepareForRegisterAllocation::VisitDivZeroCheck(HDivZeroCheck* check) {
+  check->ReplaceWith(check->InputAt(0));
+}
+
+void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) {
+  check->ReplaceWith(check->InputAt(0));
+}
+
+void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) {
+  HLoadClass* cls = check->GetLoadClass();
+  check->ReplaceWith(cls);
+  if (check->GetPrevious() == cls) {
+    // Pass the initialization duty to the `HLoadClass` instruction,
+    // and remove the instruction from the graph.
+    cls->SetMustGenerateClinitCheck();
+    check->GetBlock()->RemoveInstruction(check);
+  }
+}
+
+void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
+  bool needs_materialization = false;
+  if (!condition->HasOnlyOneUse()) {
+    needs_materialization = true;
+  } else {
+    HUseListNode<HInstruction>* uses = condition->GetUses();
+    HInstruction* user = uses->GetUser();
+    if (!user->IsIf()) {
+      needs_materialization = true;
+    } else {
+      // 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 (condition->GetNext() != user) {
+        needs_materialization = true;
+      }
+    }
+  }
+  if (!needs_materialization) {
+    condition->ClearNeedsMaterialization();
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
new file mode 100644
index 0000000..0fdb65f
--- /dev/null
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_PREPARE_FOR_REGISTER_ALLOCATION_H_
+#define ART_COMPILER_OPTIMIZING_PREPARE_FOR_REGISTER_ALLOCATION_H_
+
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * A simplification pass over the graph before doing register allocation.
+ * For example it changes uses of null checks and bounds checks to the original
+ * objects, to avoid creating a live range for these checks.
+ */
+class PrepareForRegisterAllocation : public HGraphDelegateVisitor {
+ public:
+  explicit PrepareForRegisterAllocation(HGraph* graph) : HGraphDelegateVisitor(graph) {}
+
+  void Run();
+
+ private:
+  virtual void VisitNullCheck(HNullCheck* check) OVERRIDE;
+  virtual void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE;
+  virtual void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
+  virtual void VisitClinitCheck(HClinitCheck* check) OVERRIDE;
+  virtual void VisitCondition(HCondition* condition) OVERRIDE;
+
+  DISALLOW_COPY_AND_ASSIGN(PrepareForRegisterAllocation);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_PREPARE_FOR_REGISTER_ALLOCATION_H_
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..4d6e664 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -16,6 +16,9 @@
 
 #include "register_allocator.h"
 
+#include <sstream>
+
+#include "base/bit_vector-inl.h"
 #include "code_generator.h"
 #include "ssa_liveness_analysis.h"
 
@@ -30,18 +33,30 @@
       : 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()),
+        physical_core_register_intervals_(allocator, codegen->GetNumberOfCoreRegisters()),
+        physical_fp_register_intervals_(allocator, codegen->GetNumberOfFloatingPointRegisters()),
+        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())) {
-  codegen->SetupBlockedRegisters(blocked_registers_);
-  physical_register_intervals_.SetSize(codegen->GetNumberOfRegisters());
+        blocked_core_registers_(codegen->GetBlockedCoreRegisters()),
+        blocked_fp_registers_(codegen->GetBlockedFloatingPointRegisters()),
+        reserved_out_slots_(0),
+        maximum_number_of_live_registers_(0) {
+  codegen->SetupBlockedRegisters();
+  physical_core_register_intervals_.SetSize(codegen->GetNumberOfCoreRegisters());
+  physical_fp_register_intervals_.SetSize(codegen->GetNumberOfFloatingPointRegisters());
+  // 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,27 +69,25 @@
          !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;
+      if ((current->GetType() == Primitive::kPrimFloat || current->GetType() == Primitive::kPrimDouble)
+          && instruction_set != kX86_64) {
+        return false;
+      }
     }
   }
   return true;
 }
 
 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) {
@@ -87,91 +100,197 @@
 
 void RegisterAllocator::BlockRegister(Location location,
                                       size_t start,
-                                      size_t end,
-                                      Primitive::Type type) {
-  int reg = location.reg().RegId();
-  LiveInterval* interval = physical_register_intervals_.Get(reg);
+                                      size_t end) {
+  int reg = location.reg();
+  DCHECK(location.IsRegister() || location.IsFpuRegister());
+  LiveInterval* interval = location.IsRegister()
+      ? physical_core_register_intervals_.Get(reg)
+      : physical_fp_register_intervals_.Get(reg);
+  Primitive::Type type = location.IsRegister()
+      ? Primitive::kPrimInt
+      : Primitive::kPrimDouble;
   if (interval == nullptr) {
     interval = LiveInterval::MakeFixedInterval(allocator_, reg, type);
-    physical_register_intervals_.Put(reg, interval);
-    inactive_.Add(interval);
+    if (location.IsRegister()) {
+      physical_core_register_intervals_.Put(reg, interval);
+    } else {
+      physical_fp_register_intervals_.Put(reg, interval);
+    }
   }
   DCHECK(interval->GetRegister() == reg);
   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 back_it(block->GetInstructions()); !back_it.Done();
+         back_it.Advance()) {
+      ProcessInstruction(back_it.Current());
+    }
+    for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
+      ProcessInstruction(inst_it.Current());
     }
   }
 
+  number_of_registers_ = codegen_->GetNumberOfCoreRegisters();
+  registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
+  processing_core_registers_ = true;
+  unhandled_ = &unhandled_core_intervals_;
+  for (size_t i = 0, e = physical_core_register_intervals_.Size(); i < e; ++i) {
+    LiveInterval* fixed = physical_core_register_intervals_.Get(i);
+    if (fixed != nullptr) {
+      // Fixed interval is added to inactive_ instead of unhandled_.
+      // It's also the only type of inactive interval whose start position
+      // can be after the current interval during linear scan.
+      // Fixed interval is never split and never moves to unhandled_.
+      inactive_.Add(fixed);
+    }
+  }
   LinearScan();
+
+  size_t saved_maximum_number_of_live_registers = maximum_number_of_live_registers_;
+  maximum_number_of_live_registers_ = 0;
+
+  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_;
+  for (size_t i = 0, e = physical_fp_register_intervals_.Size(); i < e; ++i) {
+    LiveInterval* fixed = physical_fp_register_intervals_.Get(i);
+    if (fixed != nullptr) {
+      // Fixed interval is added to inactive_ instead of unhandled_.
+      // It's also the only type of inactive interval whose start position
+      // can be after the current interval during linear scan.
+      // Fixed interval is never split and never moves to unhandled_.
+      inactive_.Add(fixed);
+    }
+  }
+  LinearScan();
+  maximum_number_of_live_registers_ += saved_maximum_number_of_live_registers;
+}
+
+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() || temp.IsFpuRegister()) {
+      BlockRegister(temp, position, position + 1);
+    } else {
+      DCHECK(temp.IsUnallocated());
+      DCHECK(temp.GetPolicy() == Location::kRequiresRegister);
+      LiveInterval* interval = LiveInterval::MakeTempInterval(allocator_, 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);
+
+  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_core_intervals_.Add(interval);
+      unhandled_fp_intervals_.Add(interval);
+    }
+  }
+
+  if (locations->WillCall()) {
+    // Block all registers.
+    for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) {
+      BlockRegister(Location::RegisterLocation(i),
+                    position,
+                    position + 1);
+    }
+    for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) {
+      BlockRegister(Location::FpuRegisterLocation(i),
+                    position,
+                    position + 1);
+    }
+  }
+
+  for (size_t i = 0; i < instruction->InputCount(); ++i) {
+    Location input = locations->InAt(i);
+    if (input.IsRegister() || input.IsFpuRegister()) {
+      BlockRegister(input, position, position + 1);
+    }
+  }
+
+  LiveInterval* current = instruction->GetLiveInterval();
+  if (current == nullptr) return;
+
+  GrowableArray<LiveInterval*>& unhandled = core_register
+      ? unhandled_core_intervals_
+      : unhandled_fp_intervals_;
+
+  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.IsUnallocated() && output.GetPolicy() == Location::kSameAsFirstInput) {
+    Location first = locations->InAt(0);
+    if (first.IsRegister() || first.IsFpuRegister()) {
+      current->SetFrom(position + 1);
+      current->SetRegister(first.reg());
+    }
+  } else if (output.IsRegister() || output.IsFpuRegister()) {
+    // Shift the interval's start by one to account for the blocked register.
+    current->SetFrom(position + 1);
+    current->SetRegister(output.reg());
+    BlockRegister(output, position, position + 1);
+  } 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 just 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 directly 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 {
+    // Don't add directly to `unhandled`, temp or safepoint intervals
+    // for this instruction may have been added, and those can be
+    // processed first.
+    AddSorted(&unhandled, current);
+  }
 }
 
 class AllRangesIterator : public ValueObject {
@@ -212,19 +331,36 @@
     }
   }
 
-  for (size_t i = 0, e = physical_register_intervals_.Size(); i < e; ++i) {
-    LiveInterval* fixed = physical_register_intervals_.Get(i);
-    if (fixed != nullptr && ShouldProcess(processing_core_registers_, fixed)) {
-      intervals.Add(fixed);
+  if (processing_core_registers_) {
+    for (size_t i = 0, e = physical_core_register_intervals_.Size(); i < e; ++i) {
+      LiveInterval* fixed = physical_core_register_intervals_.Get(i);
+      if (fixed != nullptr) {
+        intervals.Add(fixed);
+      }
+    }
+  } else {
+    for (size_t i = 0, e = physical_fp_register_intervals_.Size(); i < e; ++i) {
+      LiveInterval* fixed = physical_fp_register_intervals_.Get(i);
+      if (fixed != nullptr) {
+        intervals.Add(fixed);
+      }
     }
   }
 
-  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 +384,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 +408,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 {
@@ -295,10 +436,10 @@
   interval->Dump(stream);
   stream << ": ";
   if (interval->HasRegister()) {
-    if (processing_core_registers_) {
-      codegen_->DumpCoreRegister(stream, interval->GetRegister());
-    } else {
+    if (interval->IsFloatingPoint()) {
       codegen_->DumpFloatingPointRegister(stream, interval->GetRegister());
+    } else {
+      codegen_->DumpCoreRegister(stream, interval->GetRegister());
     }
   } else {
     stream << "spilled";
@@ -306,14 +447,40 @@
   stream << std::endl;
 }
 
+void RegisterAllocator::DumpAllIntervals(std::ostream& stream) const {
+  stream << "inactive: " << std::endl;
+  for (size_t i = 0; i < inactive_.Size(); i ++) {
+    DumpInterval(stream, inactive_.Get(i));
+  }
+  stream << "active: " << std::endl;
+  for (size_t i = 0; i < active_.Size(); i ++) {
+    DumpInterval(stream, active_.Get(i));
+  }
+  stream << "unhandled: " << std::endl;
+  auto unhandled = (unhandled_ != nullptr) ?
+      unhandled_ : &unhandled_core_intervals_;
+  for (size_t i = 0; i < unhandled->Size(); i ++) {
+    DumpInterval(stream, unhandled->Get(i));
+  }
+  stream << "handled: " << std::endl;
+  for (size_t i = 0; i < handled_.Size(); i ++) {
+    DumpInterval(stream, handled_.Get(i));
+  }
+}
+
 // 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());
+    DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() >= current->GetStart());
     size_t position = current->GetStart();
 
+    // Remember the inactive_ size here since the ones moved to inactive_ from
+    // active_ below shouldn't need to be re-checked.
+    size_t inactive_intervals_to_handle = inactive_.Size();
+
     // (2) Remove currently active intervals that are dead at this position.
     //     Move active intervals that have a lifetime hole at this position
     //     to inactive.
@@ -332,19 +499,30 @@
 
     // (3) Remove currently inactive intervals that are dead at this position.
     //     Move inactive intervals that cover this position to active.
-    for (size_t i = 0; i < inactive_.Size(); ++i) {
+    for (size_t i = 0; i < inactive_intervals_to_handle; ++i) {
       LiveInterval* interval = inactive_.Get(i);
+      DCHECK(interval->GetStart() < position || interval->IsFixed());
       if (interval->IsDeadAt(position)) {
         inactive_.Delete(interval);
         --i;
+        --inactive_intervals_to_handle;
         handled_.Add(interval);
       } else if (interval->Covers(position)) {
         inactive_.Delete(interval);
         --i;
+        --inactive_intervals_to_handle;
         active_.Add(interval);
       }
     }
 
+    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);
 
@@ -371,19 +549,6 @@
     free_until[i] = kMaxLifetimePosition;
   }
 
-  // For each inactive interval, set its register to be free until
-  // the next intersection with `current`.
-  // Thanks to SSA, this should only be needed for intervals
-  // that are the result of a split.
-  for (size_t i = 0, e = inactive_.Size(); i < e; ++i) {
-    LiveInterval* inactive = inactive_.Get(i);
-    DCHECK(inactive->HasRegister());
-    size_t next_intersection = inactive->FirstIntersectionWith(current);
-    if (next_intersection != kNoLifetime) {
-      free_until[inactive->GetRegister()] = next_intersection;
-    }
-  }
-
   // For each active interval, set its register to not free.
   for (size_t i = 0, e = active_.Size(); i < e; ++i) {
     LiveInterval* interval = active_.Get(i);
@@ -391,13 +556,52 @@
     free_until[interval->GetRegister()] = 0;
   }
 
-  // Pick the register that is free the longest.
+  // For each inactive interval, set its register to be free until
+  // the next intersection with `current`.
+  for (size_t i = 0, e = inactive_.Size(); i < e; ++i) {
+    LiveInterval* inactive = inactive_.Get(i);
+    // Temp/Slow-path-safepoint interval has no holes.
+    DCHECK(!inactive->IsTemp() && !inactive->IsSlowPathSafepoint());
+    if (!current->IsSplit() && !inactive->IsFixed()) {
+      // Neither current nor inactive are fixed.
+      // Thanks to SSA, a non-split interval starting in a hole of an
+      // inactive interval should never intersect with that inactive interval.
+      // Only if it's not fixed though, because fixed intervals don't come from SSA.
+      DCHECK_EQ(inactive->FirstIntersectionWith(current), kNoLifetime);
+      continue;
+    }
+
+    DCHECK(inactive->HasRegister());
+    if (free_until[inactive->GetRegister()] == 0) {
+      // Already used by some active interval. No need to intersect.
+      continue;
+    }
+    size_t next_intersection = inactive->FirstIntersectionWith(current);
+    if (next_intersection != kNoLifetime) {
+      free_until[inactive->GetRegister()] =
+          std::min(free_until[inactive->GetRegister()], next_intersection);
+    }
+  }
+
   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 {
+    int hint = current->FindFirstRegisterHint(free_until);
+    if (hint != kNoRegister) {
+      DCHECK(!IsBlocked(hint));
+      reg = hint;
+    } 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,16 +617,15 @@
     // the register is not available anymore.
     LiveInterval* split = Split(current, free_until[reg]);
     DCHECK(split != nullptr);
-    AddToUnhandled(split);
+    AddSorted(unhandled_, split);
   }
   return true;
 }
 
 bool RegisterAllocator::IsBlocked(int reg) const {
-  // TODO: This only works for core registers and needs to be adjusted for
-  // floating point registers.
-  DCHECK(processing_core_registers_);
-  return blocked_registers_[reg];
+  return processing_core_registers_
+      ? blocked_core_registers_[reg]
+      : blocked_fp_registers_[reg];
 }
 
 // Find the register that is used the last, and spill the interval
@@ -458,10 +661,18 @@
 
   // For each inactive interval, find the next use of its register after the
   // start of current.
-  // Thanks to SSA, this should only be needed for intervals
-  // that are the result of a split.
   for (size_t i = 0, e = inactive_.Size(); i < e; ++i) {
     LiveInterval* inactive = inactive_.Get(i);
+    // Temp/Slow-path-safepoint interval has no holes.
+    DCHECK(!inactive->IsTemp() && !inactive->IsSlowPathSafepoint());
+    if (!current->IsSplit() && !inactive->IsFixed()) {
+      // Neither current nor inactive are fixed.
+      // Thanks to SSA, a non-split interval starting in a hole of an
+      // inactive interval should never intersect with that inactive interval.
+      // Only if it's not fixed though, because fixed intervals don't come from SSA.
+      DCHECK_EQ(inactive->FirstIntersectionWith(current), kNoLifetime);
+      continue;
+    }
     DCHECK(inactive->HasRegister());
     size_t next_intersection = inactive->FirstIntersectionWith(current);
     if (next_intersection != kNoLifetime) {
@@ -492,7 +703,9 @@
     // register, we split this interval just before its first register use.
     AllocateSpillSlotFor(current);
     LiveInterval* split = Split(current, first_register_use - 1);
-    AddToUnhandled(split);
+    DCHECK_NE(current, split) << "There is not enough registers available for "
+      << split->GetParent()->GetDefinedBy()->DebugName();
+    AddSorted(unhandled_, split);
     return false;
   } else {
     // Use this register and spill the active and inactives interval that
@@ -506,25 +719,34 @@
         LiveInterval* split = Split(active, current->GetStart());
         active_.DeleteAt(i);
         handled_.Add(active);
-        AddToUnhandled(split);
+        AddSorted(unhandled_, split);
         break;
       }
     }
 
-    for (size_t i = 0; i < inactive_.Size(); ++i) {
+    for (size_t i = 0, e = inactive_.Size(); i < e; ++i) {
       LiveInterval* inactive = inactive_.Get(i);
       if (inactive->GetRegister() == reg) {
+        if (!current->IsSplit() && !inactive->IsFixed()) {
+          // Neither current nor inactive are fixed.
+          // Thanks to SSA, a non-split interval starting in a hole of an
+          // inactive interval should never intersect with that inactive interval.
+          // Only if it's not fixed though, because fixed intervals don't come from SSA.
+          DCHECK_EQ(inactive->FirstIntersectionWith(current), kNoLifetime);
+          continue;
+        }
         size_t next_intersection = inactive->FirstIntersectionWith(current);
         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());
+            LiveInterval* split = Split(inactive, next_intersection);
             inactive_.DeleteAt(i);
-            handled_.Add(inactive);
-            AddToUnhandled(split);
             --i;
+            --e;
+            handled_.Add(inactive);
+            AddSorted(unhandled_, split);
           }
         }
       }
@@ -534,16 +756,17 @@
   }
 }
 
-void RegisterAllocator::AddToUnhandled(LiveInterval* interval) {
+void RegisterAllocator::AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval) {
+  DCHECK(!interval->IsFixed() && !interval->HasSpillSlot());
   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) {
@@ -559,10 +782,6 @@
   }
 }
 
-static bool NeedTwoSpillSlot(Primitive::Type type) {
-  return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
-}
-
 void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) {
   LiveInterval* parent = interval->GetParent();
 
@@ -590,14 +809,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,97 +822,70 @@
     }
   }
 
-  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);
-  }
-
-  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);
-  }
-
-  parent->SetSpillSlot(slot * kVRegSize);
-}
-
-static Location ConvertToLocation(LiveInterval* interval) {
-  if (interval->HasRegister()) {
-    return Location::RegisterLocation(ManagedRegister(interval->GetRegister()));
-  } else {
-    HInstruction* defined_by = interval->GetParent()->GetDefinedBy();
-    if (defined_by->IsConstant()) {
-      return defined_by->GetLocations()->Out();
+  if (parent->NeedsTwoSpillSlots()) {
+    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 {
-      DCHECK(interval->GetParent()->HasSpillSlot());
-      if (NeedTwoSpillSlot(interval->GetType())) {
-        return Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot());
-      } else {
-        return Location::StackSlot(interval->GetParent()->GetSpillSlot());
-      }
+      spill_slots_.Put(slot, end);
+      spill_slots_.Put(slot + 1, end);
+    }
+  } else {
+    if (slot == spill_slots_.Size()) {
+      // We need a new spill slot.
+      spill_slots_.Add(end);
+    } else {
+      spill_slots_.Put(slot, end);
     }
   }
+
+  parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize);
 }
 
-// We create a special marker for inputs moves to differentiate them from
-// moves created during resolution. They must be different instructions
-// because the input moves work on the assumption that the interval moves
-// have been executed.
-static constexpr size_t kInputMoveLifetimePosition = 0;
-static bool IsInputMove(HInstruction* instruction) {
-  return instruction->GetLifetimePosition() == kInputMoveLifetimePosition;
+static bool IsValidDestination(Location destination) {
+  return destination.IsRegister()
+      || destination.IsFpuRegister()
+      || destination.IsStackSlot()
+      || destination.IsDoubleStackSlot();
 }
 
-void RegisterAllocator::AddInputMoveFor(HInstruction* instruction,
+void RegisterAllocator::AddInputMoveFor(HInstruction* user,
                                         Location source,
                                         Location destination) const {
+  DCHECK(IsValidDestination(destination));
   if (source.Equals(destination)) return;
 
-  DCHECK(instruction->AsPhi() == nullptr);
+  DCHECK(!user->IsPhi());
 
-  HInstruction* previous = instruction->GetPrevious();
+  HInstruction* previous = user->GetPrevious();
   HParallelMove* move = nullptr;
   if (previous == nullptr
-      || previous->AsParallelMove() == nullptr
-      || !IsInputMove(previous)) {
+      || !previous->IsParallelMove()
+      || previous->GetLifetimePosition() < user->GetLifetimePosition()) {
     move = new (allocator_) HParallelMove(allocator_);
-    move->SetLifetimePosition(kInputMoveLifetimePosition);
-    instruction->GetBlock()->InsertInstructionBefore(move, instruction);
+    move->SetLifetimePosition(user->GetLifetimePosition());
+    user->GetBlock()->InsertInstructionBefore(move, user);
   } else {
     move = previous->AsParallelMove();
   }
-  DCHECK(IsInputMove(move));
-  move->AddMove(new (allocator_) MoveOperands(source, destination));
+  DCHECK_EQ(move->GetLifetimePosition(), user->GetLifetimePosition());
+  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);
   if (at == nullptr) {
-    // Block boundary, don't no anything the connection of split siblings will handle it.
+    // Block boundary, don't do anything the connection of split siblings will handle it.
     return;
   }
   HParallelMove* move;
@@ -711,7 +895,7 @@
     move = at->GetNext()->AsParallelMove();
     // 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 (move == nullptr || move->GetLifetimePosition() != position) {
+    if (move == nullptr || move->GetLifetimePosition() > position) {
       move = new (allocator_) HParallelMove(allocator_);
       move->SetLifetimePosition(position);
       at->GetBlock()->InsertInstructionBefore(move, at->GetNext());
@@ -719,14 +903,15 @@
   } else {
     // Move must happen before the instruction.
     HInstruction* previous = at->GetPrevious();
-    if (previous != nullptr && previous->AsParallelMove() != nullptr) {
-      // 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) {
-        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,21 +919,29 @@
       move = previous->AsParallelMove();
     }
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination));
+  DCHECK_EQ(move->GetLifetimePosition(), position);
+  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);
   HInstruction* last = block->GetLastInstruction();
+  // We insert moves at exit for phi predecessors and connecting blocks.
+  // A block ending with an if cannot branch to a block with phis because
+  // we do not allow critical edges. It can also not connect
+  // a split interval between two blocks: the move has to happen in the successor.
+  DCHECK(!last->IsIf());
   HInstruction* previous = last->GetPrevious();
   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 +949,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 +968,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);
+  if (instruction->IsPhi()) {
+    InsertParallelMoveAtEntryOf(instruction->GetBlock(), instruction, source, destination);
     return;
   }
 
@@ -796,7 +992,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) {
@@ -804,8 +1000,10 @@
   if (current->HasSpillSlot() && current->HasRegister()) {
     // We spill eagerly, so move must be at definition.
     InsertMoveAfter(interval->GetDefinedBy(),
-                    Location::RegisterLocation(ManagedRegister(interval->GetRegister())),
-                    NeedTwoSpillSlot(interval->GetType())
+                    interval->IsFloatingPoint()
+                        ? Location::FpuRegisterLocation(interval->GetRegister())
+                        : Location::RegisterLocation(interval->GetRegister()),
+                    interval->NeedsTwoSpillSlots()
                         ? Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot())
                         : Location::StackSlot(interval->GetParent()->GetSpillSlot()));
   }
@@ -814,17 +1012,19 @@
   // Walk over all siblings, updating locations of use positions, and
   // connecting them when they are adjacent.
   do {
-    Location source = ConvertToLocation(current);
+    Location source = current->ToLocation();
 
     // 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);
         }
       }
@@ -837,8 +1037,50 @@
     if (next_sibling != nullptr
         && next_sibling->HasRegister()
         && current->GetEnd() == next_sibling->GetStart()) {
-      Location destination = ConvertToLocation(next_sibling);
-      InsertParallelMoveAt(current->GetEnd(), source, destination);
+      Location destination = next_sibling->ToLocation();
+      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 (interval->GetStart() == position) {
+        // The safepoint is for this instruction, so the location of the instruction
+        // does not need to be saved.
+        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());
+          }
+          break;
+        }
+        case Location::kFpuRegister: {
+          locations->AddLiveRegister(source);
+          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 +1096,11 @@
   }
 
   size_t from_position = from->GetLifetimeEnd() - 1;
-  size_t to_position = to->GetLifetimeStart();
+  // When an instruction 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 +1126,8 @@
     return;
   }
 
+  DCHECK(destination != nullptr && source != nullptr);
+
   if (!destination->HasRegister()) {
     // Values are eagerly spilled. Spill slot already contains appropriate value.
     return;
@@ -888,25 +1136,22 @@
   // 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(),
+                               source->ToLocation(),
+                               destination->ToLocation());
   } else {
     DCHECK_EQ(to->GetPredecessors().Size(), 1u);
-    InsertParallelMoveAtEntryOf(to, ConvertToLocation(source), ConvertToLocation(destination));
+    InsertParallelMoveAtEntryOf(to,
+                                interval->GetParent()->GetDefinedBy(),
+                                source->ToLocation(),
+                                destination->ToLocation());
   }
 }
 
-// Returns the location of `interval`, or siblings of `interval`, at `position`.
-static Location FindLocationAt(LiveInterval* interval, size_t position) {
-  LiveInterval* current = interval;
-  while (!current->Covers(position)) {
-    current = current->GetNextSibling();
-    DCHECK(current != nullptr);
-  }
-  return ConvertToLocation(current);
-}
-
 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.
@@ -915,26 +1160,30 @@
     LiveInterval* current = instruction->GetLiveInterval();
     LocationSummary* locations = instruction->GetLocations();
     Location location = locations->Out();
-    if (instruction->AsParameterValue() != nullptr) {
+    if (instruction->IsParameterValue()) {
       // Now that we know the frame size, adjust the parameter's location.
       if (location.IsStackSlot()) {
         location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
         current->SetSpillSlot(location.GetStackIndex());
-        locations->SetOut(location);
+        locations->UpdateOut(location);
       } else if (location.IsDoubleStackSlot()) {
         location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
         current->SetSpillSlot(location.GetStackIndex());
-        locations->SetOut(location);
+        locations->UpdateOut(location);
       } else if (current->HasSpillSlot()) {
         current->SetSpillSlot(current->GetSpillSlot() + codegen_->GetFrameSize());
       }
     }
 
-    Location source = ConvertToLocation(current);
+    Location source = current->ToLocation();
 
     if (location.IsUnallocated()) {
       if (location.GetPolicy() == Location::kSameAsFirstInput) {
-        locations->SetInAt(0, source);
+        if (locations->InAt(0).IsUnallocated()) {
+          locations->SetInAt(0, source);
+        } else {
+          DCHECK(locations->InAt(0).Equals(source));
+        }
       }
       locations->SetOut(source);
     } else {
@@ -964,19 +1213,35 @@
   // Resolve phi inputs. Order does not matter.
   for (HLinearOrderIterator it(liveness_); !it.Done(); it.Advance()) {
     HBasicBlock* current = it.Current();
-    for (HInstructionIterator it(current->GetPhis()); !it.Done(); it.Advance()) {
-      HInstruction* phi = it.Current();
+    for (HInstructionIterator inst_it(current->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
+      HInstruction* phi = inst_it.Current();
       for (size_t i = 0, e = current->GetPredecessors().Size(); i < e; ++i) {
         HBasicBlock* predecessor = current->GetPredecessors().Get(i);
         DCHECK_EQ(predecessor->GetSuccessors().Size(), 1u);
         HInstruction* input = phi->InputAt(i);
-        Location source = FindLocationAt(input->GetLiveInterval(),
-                                         predecessor->GetLastInstruction()->GetLifetimePosition());
-        Location destination = ConvertToLocation(phi->GetLiveInterval());
-        InsertParallelMoveAtExitOf(predecessor, source, destination);
+        Location source = input->GetLiveInterval()->GetLocationAt(
+            predecessor->GetLifetimeEnd() - 1);
+        Location destination = phi->GetLiveInterval()->ToLocation();
+        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);
+    HInstruction* at = liveness_.GetTempUser(temp);
+    if (at != current) {
+      temp_index = 0;
+      current = at;
+    }
+    LocationSummary* locations = at->GetLocations();
+    DCHECK(temp->GetType() == Primitive::kPrimInt);
+    locations->SetTempAt(
+        temp_index++, Location::RegisterLocation(temp->GetRegister()));
+  }
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index be1c7ec..976ee39 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -59,6 +59,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 +84,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);
@@ -93,12 +94,10 @@
   bool IsBlocked(int reg) const;
 
   // Update the interval for the register in `location` to cover [start, end).
-  void BlockRegister(Location location, size_t start, size_t end, Primitive::Type type);
+  void BlockRegister(Location location, size_t start, size_t end);
 
   // 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,24 +106,43 @@
   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;
+  void DumpAllIntervals(std::ostream& stream) const;
 
   ArenaAllocator* const allocator_;
   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 +155,21 @@
   // 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_;
+  GrowableArray<LiveInterval*> physical_core_register_intervals_;
+  GrowableArray<LiveInterval*> physical_fp_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_;
@@ -155,7 +181,16 @@
   size_t* registers_array_;
 
   // Blocked registers, as decided by the code generator.
-  bool* const blocked_registers_;
+  bool* const blocked_core_registers_;
+  bool* const blocked_fp_registers_;
+
+  // Slots reserved for out arguments.
+  size_t reserved_out_slots_;
+
+  // The maximum live registers at safepoints.
+  size_t maximum_number_of_live_registers_;
+
+  ART_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..3d81362 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,395 @@
   // 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());
 
   // 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());
+}
+
+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);
+  // For SSA value intervals, only an interval resulted from a split may intersect
+  // with inactive intervals.
+  unhandled = register_allocator.Split(unhandled, 5);
+
+  // 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::MakeInterval(&allocator, Primitive::kPrimInt);
+  interval->SetRegister(0);
+  interval->AddRange(40, 50);
+  register_allocator.inactive_.Add(interval);
+
+  interval = LiveInterval::MakeInterval(&allocator, Primitive::kPrimInt);
+  interval->SetRegister(0);
+  interval->AddRange(20, 30);
+  register_allocator.inactive_.Add(interval);
+
+  interval = LiveInterval::MakeInterval(&allocator, 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_;
+
+  ASSERT_TRUE(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());
+}
+
+static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator,
+                                  HPhi** phi,
+                                  HInstruction** input1,
+                                  HInstruction** input2) {
+  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);
+
+  HInstruction* test = new (allocator) HInstanceFieldGet(
+      parameter, Primitive::kPrimBoolean, MemberOffset(22));
+  block->AddInstruction(test);
+  block->AddInstruction(new (allocator) HIf(test));
+  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) HGoto());
+  else_->AddInstruction(new (allocator) HGoto());
+
+  *phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
+  join->AddPhi(*phi);
+  *input1 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, MemberOffset(42));
+  *input2 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, MemberOffset(42));
+  then->AddInstruction(*input1);
+  else_->AddInstruction(*input2);
+  join->AddInstruction(new (allocator) HExit());
+  (*phi)->AddInput(*input1);
+  (*phi)->AddInput(*input2);
+
+  graph->BuildDominatorTree();
+  graph->FindNaturalLoops();
+  return graph;
+}
+
+TEST(RegisterAllocatorTest, PhiHint) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HPhi *phi;
+  HInstruction *input1, *input2;
+
+  {
+    HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
+    x86::CodeGeneratorX86 codegen(graph);
+    SsaLivenessAnalysis liveness(*graph, &codegen);
+    liveness.Analyze();
+
+    // Check that the register allocator is deterministic.
+    RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+    register_allocator.AllocateRegisters();
+
+    ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 0);
+    ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 0);
+    ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 0);
+  }
+
+  {
+    HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
+    x86::CodeGeneratorX86 codegen(graph);
+    SsaLivenessAnalysis liveness(*graph, &codegen);
+    liveness.Analyze();
+
+    // Set the phi to a specific register, and check that the inputs get allocated
+    // the same register.
+    phi->GetLocations()->SetOut(Location::RegisterLocation(2));
+    RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+    register_allocator.AllocateRegisters();
+
+    ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 2);
+    ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 2);
+    ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 2);
+  }
+
+  {
+    HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
+    x86::CodeGeneratorX86 codegen(graph);
+    SsaLivenessAnalysis liveness(*graph, &codegen);
+    liveness.Analyze();
+
+    // Set input1 to a specific register, and check that the phi and other input get allocated
+    // the same register.
+    input1->GetLocations()->SetOut(Location::RegisterLocation(2));
+    RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+    register_allocator.AllocateRegisters();
+
+    ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 2);
+    ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 2);
+    ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 2);
+  }
+
+  {
+    HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
+    x86::CodeGeneratorX86 codegen(graph);
+    SsaLivenessAnalysis liveness(*graph, &codegen);
+    liveness.Analyze();
+
+    // Set input2 to a specific register, and check that the phi and other input get allocated
+    // the same register.
+    input2->GetLocations()->SetOut(Location::RegisterLocation(2));
+    RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+    register_allocator.AllocateRegisters();
+
+    ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 2);
+    ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 2);
+    ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 2);
+  }
+}
+
+static HGraph* BuildFieldReturn(ArenaAllocator* allocator,
+                                HInstruction** field,
+                                HInstruction** ret) {
+  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);
+
+  *field = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, MemberOffset(42));
+  block->AddInstruction(*field);
+  *ret = new (allocator) HReturn(*field);
+  block->AddInstruction(*ret);
+
+  HBasicBlock* exit = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(exit);
+  block->AddSuccessor(exit);
+  exit->AddInstruction(new (allocator) HExit());
+  return graph;
+}
+
+TEST(RegisterAllocatorTest, ExpectedInRegisterHint) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HInstruction *field, *ret;
+
+  {
+    HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
+    x86::CodeGeneratorX86 codegen(graph);
+    SsaLivenessAnalysis liveness(*graph, &codegen);
+    liveness.Analyze();
+
+    RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+    register_allocator.AllocateRegisters();
+
+    // Sanity check that in normal conditions, the register should be hinted to 0 (EAX).
+    ASSERT_EQ(field->GetLiveInterval()->GetRegister(), 0);
+  }
+
+  {
+    HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
+    x86::CodeGeneratorX86 codegen(graph);
+    SsaLivenessAnalysis liveness(*graph, &codegen);
+    liveness.Analyze();
+
+    // Check that the field gets put in the register expected by its use.
+    // Don't use SetInAt because we are overriding an already allocated location.
+    ret->GetLocations()->inputs_.Put(0, Location::RegisterLocation(2));
+
+    RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+    register_allocator.AllocateRegisters();
+
+    ASSERT_EQ(field->GetLiveInterval()->GetRegister(), 2);
+  }
+}
+
+static HGraph* BuildTwoAdds(ArenaAllocator* allocator,
+                            HInstruction** first_add,
+                            HInstruction** second_add) {
+  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::kPrimInt);
+  HInstruction* constant1 = new (allocator) HIntConstant(0);
+  HInstruction* constant2 = new (allocator) HIntConstant(0);
+  entry->AddInstruction(parameter);
+  entry->AddInstruction(constant1);
+  entry->AddInstruction(constant2);
+
+  HBasicBlock* block = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+
+  *first_add = new (allocator) HAdd(Primitive::kPrimInt, parameter, constant1);
+  block->AddInstruction(*first_add);
+  *second_add = new (allocator) HAdd(Primitive::kPrimInt, *first_add, constant2);
+  block->AddInstruction(*second_add);
+
+  block->AddInstruction(new (allocator) HExit());
+  return graph;
+}
+
+TEST(RegisterAllocatorTest, SameAsFirstInputHint) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HInstruction *first_add, *second_add;
+
+  {
+    HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
+    x86::CodeGeneratorX86 codegen(graph);
+    SsaLivenessAnalysis liveness(*graph, &codegen);
+    liveness.Analyze();
+
+    RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+    register_allocator.AllocateRegisters();
+
+    // Sanity check that in normal conditions, the registers are the same.
+    ASSERT_EQ(first_add->GetLiveInterval()->GetRegister(), 1);
+    ASSERT_EQ(second_add->GetLiveInterval()->GetRegister(), 1);
+  }
+
+  {
+    HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
+    x86::CodeGeneratorX86 codegen(graph);
+    SsaLivenessAnalysis liveness(*graph, &codegen);
+    liveness.Analyze();
+
+    // check that both adds get the same register.
+    // Don't use SetOutput because output is already allocated.
+    first_add->InputAt(0)->GetLocations()->output_ = Location::RegisterLocation(2);
+    ASSERT_EQ(first_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput);
+    ASSERT_EQ(second_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput);
+
+    RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+    register_allocator.AllocateRegisters();
+
+    ASSERT_EQ(first_add->GetLiveInterval()->GetRegister(), 2);
+    ASSERT_EQ(second_add->GetLiveInterval()->GetRegister(), 2);
+  }
+}
+
+static HGraph* BuildDiv(ArenaAllocator* allocator,
+                        HInstruction** div) {
+  HGraph* graph = new (allocator) HGraph(allocator);
+  HBasicBlock* entry = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* first = new (allocator) HParameterValue(0, Primitive::kPrimInt);
+  HInstruction* second = new (allocator) HParameterValue(0, Primitive::kPrimInt);
+  entry->AddInstruction(first);
+  entry->AddInstruction(second);
+
+  HBasicBlock* block = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+
+  *div = new (allocator) HDiv(Primitive::kPrimInt, first, second);
+  block->AddInstruction(*div);
+
+  block->AddInstruction(new (allocator) HExit());
+  return graph;
+}
+
+TEST(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HInstruction *div;
+
+  {
+    HGraph* graph = BuildDiv(&allocator, &div);
+    x86::CodeGeneratorX86 codegen(graph);
+    SsaLivenessAnalysis liveness(*graph, &codegen);
+    liveness.Analyze();
+
+    RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+    register_allocator.AllocateRegisters();
+
+    // div on x86 requires its first input in eax and the output be the same as the first input.
+    ASSERT_EQ(div->GetLiveInterval()->GetRegister(), 0);
+  }
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 471307e..e83c528 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -51,7 +51,7 @@
        !it.Done();
        it.Advance()) {
     HInstruction* current = it.Current();
-    if (current->AsLocal() != nullptr) {
+    if (current->IsLocal()) {
       current->GetBlock()->RemoveInstruction(current);
     }
   }
@@ -109,8 +109,8 @@
         HPhi* phi = new (GetGraph()->GetArena()) HPhi(
             GetGraph()->GetArena(), local, block->GetPredecessors().Size(), Primitive::kPrimVoid);
         for (size_t i = 0; i < block->GetPredecessors().Size(); i++) {
-          HInstruction* value = ValueOfLocal(block->GetPredecessors().Get(i), local);
-          phi->SetRawInputAt(i, value);
+          HInstruction* pred_value = ValueOfLocal(block->GetPredecessors().Get(i), local);
+          phi->SetRawInputAt(i, pred_value);
         }
         block->AddPhi(phi);
         value = phi;
@@ -129,8 +129,112 @@
   }
 }
 
+/**
+ * Constants in the Dex format are not typed. So the builder types them as
+ * integers, but when doing the SSA form, we might realize the constant
+ * is used for floating point operations. We create a floating-point equivalent
+ * constant to make the operations correctly typed.
+ */
+static HFloatConstant* GetFloatEquivalent(HIntConstant* constant) {
+  // We place the floating point constant next to this constant.
+  HFloatConstant* result = constant->GetNext()->AsFloatConstant();
+  if (result == nullptr) {
+    HGraph* graph = constant->GetBlock()->GetGraph();
+    ArenaAllocator* allocator = graph->GetArena();
+    result = new (allocator) HFloatConstant(bit_cast<int32_t, float>(constant->GetValue()));
+    constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext());
+  } else {
+    // If there is already a constant with the expected type, we know it is
+    // the floating point equivalent of this constant.
+    DCHECK_EQ((bit_cast<float, int32_t>(result->GetValue())), constant->GetValue());
+  }
+  return result;
+}
+
+/**
+ * Wide constants in the Dex format are not typed. So the builder types them as
+ * longs, but when doing the SSA form, we might realize the constant
+ * is used for floating point operations. We create a floating-point equivalent
+ * constant to make the operations correctly typed.
+ */
+static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant) {
+  // We place the floating point constant next to this constant.
+  HDoubleConstant* result = constant->GetNext()->AsDoubleConstant();
+  if (result == nullptr) {
+    HGraph* graph = constant->GetBlock()->GetGraph();
+    ArenaAllocator* allocator = graph->GetArena();
+    result = new (allocator) HDoubleConstant(bit_cast<int64_t, double>(constant->GetValue()));
+    constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext());
+  } else {
+    // If there is already a constant with the expected type, we know it is
+    // the floating point equivalent of this constant.
+    DCHECK_EQ((bit_cast<double, int64_t>(result->GetValue())), constant->GetValue());
+  }
+  return result;
+}
+
+/**
+ * Because of Dex format, we might end up having the same phi being
+ * used for non floating point operations and floating point operations. Because
+ * we want the graph to be correctly typed (and thereafter avoid moves between
+ * floating point registers and core registers), we need to create a copy of the
+ * phi with a floating point type.
+ */
+static HPhi* GetFloatOrDoubleEquivalentOfPhi(HPhi* phi, Primitive::Type type) {
+  // We place the floating point phi next to this phi.
+  HInstruction* next = phi->GetNext();
+  if (next == nullptr
+      || (next->GetType() != Primitive::kPrimDouble && next->GetType() != Primitive::kPrimFloat)) {
+    ArenaAllocator* allocator = phi->GetBlock()->GetGraph()->GetArena();
+    HPhi* new_phi = new (allocator) HPhi(allocator, phi->GetRegNumber(), phi->InputCount(), type);
+    for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+      // Copy the inputs. Note that the graph may not be correctly typed by doing this copy,
+      // but the type propagation phase will fix it.
+      new_phi->SetRawInputAt(i, phi->InputAt(i));
+    }
+    phi->GetBlock()->InsertPhiAfter(new_phi, phi);
+    return new_phi;
+  } else {
+    // If there is already a phi with the expected type, we know it is the floating
+    // point equivalent of this phi.
+    DCHECK_EQ(next->AsPhi()->GetRegNumber(), phi->GetRegNumber());
+    return next->AsPhi();
+  }
+}
+
+HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* user,
+                                                     HInstruction* value,
+                                                     Primitive::Type type) {
+  if (value->IsArrayGet()) {
+    // The verifier has checked that values in arrays cannot be used for both
+    // floating point and non-floating point operations. It is therefore safe to just
+    // change the type of the operation.
+    value->AsArrayGet()->SetType(type);
+    return value;
+  } else if (value->IsLongConstant()) {
+    return GetDoubleEquivalent(value->AsLongConstant());
+  } else if (value->IsIntConstant()) {
+    return GetFloatEquivalent(value->AsIntConstant());
+  } else if (value->IsPhi()) {
+    return GetFloatOrDoubleEquivalentOfPhi(value->AsPhi(), type);
+  } else {
+    // For other instructions, we assume the verifier has checked that the dex format is correctly
+    // typed and the value in a dex register will not be used for both floating point and
+    // non-floating point operations. So the only reason an instruction would want a floating
+    // point equivalent is for an unused phi that will be removed by the dead phi elimination phase.
+    DCHECK(user->IsPhi());
+    return value;
+  }
+}
+
 void SsaBuilder::VisitLoadLocal(HLoadLocal* load) {
-  load->ReplaceWith(current_locals_->Get(load->GetLocal()->GetRegNumber()));
+  HInstruction* value = current_locals_->Get(load->GetLocal()->GetRegNumber());
+  if (load->GetType() != value->GetType()
+      && (load->GetType() == Primitive::kPrimFloat || load->GetType() == Primitive::kPrimDouble)) {
+    // If the operation requests a specific type, we make sure its input is of that type.
+    value = GetFloatOrDoubleEquivalent(load, value, load->GetType());
+  }
+  load->ReplaceWith(value);
   load->GetBlock()->RemoveInstruction(load);
 }
 
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 9d8c072..24f5ac5 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -52,6 +52,10 @@
   void VisitStoreLocal(HStoreLocal* store);
   void VisitInstruction(HInstruction* instruction);
 
+  static HInstruction* GetFloatOrDoubleEquivalent(HInstruction* user,
+                                                  HInstruction* instruction,
+                                                  Primitive::Type type);
+
  private:
   // Locals for the current block being visited.
   GrowableArray<HInstruction*>* current_locals_;
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index fbdc0b9..0085b27 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,19 +102,20 @@
   // 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());
+    for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
+      HInstruction* current = inst_it.Current();
+      current->Accept(location_builder);
       LocationSummary* locations = current->GetLocations();
       if (locations != nullptr && locations->Out().IsValid()) {
         instructions_from_ssa_index_.Add(current);
         current->SetSsaIndex(ssa_index++);
         current->SetLiveInterval(
-            new (graph_.GetArena()) LiveInterval(graph_.GetArena(), current->GetType(), current));
+            LiveInterval::MakeInterval(graph_.GetArena(), current->GetType(), current));
       }
       current->SetLifetimePosition(lifetime_position);
     }
@@ -122,15 +124,16 @@
     // Add a null marker to notify we are starting a block.
     instructions_from_lifetime_position_.Add(nullptr);
 
-    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
-      HInstruction* current = it.Current();
+    for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
+         inst_it.Advance()) {
+      HInstruction* current = inst_it.Current();
       current->Accept(codegen_->GetLocationBuilder());
       LocationSummary* locations = current->GetLocations();
       if (locations != nullptr && locations->Out().IsValid()) {
         instructions_from_ssa_index_.Add(current);
         current->SetSsaIndex(ssa_index++);
         current->SetLiveInterval(
-            new (graph_.GetArena()) LiveInterval(graph_.GetArena(), current->GetType(), current));
+            LiveInterval::MakeInterval(graph_.GetArena(), current->GetType(), current));
       }
       instructions_from_lifetime_position_.Add(current);
       current->SetLifetimePosition(lifetime_position);
@@ -176,8 +179,8 @@
       HBasicBlock* successor = block->GetSuccessors().Get(i);
       live_in->Union(GetLiveInSet(*successor));
       size_t phi_input_index = successor->GetPredecessorIndexOf(block);
-      for (HInstructionIterator it(successor->GetPhis()); !it.Done(); it.Advance()) {
-        HInstruction* phi = it.Current();
+      for (HInstructionIterator inst_it(successor->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
+        HInstruction* phi = inst_it.Current();
         HInstruction* input = phi->InputAt(phi_input_index);
         input->GetLiveInterval()->AddPhiUse(phi, phi_input_index, block);
         // A phi input whose last user is the phi dies at the end of the predecessor block,
@@ -187,13 +190,15 @@
     }
 
     // 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());
     }
 
-    for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
-      HInstruction* current = it.Current();
+    for (HBackwardInstructionIterator back_it(block->GetInstructions()); !back_it.Done();
+         back_it.Advance()) {
+      HInstruction* current = back_it.Current();
       if (current->HasSsaIndex()) {
         // Kill the instruction and shorten its interval.
         kill->SetBit(current->GetSsaIndex());
@@ -227,8 +232,8 @@
     }
 
     // Kill phis defined in this block.
-    for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
-      HInstruction* current = it.Current();
+    for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
+      HInstruction* current = inst_it.Current();
       if (current->HasSsaIndex()) {
         kill->SetBit(current->GetSsaIndex());
         live_in->ClearBit(current->GetSsaIndex());
@@ -294,4 +299,144 @@
   return live_in->UnionIfNotIn(live_out, kill);
 }
 
+int LiveInterval::FindFirstRegisterHint(size_t* free_until) const {
+  if (GetParent() == this && defined_by_ != nullptr) {
+    // This is the first interval for the instruction. Try to find
+    // a register based on its definition.
+    DCHECK_EQ(defined_by_->GetLiveInterval(), this);
+    int hint = FindHintAtDefinition();
+    if (hint != kNoRegister && free_until[hint] > GetStart()) {
+      return hint;
+    }
+  }
+
+  UsePosition* use = first_use_;
+  size_t start = GetStart();
+  size_t end = GetEnd();
+  while (use != nullptr && use->GetPosition() <= end) {
+    size_t use_position = use->GetPosition();
+    if (use_position >= start && !use->GetIsEnvironment()) {
+      HInstruction* user = use->GetUser();
+      size_t input_index = use->GetInputIndex();
+      if (user->IsPhi()) {
+        // If the phi has a register, try to use the same.
+        Location phi_location = user->GetLiveInterval()->ToLocation();
+        if (SameRegisterKind(phi_location) && free_until[phi_location.reg()] >= use_position) {
+          return phi_location.reg();
+        }
+        const GrowableArray<HBasicBlock*>& predecessors = user->GetBlock()->GetPredecessors();
+        // If the instruction dies at the phi assignment, we can try having the
+        // same register.
+        if (end == predecessors.Get(input_index)->GetLifetimeEnd()) {
+          for (size_t i = 0, e = user->InputCount(); i < e; ++i) {
+            if (i == input_index) {
+              continue;
+            }
+            HInstruction* input = user->InputAt(i);
+            Location location = input->GetLiveInterval()->GetLocationAt(
+                predecessors.Get(i)->GetLifetimeEnd() - 1);
+            if (location.IsRegister() && free_until[location.reg()] >= use_position) {
+              return location.reg();
+            }
+          }
+        }
+      } else {
+        // If the instruction is expected in a register, try to use it.
+        LocationSummary* locations = user->GetLocations();
+        Location expected = locations->InAt(use->GetInputIndex());
+        // We use the user's lifetime position - 1 (and not `use_position`) because the
+        // register is blocked at the beginning of the user.
+        size_t position = user->GetLifetimePosition() - 1;
+        if (SameRegisterKind(expected) && free_until[expected.reg()] >= position) {
+          return expected.reg();
+        }
+      }
+    }
+    use = use->GetNext();
+  }
+
+  return kNoRegister;
+}
+
+int LiveInterval::FindHintAtDefinition() const {
+  if (defined_by_->IsPhi()) {
+    // Try to use the same register as one of the inputs.
+    const GrowableArray<HBasicBlock*>& predecessors = defined_by_->GetBlock()->GetPredecessors();
+    for (size_t i = 0, e = defined_by_->InputCount(); i < e; ++i) {
+      HInstruction* input = defined_by_->InputAt(i);
+      size_t end = predecessors.Get(i)->GetLifetimeEnd();
+      const LiveInterval& input_interval = input->GetLiveInterval()->GetIntervalAt(end - 1);
+      if (input_interval.GetEnd() == end) {
+        // If the input dies at the end of the predecessor, we know its register can
+        // be reused.
+        Location input_location = input_interval.ToLocation();
+        if (SameRegisterKind(input_location)) {
+          return input_location.reg();
+        }
+      }
+    }
+  } else {
+    LocationSummary* locations = GetDefinedBy()->GetLocations();
+    Location out = locations->Out();
+    if (out.IsUnallocated() && out.GetPolicy() == Location::kSameAsFirstInput) {
+      // Try to use the same register as the first input.
+      const LiveInterval& input_interval =
+          GetDefinedBy()->InputAt(0)->GetLiveInterval()->GetIntervalAt(GetStart() - 1);
+      if (input_interval.GetEnd() == GetStart()) {
+        // If the input dies at the start of this instruction, we know its register can
+        // be reused.
+        Location location = input_interval.ToLocation();
+        if (SameRegisterKind(location)) {
+          return location.reg();
+        }
+      }
+    }
+  }
+  return kNoRegister;
+}
+
+bool LiveInterval::SameRegisterKind(Location other) const {
+  return IsFloatingPoint()
+      ? other.IsFpuRegister()
+      : other.IsRegister();
+}
+
+bool LiveInterval::NeedsTwoSpillSlots() const {
+  return type_ == Primitive::kPrimLong || type_ == Primitive::kPrimDouble;
+}
+
+Location LiveInterval::ToLocation() const {
+  if (HasRegister()) {
+    return IsFloatingPoint()
+        ? Location::FpuRegisterLocation(GetRegister())
+        : Location::RegisterLocation(GetRegister());
+  } else {
+    HInstruction* defined_by = GetParent()->GetDefinedBy();
+    if (defined_by->IsConstant()) {
+      return defined_by->GetLocations()->Out();
+    } else if (GetParent()->HasSpillSlot()) {
+      if (NeedsTwoSpillSlots()) {
+        return Location::DoubleStackSlot(GetParent()->GetSpillSlot());
+      } else {
+        return Location::StackSlot(GetParent()->GetSpillSlot());
+      }
+    } else {
+      return Location();
+    }
+  }
+}
+
+Location LiveInterval::GetLocationAt(size_t position) const {
+  return GetIntervalAt(position).ToLocation();
+}
+
+const LiveInterval& LiveInterval::GetIntervalAt(size_t position) const {
+  const LiveInterval* current = this;
+  while (!current->Covers(position)) {
+    current = current->GetNextSibling();
+    DCHECK(current != nullptr);
+  }
+  return *current;
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 83035b5..ca08d5b 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -23,13 +23,16 @@
 
 class CodeGenerator;
 
-class BlockInfo : public ArenaObject {
+static constexpr int kNoRegister = -1;
+
+class BlockInfo : public ArenaObject<kArenaAllocMisc> {
  public:
   BlockInfo(ArenaAllocator* allocator, const HBasicBlock& block, size_t number_of_ssa_values)
       : block_(block),
         live_in_(allocator, number_of_ssa_values, false),
         live_out_(allocator, number_of_ssa_values, false),
         kill_(allocator, number_of_ssa_values, false) {
+    UNUSED(block_);
     live_in_.ClearAllBits();
     live_out_.ClearAllBits();
     kill_.ClearAllBits();
@@ -47,10 +50,10 @@
 };
 
 /**
- * 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 {
+class LiveRange FINAL : public ArenaObject<kArenaAllocMisc> {
  public:
   LiveRange(size_t start, size_t end, LiveRange* next) : start_(start), end_(end), next_(next) {
     DCHECK_LT(start, end);
@@ -61,16 +64,16 @@
   size_t GetEnd() const { return end_; }
   LiveRange* GetNext() const { return next_; }
 
-  bool IntersectsWith(const LiveRange& other) {
+  bool IntersectsWith(const LiveRange& other) const {
     return (start_ >= other.start_ && start_ < other.end_)
         || (other.start_ >= start_ && other.start_ < end_);
   }
 
-  bool IsBefore(const LiveRange& other) {
+  bool IsBefore(const LiveRange& other) const {
     return end_ <= other.start_;
   }
 
-  void Dump(std::ostream& stream) {
+  void Dump(std::ostream& stream) const {
     stream << "[" << start_ << ", " << end_ << ")";
   }
 
@@ -87,7 +90,7 @@
 /**
  * A use position represents a live interval use at a given position.
  */
-class UsePosition : public ArenaObject {
+class UsePosition : public ArenaObject<kArenaAllocMisc> {
  public:
   UsePosition(HInstruction* user,
               size_t input_index,
@@ -99,13 +102,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 +128,7 @@
   const size_t input_index_;
   const bool is_environment_;
   const size_t position_;
-  UsePosition* const next_;
+  UsePosition* next_;
 
   DISALLOW_COPY_AND_ASSIGN(UsePosition);
 };
@@ -131,49 +137,79 @@
  * An interval is a list of disjoint live ranges where an instruction is live.
  * Each instruction that has uses gets an interval.
  */
-class LiveInterval : public ArenaObject {
+class LiveInterval : public ArenaObject<kArenaAllocMisc> {
  public:
-  LiveInterval(ArenaAllocator* allocator, Primitive::Type type, HInstruction* defined_by = nullptr)
-      : allocator_(allocator),
-        first_range_(nullptr),
-        last_range_(nullptr),
-        first_use_(nullptr),
-        type_(type),
-        next_sibling_(nullptr),
-        parent_(this),
-        register_(kNoRegister),
-        spill_slot_(kNoSpillSlot),
-        is_fixed_(false),
-        defined_by_(defined_by) {}
+  static LiveInterval* MakeInterval(ArenaAllocator* allocator,
+                                    Primitive::Type type,
+                                    HInstruction* instruction = nullptr) {
+    return new (allocator) LiveInterval(allocator, type, instruction);
+  }
+
+  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, Primitive::Type type) {
+    return new (allocator) LiveInterval(allocator, type, nullptr, false, kNoRegister, true);
   }
 
   bool IsFixed() const { return is_fixed_; }
+  bool IsTemp() const { return is_temp_; }
+  bool IsSlowPathSafepoint() const { return is_slow_path_safepoint_; }
+  // This interval is the result of a split.
+  bool IsSplit() const { return parent_ != this; }
 
   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.
+      UsePosition* cursor = first_use_;
+      while ((cursor->GetNext() != nullptr) && (cursor->GetNext()->GetPosition() < position)) {
+        cursor = cursor->GetNext();
+      }
+      DCHECK(first_use_->GetPosition() + 1 == position);
+      UsePosition* new_use = new (allocator_) UsePosition(
+          instruction, input_index, is_environment, position, cursor->GetNext());
+      cursor->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 +217,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 +228,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 +253,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 +285,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 +333,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();
@@ -299,6 +347,10 @@
              || (location.GetPolicy() == Location::kSameAsFirstInput
                  && locations->InAt(0).GetPolicy() == Location::kRequiresRegister)) {
           return position;
+        } else if ((location.GetPolicy() == Location::kRequiresFpuRegister)
+                   || (location.GetPolicy() == Location::kSameAsFirstInput
+                       && locations->InAt(0).GetPolicy() == Location::kRequiresFpuRegister)) {
+          return position;
         }
       }
     }
@@ -307,9 +359,11 @@
     size_t end = GetEnd();
     while (use != nullptr && use->GetPosition() <= end) {
       size_t use_position = use->GetPosition();
-      if (use_position >= position && !use->GetIsEnvironment()) {
+      if (use_position > position && !use->GetIsEnvironment()) {
         Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex());
-        if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) {
+        if (location.IsUnallocated()
+            && (location.GetPolicy() == Location::kRequiresRegister
+                || location.GetPolicy() == Location::kRequiresFpuRegister)) {
           return use_position;
         }
       }
@@ -342,6 +396,7 @@
    * [position ... end)
    */
   LiveInterval* SplitAt(size_t position) {
+    DCHECK(!is_temp_);
     DCHECK(!is_fixed_);
     DCHECK_GT(position, GetStart());
 
@@ -398,12 +453,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 {
@@ -422,11 +477,63 @@
       } while ((use = use->GetNext()) != nullptr);
     }
     stream << "}";
+    stream << " is_fixed: " << is_fixed_ << ", is_split: " << IsSplit();
   }
 
   LiveInterval* GetNextSibling() const { return next_sibling_; }
 
+  // Returns the first register hint that is at least free before
+  // the value contained in `free_until`. If none is found, returns
+  // `kNoRegister`.
+  int FindFirstRegisterHint(size_t* free_until) const;
+
+  // If there is enough at the definition site to find a register (for example
+  // it uses the same input as the first input), returns the register as a hint.
+  // Returns kNoRegister otherwise.
+  int FindHintAtDefinition() const;
+
+  // Returns whether the interval needs two (Dex virtual register size `kVRegSize`)
+  // slots for spilling.
+  bool NeedsTwoSpillSlots() const;
+
+  bool IsFloatingPoint() const {
+    return type_ == Primitive::kPrimFloat || type_ == Primitive::kPrimDouble;
+  }
+
+  // Converts the location of the interval to a `Location` object.
+  Location ToLocation() const;
+
+  // Returns the location of the interval following its siblings at `position`.
+  Location GetLocationAt(size_t position) const;
+
+  // Finds the interval that covers `position`.
+  const LiveInterval& GetIntervalAt(size_t position) const;
+
+  // Returns whether `other` and `this` share the same kind of register.
+  bool SameRegisterKind(Location other) const;
+
  private:
+  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),
+        first_use_(nullptr),
+        type_(type),
+        next_sibling_(nullptr),
+        parent_(this),
+        register_(reg),
+        spill_slot_(kNoSpillSlot),
+        is_fixed_(is_fixed),
+        is_temp_(is_temp),
+        is_slow_path_safepoint_(is_slow_path_safepoint),
+        defined_by_(defined_by) {}
+
   ArenaAllocator* const allocator_;
 
   // Ranges of this interval. We need a quick access to the last range to test
@@ -453,7 +560,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_;
@@ -503,6 +616,12 @@
     return instructions_from_lifetime_position_.Get(index);
   }
 
+  HInstruction* GetTempUser(LiveInterval* temp) const {
+    // A temporary shares the same lifetime start as the instruction that requires it.
+    DCHECK(temp->IsTemp());
+    return GetInstructionFromPosition(temp->GetStart() / 2);
+  }
+
   size_t GetMaxLifetimePosition() const {
     return instructions_from_lifetime_position_.Size() * 2 - 1;
   }
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 13fa03f..56979e1 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -22,18 +22,15 @@
   // Add to the worklist phis referenced by non-phi instructions.
   for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
-    for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
-      HPhi* phi = it.Current()->AsPhi();
-      if (phi->HasEnvironmentUses()) {
-        // TODO: Do we want to keep that phi alive?
-        continue;
-      }
-      for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) {
-        HUseListNode<HInstruction>* current = it.Current();
+    for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
+      HPhi* phi = inst_it.Current()->AsPhi();
+      for (HUseIterator<HInstruction> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
+        HUseListNode<HInstruction>* current = use_it.Current();
         HInstruction* user = current->GetUser();
         if (!user->IsPhi()) {
           worklist_.Add(phi);
           phi->SetLive();
+          break;
         } else {
           phi->SetDead();
         }
@@ -53,8 +50,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 +60,27 @@
     while (current != nullptr) {
       next = current->GetNext();
       if (current->AsPhi()->IsDead()) {
+        if (current->HasUses()) {
+          for (HUseIterator<HInstruction> use_it(current->GetUses()); !use_it.Done();
+               use_it.Advance()) {
+            HUseListNode<HInstruction>* user_node = use_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());
+          }
+        }
+        if (current->HasEnvironmentUses()) {
+          for (HUseIterator<HEnvironment> use_it(current->GetEnvUses()); !use_it.Done();
+               use_it.Advance()) {
+            HUseListNode<HEnvironment>* user_node = use_it.Current();
+            HEnvironment* user = user_node->GetUser();
+            user->SetRawEnvAt(user_node->GetIndex(), nullptr);
+            current->RemoveEnvironmentUser(user, user_node->GetIndex());
+          }
+        }
         block->RemovePhi(current->AsPhi());
       }
       current = next;
@@ -73,8 +92,8 @@
   // Add all phis in the worklist.
   for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
-    for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
-      worklist_.Add(it.Current()->AsPhi());
+    for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
+      worklist_.Add(inst_it.Current()->AsPhi());
     }
   }
 
@@ -88,12 +107,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..3828142 100644
--- a/compiler/optimizing/ssa_type_propagation.cc
+++ b/compiler/optimizing/ssa_type_propagation.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "ssa_builder.h"
 #include "ssa_type_propagation.h"
 
 #include "nodes.h"
@@ -28,21 +29,41 @@
     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;
   }
 }
 
 // Re-compute and update the type of the instruction. Returns
 // whether or not the type was changed.
-static bool UpdateType(HPhi* phi) {
+bool SsaTypePropagation::UpdateType(HPhi* phi) {
   Primitive::Type existing = phi->GetType();
 
-  Primitive::Type new_type = Primitive::kPrimVoid;
+  Primitive::Type new_type = existing;
   for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
     Primitive::Type input_type = phi->InputAt(i)->GetType();
     new_type = MergeTypes(new_type, input_type);
   }
   phi->SetType(new_type);
+
+  if (new_type == Primitive::kPrimDouble || new_type == Primitive::kPrimFloat) {
+    // If the phi is of floating point type, we need to update its inputs to that
+    // type. For inputs that are phis, we need to recompute their types.
+    for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+      HInstruction* input = phi->InputAt(i);
+      if (input->GetType() != new_type) {
+        HInstruction* equivalent = SsaBuilder::GetFloatOrDoubleEquivalent(phi, input, new_type);
+        phi->ReplaceInput(equivalent, i);
+        if (equivalent->IsPhi()) {
+          AddToWorklist(equivalent->AsPhi());
+        }
+      }
+    }
+  }
+
   return existing != new_type;
 }
 
@@ -59,7 +80,12 @@
       HPhi* phi = it.Current()->AsPhi();
       // Set the initial type for the phi. Use the non back edge input for reaching
       // a fixed point faster.
-      phi->SetType(phi->InputAt(0)->GetType());
+      Primitive::Type phi_type = phi->GetType();
+      // We merge with the existing type, that has been set by the SSA builder.
+      DCHECK(phi_type == Primitive::kPrimVoid
+          || phi_type == Primitive::kPrimFloat
+          || phi_type == Primitive::kPrimDouble);
+      phi->SetType(MergeTypes(phi->InputAt(0)->GetType(), phi->GetType()));
       AddToWorklist(phi);
     }
   } else {
diff --git a/compiler/optimizing/ssa_type_propagation.h b/compiler/optimizing/ssa_type_propagation.h
index 5f471a9..f4d3d63 100644
--- a/compiler/optimizing/ssa_type_propagation.h
+++ b/compiler/optimizing/ssa_type_propagation.h
@@ -34,6 +34,7 @@
   void ProcessWorklist();
   void AddToWorklist(HPhi* phi);
   void AddDependentInstructionsToWorklist(HPhi* phi);
+  bool UpdateType(HPhi* phi);
 
   HGraph* const graph_;
   GrowableArray<HPhi*> worklist_;
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 5e1329e..9cfa71c 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -18,17 +18,17 @@
 #define ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_
 
 #include "base/bit_vector.h"
+#include "base/value_object.h"
 #include "memory_region.h"
 #include "stack_map.h"
-#include "utils/allocation.h"
 #include "utils/growable_array.h"
 
 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,42 +156,44 @@
     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(
+      MemoryRegion register_region = dex_register_maps_region.Subregion(
           next_dex_register_map_offset,
           DexRegisterMap::kFixedSize + entry.num_dex_registers * DexRegisterMap::SingleEntrySize());
-      next_dex_register_map_offset += region.size();
-      DexRegisterMap dex_register_map(region);
-      stack_map.SetDexRegisterMapOffset(region.start() - memory_start);
+      next_dex_register_map_offset += register_region.size();
+      DexRegisterMap dex_register_map(register_region);
+      stack_map.SetDexRegisterMapOffset(register_region.start() - memory_start);
 
-      for (size_t i = 0; i < entry.num_dex_registers; ++i) {
+      for (size_t j = 0; j < entry.num_dex_registers; ++j) {
         DexRegisterEntry register_entry =
-            dex_register_maps_.Get(i + entry.dex_register_maps_start_index);
-        dex_register_map.SetRegisterInfo(i, register_entry.kind, register_entry.value);
+            dex_register_maps_.Get(j + entry.dex_register_maps_start_index);
+        dex_register_map.SetRegisterInfo(j, register_entry.kind, register_entry.value);
       }
 
       // Set the inlining info.
       if (entry.inlining_depth != 0) {
-        MemoryRegion region = inline_infos_region.Subregion(
+        MemoryRegion inline_region = inline_infos_region.Subregion(
             next_inline_info_offset,
             InlineInfo::kFixedSize + entry.inlining_depth * InlineInfo::SingleEntrySize());
-        next_inline_info_offset += region.size();
-        InlineInfo inline_info(region);
+        next_inline_info_offset += inline_region.size();
+        InlineInfo inline_info(inline_region);
 
-        stack_map.SetInlineDescriptorOffset(region.start() - memory_start);
+        stack_map.SetInlineDescriptorOffset(inline_region.start() - memory_start);
 
         inline_info.SetDepth(entry.inlining_depth);
-        for (size_t i = 0; i < entry.inlining_depth; ++i) {
-          InlineInfoEntry inline_entry = inline_infos_.Get(i + entry.inline_infos_start_index);
-          inline_info.SetMethodReferenceIndexAtDepth(i, inline_entry.method_index);
+        for (size_t j = 0; j < entry.inlining_depth; ++j) {
+          InlineInfoEntry inline_entry = inline_infos_.Get(j + entry.inline_infos_start_index);
+          inline_info.SetMethodReferenceIndexAtDepth(j, inline_entry.method_index);
         }
       } else {
         stack_map.SetInlineDescriptorOffset(InlineInfo::kNoInlineInfo);
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/output_stream.cc b/compiler/output_stream.cc
new file mode 100644
index 0000000..a8b64ca
--- /dev/null
+++ b/compiler/output_stream.cc
@@ -0,0 +1,31 @@
+/*
+ * 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 "output_stream.h"
+
+namespace art {
+
+std::ostream& operator<<(std::ostream& os, const Whence& rhs) {
+  switch (rhs) {
+    case kSeekSet:     os << "SEEK_SET"; break;
+    case kSeekCurrent: os << "SEEK_CUR"; break;
+    case kSeekEnd:     os << "SEEK_END"; break;
+    default: UNREACHABLE();
+  }
+  return os;
+}
+
+}  // namespace art
diff --git a/compiler/output_stream.h b/compiler/output_stream.h
index 97ccc2c..4d30b83 100644
--- a/compiler/output_stream.h
+++ b/compiler/output_stream.h
@@ -17,9 +17,7 @@
 #ifndef ART_COMPILER_OUTPUT_STREAM_H_
 #define ART_COMPILER_OUTPUT_STREAM_H_
 
-#include <stdint.h>
-#include <sys/types.h>
-
+#include <ostream>
 #include <string>
 
 #include "base/macros.h"
@@ -31,6 +29,7 @@
   kSeekCurrent = SEEK_CUR,
   kSeekEnd = SEEK_END,
 };
+std::ostream& operator<<(std::ostream& os, const Whence& rhs);
 
 class OutputStream {
  public:
diff --git a/compiler/output_stream_test.cc b/compiler/output_stream_test.cc
index 315ca09..bba9892 100644
--- a/compiler/output_stream_test.cc
+++ b/compiler/output_stream_test.cc
@@ -90,7 +90,7 @@
 
 TEST_F(OutputStreamTest, Vector) {
   std::vector<uint8_t> output;
-  VectorOutputStream output_stream("test vector output", output);
+  VectorOutputStream output_stream("test vector output", &output);
   SetOutputStream(output_stream);
   GenerateTestOutput();
   CheckTestOutput(output);
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index d5225c1..733b58f 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"
@@ -62,23 +62,23 @@
 
   switch (abi) {
     case kInterpreterAbi:  // Thread* is first argument (X0) in interpreter ABI.
-      __ JumpTo(Arm64ManagedRegister::FromCoreRegister(X0), Offset(offset.Int32Value()),
-          Arm64ManagedRegister::FromCoreRegister(IP1));
+      __ JumpTo(Arm64ManagedRegister::FromXRegister(X0), Offset(offset.Int32Value()),
+          Arm64ManagedRegister::FromXRegister(IP1));
 
       break;
     case kJniAbi:  // Load via Thread* held in JNIEnv* in first argument (X0).
-      __ LoadRawPtr(Arm64ManagedRegister::FromCoreRegister(IP1),
-                      Arm64ManagedRegister::FromCoreRegister(X0),
+      __ LoadRawPtr(Arm64ManagedRegister::FromXRegister(IP1),
+                      Arm64ManagedRegister::FromXRegister(X0),
                       Offset(JNIEnvExt::SelfOffset().Int32Value()));
 
-      __ JumpTo(Arm64ManagedRegister::FromCoreRegister(IP1), Offset(offset.Int32Value()),
-                Arm64ManagedRegister::FromCoreRegister(IP0));
+      __ JumpTo(Arm64ManagedRegister::FromXRegister(IP1), Offset(offset.Int32Value()),
+                Arm64ManagedRegister::FromXRegister(IP0));
 
       break;
     case kPortableAbi:  // X18 holds Thread*.
     case kQuickAbi:  // Fall-through.
-      __ JumpTo(Arm64ManagedRegister::FromCoreRegister(TR), Offset(offset.Int32Value()),
-                Arm64ManagedRegister::FromCoreRegister(IP0));
+      __ JumpTo(Arm64ManagedRegister::FromXRegister(TR), Offset(offset.Int32Value()),
+                Arm64ManagedRegister::FromXRegister(IP0));
 
       break;
   }
@@ -166,7 +166,7 @@
       return x86_64::CreateTrampoline(offset);
     default:
       LOG(FATAL) << "Unexpected InstructionSet: " << isa;
-      return nullptr;
+      UNREACHABLE();
   }
 }
 
@@ -182,7 +182,7 @@
       return x86::CreateTrampoline(offset);
     default:
       LOG(FATAL) << "Unexpected InstructionSet: " << isa;
-      return nullptr;
+      UNREACHABLE();
   }
 }
 
diff --git a/compiler/utils/allocation.h b/compiler/utils/allocation.h
deleted file mode 100644
index b0947ca..0000000
--- a/compiler/utils/allocation.h
+++ /dev/null
@@ -1,50 +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_UTILS_ALLOCATION_H_
-#define ART_COMPILER_UTILS_ALLOCATION_H_
-
-#include "arena_allocator.h"
-#include "base/logging.h"
-
-namespace art {
-
-class ArenaObject {
- public:
-  // Allocate a new ArenaObject of 'size' bytes in the Arena.
-  void* operator new(size_t size, ArenaAllocator* allocator) {
-    return allocator->Alloc(size, kArenaAllocMisc);
-  }
-
-  void operator delete(void*, size_t) {
-    LOG(FATAL) << "UNREACHABLE";
-  }
-};
-
-class ValueObject {
- public:
-  void* operator new(size_t size) {
-    LOG(FATAL) << "UNREACHABLE";
-    abort();
-  }
-  void operator delete(void*, size_t) {
-    LOG(FATAL) << "UNREACHABLE";
-  }
-};
-
-}  // namespace art
-
-#endif  // ART_COMPILER_UTILS_ALLOCATION_H_
diff --git a/compiler/utils/arena_allocator.cc b/compiler/utils/arena_allocator.cc
index da49524..004af98 100644
--- a/compiler/utils/arena_allocator.cc
+++ b/compiler/utils/arena_allocator.cc
@@ -15,6 +15,7 @@
  */
 
 #include <algorithm>
+#include <iomanip>
 #include <numeric>
 
 #include "arena_allocator.h"
@@ -35,12 +36,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  ",
@@ -102,7 +114,7 @@
        << num_allocations << ", avg size: " << bytes_allocated / num_allocations << "\n";
   }
   os << "===== Allocation by kind\n";
-  COMPILE_ASSERT(arraysize(kAllocNames) == kNumArenaAllocKinds, check_arraysize_kAllocNames);
+  static_assert(arraysize(kAllocNames) == kNumArenaAllocKinds, "arraysize of kAllocNames");
   for (int i = 0; i < kNumArenaAllocKinds; i++) {
       os << kAllocNames[i] << std::setw(10) << alloc_stats_[i] << "\n";
   }
diff --git a/compiler/utils/arena_allocator.h b/compiler/utils/arena_allocator.h
index 7bfbb6f..6d21399 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,
@@ -71,7 +82,7 @@
   ArenaAllocatorStatsImpl& operator = (const ArenaAllocatorStatsImpl& other) = delete;
 
   void Copy(const ArenaAllocatorStatsImpl& other) { UNUSED(other); }
-  void RecordAlloc(size_t bytes, ArenaAllocKind kind) { UNUSED(bytes); UNUSED(kind); }
+  void RecordAlloc(size_t bytes, ArenaAllocKind kind) { UNUSED(bytes, kind); }
   size_t NumAllocations() const { return 0u; }
   size_t BytesAllocated() const { return 0u; }
   void Dump(std::ostream& os, const Arena* first, ssize_t lost_bytes_adjustment) const {
diff --git a/compiler/utils/arena_bit_vector.cc b/compiler/utils/arena_bit_vector.cc
index 39f7d18..f17e5a9 100644
--- a/compiler/utils/arena_bit_vector.cc
+++ b/compiler/utils/arena_bit_vector.cc
@@ -20,7 +20,8 @@
 namespace art {
 
 template <typename ArenaAlloc>
-class ArenaBitVectorAllocator : public Allocator {
+class ArenaBitVectorAllocator FINAL : public Allocator,
+    public ArenaObject<kArenaAllocGrowableBitMap> {
  public:
   explicit ArenaBitVectorAllocator(ArenaAlloc* arena) : arena_(arena) {}
   ~ArenaBitVectorAllocator() {}
@@ -31,13 +32,8 @@
 
   virtual void Free(void*) {}  // Nop.
 
-  static void* operator new(size_t size, ArenaAlloc* arena) {
-    return arena->Alloc(sizeof(ArenaBitVectorAllocator), kArenaAllocGrowableBitMap);
-  }
-  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..34f1ca9 100644
--- a/compiler/utils/arena_bit_vector.h
+++ b/compiler/utils/arena_bit_vector.h
@@ -17,12 +17,14 @@
 #ifndef ART_COMPILER_UTILS_ARENA_BIT_VECTOR_H_
 #define ART_COMPILER_UTILS_ARENA_BIT_VECTOR_H_
 
+#include "arena_object.h"
 #include "base/bit_vector.h"
-#include "utils/arena_allocator.h"
-#include "utils/scoped_arena_allocator.h"
 
 namespace art {
 
+class ArenaAllocator;
+class ScopedArenaAllocator;
+
 // Type of growable bitmap for memory tuning.
 enum OatBitMapKind {
   kBitMapMisc = 0,
@@ -50,24 +52,18 @@
 /*
  * 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() {}
+class ArenaBitVector : public BitVector, public ArenaObject<kArenaAllocGrowableBitMap> {
+ 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);
-  }
-  static void* operator new(size_t size, ScopedArenaAllocator* arena) {
-     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.
+  DISALLOW_COPY_AND_ASSIGN(ArenaBitVector);
 };
 
 
diff --git a/compiler/utils/arena_containers.h b/compiler/utils/arena_containers.h
index c48b0c8..8252591 100644
--- a/compiler/utils/arena_containers.h
+++ b/compiler/utils/arena_containers.h
@@ -66,7 +66,7 @@
 class ArenaAllocatorAdapterKindImpl<false> {
  public:
   // Not tracking allocations, ignore the supplied kind and arbitrarily provide kArenaAllocSTL.
-  explicit ArenaAllocatorAdapterKindImpl(ArenaAllocKind kind) { }
+  explicit ArenaAllocatorAdapterKindImpl(ArenaAllocKind kind) { UNUSED(kind); }
   ArenaAllocatorAdapterKindImpl& operator=(const ArenaAllocatorAdapterKindImpl& other) = default;
   ArenaAllocKind Kind() { return kArenaAllocSTL; }
 };
@@ -159,11 +159,13 @@
   const_pointer address(const_reference x) const { return &x; }
 
   pointer allocate(size_type n, ArenaAllocatorAdapter<void>::pointer hint = nullptr) {
+    UNUSED(hint);
     DCHECK_LE(n, max_size());
     return reinterpret_cast<T*>(arena_allocator_->Alloc(n * sizeof(T),
                                                         ArenaAllocatorAdapterKind::Kind()));
   }
   void deallocate(pointer p, size_type n) {
+    UNUSED(p, n);
   }
 
   void construct(pointer p, const_reference val) {
diff --git a/compiler/utils/arena_object.h b/compiler/utils/arena_object.h
new file mode 100644
index 0000000..d64c419
--- /dev/null
+++ b/compiler/utils/arena_object.h
@@ -0,0 +1,67 @@
+/*
+ * 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_ARENA_OBJECT_H_
+#define ART_COMPILER_UTILS_ARENA_OBJECT_H_
+
+#include "arena_allocator.h"
+#include "base/logging.h"
+#include "scoped_arena_allocator.h"
+
+namespace art {
+
+// Parent for arena allocated objects giving appropriate new and delete operators.
+template<enum ArenaAllocKind kAllocKind>
+class ArenaObject {
+ public:
+  // Allocate a new ArenaObject of 'size' bytes in the Arena.
+  void* operator new(size_t size, ArenaAllocator* allocator) {
+    return allocator->Alloc(size, kAllocKind);
+  }
+
+  static void* operator new(size_t size, ScopedArenaAllocator* arena) {
+    return arena->Alloc(size, kAllocKind);
+  }
+
+  void operator delete(void*, size_t) {
+    LOG(FATAL) << "UNREACHABLE";
+    UNREACHABLE();
+  }
+};
+
+
+// Parent for arena allocated objects that get deleted, gives appropriate new and delete operators.
+// Currently this is used by the quick compiler for debug reference counting arena allocations.
+template<enum ArenaAllocKind kAllocKind>
+class DeletableArenaObject {
+ public:
+  // Allocate a new ArenaObject of 'size' bytes in the Arena.
+  void* operator new(size_t size, ArenaAllocator* allocator) {
+    return allocator->Alloc(size, kAllocKind);
+  }
+
+  static void* operator new(size_t size, ScopedArenaAllocator* arena) {
+    return arena->Alloc(size, kAllocKind);
+  }
+
+  void operator delete(void*, size_t) {
+    // Nop.
+  }
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_ARENA_OBJECT_H_
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 671ccb6..591d461 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;
@@ -301,11 +301,11 @@
   CHECK(IsAbsoluteUint(10, offset));  // In the range -1020 to +1020.
   CHECK_ALIGNED(offset, 2);  // Multiple of 4.
   CHECK((am_ == Offset) || (am_ == NegOffset));
-  uint32_t vencoding = (encoding & (0xf << kRnShift)) | (offset >> 2);
+  uint32_t vencoding_value = (encoding & (0xf << kRnShift)) | (offset >> 2);
   if (am_ == Offset) {
-    vencoding |= 1 << 23;
+    vencoding_value |= 1 << 23;
   }
-  return vencoding;
+  return vencoding_value;
 }
 
 
@@ -324,7 +324,7 @@
       return IsAbsoluteUint(10, offset);  // VFP addressing mode.
     default:
       LOG(FATAL) << "UNREACHABLE";
-      return false;
+      UNREACHABLE();
   }
 }
 
@@ -342,7 +342,7 @@
       return IsAbsoluteUint(10, offset);  // VFP addressing mode.
     default:
       LOG(FATAL) << "UNREACHABLE";
-      return false;
+      UNREACHABLE();
   }
 }
 
@@ -359,9 +359,9 @@
       return IsAbsoluteUint(10, offset);  // VFP addressing mode.
     case kLoadWordPair:
       return IsAbsoluteUint(10, offset);
-  default:
+    default:
       LOG(FATAL) << "UNREACHABLE";
-      return false;
+      UNREACHABLE();
   }
 }
 
@@ -377,16 +377,16 @@
       return IsAbsoluteUint(10, offset);  // VFP addressing mode.
     case kStoreWordPair:
       return IsAbsoluteUint(10, offset);
-  default:
+    default:
       LOG(FATAL) << "UNREACHABLE";
-      return false;
+      UNREACHABLE();
   }
 }
 
 void ArmAssembler::Pad(uint32_t bytes) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   for (uint32_t i = 0; i < bytes; ++i) {
-    buffer_.Emit<byte>(0);
+    buffer_.Emit<uint8_t>(0);
   }
 }
 
@@ -417,9 +417,23 @@
   StoreToOffset(kStoreWord, R0, SP, 0);
 
   // Write out entry spills.
+  int32_t offset = frame_size + sizeof(StackReference<mirror::ArtMethod>);
   for (size_t i = 0; i < entry_spills.size(); ++i) {
-    Register reg = entry_spills.at(i).AsArm().AsCoreRegister();
-    StoreToOffset(kStoreWord, reg, SP, frame_size + kFramePointerSize + (i * kFramePointerSize));
+    ArmManagedRegister reg = entry_spills.at(i).AsArm();
+    if (reg.IsNoRegister()) {
+      // only increment stack offset.
+      ManagedRegisterSpill spill = entry_spills.at(i);
+      offset += spill.getSize();
+    } else if (reg.IsCoreRegister()) {
+      StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
+      offset += 4;
+    } else if (reg.IsSRegister()) {
+      StoreSToOffset(reg.AsSRegister(), SP, offset);
+      offset += 4;
+    } else if (reg.IsDRegister()) {
+      StoreDToOffset(reg.AsDRegister(), SP, offset);
+      offset += 8;
+    }
   }
 }
 
@@ -886,8 +900,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_arm.h b/compiler/utils/arm/assembler_arm.h
index 54965f6..dca2ab7 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/value_object.h"
 #include "constants_arm.h"
 #include "utils/arm/managed_register_arm.h"
 #include "utils/assembler.h"
@@ -179,8 +180,12 @@
   DB_W         = (8|0|1) << 21,  // decrement before with writeback to base
   IB_W         = (8|4|1) << 21   // increment before with writeback to base
 };
+inline std::ostream& operator<<(std::ostream& os, const BlockAddressMode& rhs) {
+  os << static_cast<int>(rhs);
+  return os;
+}
 
-class Address {
+class Address : public ValueObject {
  public:
   // Memory operand addressing mode (in ARM encoding form.  For others we need
   // to adjust)
@@ -260,13 +265,17 @@
   }
 
  private:
-  Register rn_;
-  Register rm_;
-  int32_t offset_;      // Used as shift amount for register offset.
-  Mode am_;
-  bool is_immed_offset_;
-  Shift shift_;
+  const Register rn_;
+  const Register rm_;
+  const int32_t offset_;      // Used as shift amount for register offset.
+  const Mode am_;
+  const bool is_immed_offset_;
+  const Shift shift_;
 };
+inline std::ostream& operator<<(std::ostream& os, const Address::Mode& rhs) {
+  os << static_cast<int>(rhs);
+  return os;
+}
 
 // Instruction encoding bits.
 enum {
@@ -344,10 +353,6 @@
 
 extern const char* kRegisterNames[];
 extern const char* kConditionNames[];
-extern std::ostream& operator<<(std::ostream& os, const Register& rhs);
-extern std::ostream& operator<<(std::ostream& os, const SRegister& rhs);
-extern std::ostream& operator<<(std::ostream& os, const DRegister& rhs);
-extern std::ostream& operator<<(std::ostream& os, const Condition& rhs);
 
 // This is an abstract ARM assembler.  Subclasses provide assemblers for the individual
 // instruction sets (ARM32, Thumb2, etc.)
@@ -448,8 +453,10 @@
   virtual void bkpt(uint16_t imm16) = 0;
   virtual void svc(uint32_t imm24) = 0;
 
-  virtual void it(Condition firstcond, ItState i1 = kItOmitted,
-                  ItState i2 = kItOmitted, ItState i3 = kItOmitted) {
+  virtual void it(Condition firstcond ATTRIBUTE_UNUSED,
+                  ItState i1 ATTRIBUTE_UNUSED = kItOmitted,
+                  ItState i2 ATTRIBUTE_UNUSED = kItOmitted,
+                  ItState i3 ATTRIBUTE_UNUSED = kItOmitted) {
     // Ignored if not supported.
   }
 
@@ -523,6 +530,9 @@
   virtual void blx(Register rm, Condition cond = AL) = 0;
   virtual void bx(Register rm, Condition cond = AL) = 0;
 
+  // Memory barriers.
+  virtual void dmb(DmbOptions flavor) = 0;
+
   void Pad(uint32_t bytes);
 
   // Macros.
@@ -534,14 +544,9 @@
                            Condition cond = AL) = 0;
   virtual void AddConstantSetFlags(Register rd, Register rn, int32_t value,
                                    Condition cond = AL) = 0;
-  virtual void AddConstantWithCarry(Register rd, Register rn, int32_t value,
-                                    Condition cond = AL) = 0;
 
   // Load and Store. May clobber IP.
   virtual void LoadImmediate(Register rd, int32_t value, Condition cond = AL) = 0;
-  virtual void LoadSImmediate(SRegister sd, float value, Condition cond = AL) = 0;
-  virtual void LoadDImmediate(DRegister dd, double value,
-                              Register scratch, Condition cond = AL) = 0;
   virtual void MarkExceptionHandler(Label* label) = 0;
   virtual void LoadFromOffset(LoadOperandType type,
                               Register reg,
@@ -600,7 +605,7 @@
   virtual void Ror(Register rd, Register rm, Register rn, bool setcc = false,
                    Condition cond = AL) = 0;
 
-  static bool IsInstructionForExceptionHandling(uword pc);
+  static bool IsInstructionForExceptionHandling(uintptr_t pc);
 
   virtual void Bind(Label* label) = 0;
 
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 267bba8..c8a57b1 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 |
@@ -1303,7 +1303,6 @@
   }
 }
 
-
 void Arm32Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
   ShifterOperand shifter_op;
   if (ShifterOperand::CanHoldArm(value, &shifter_op)) {
@@ -1356,6 +1355,7 @@
       break;
     default:
       LOG(FATAL) << "UNREACHABLE";
+      UNREACHABLE();
   }
 }
 
@@ -1427,6 +1427,7 @@
       break;
     default:
       LOG(FATAL) << "UNREACHABLE";
+      UNREACHABLE();
   }
 }
 
@@ -1469,19 +1470,24 @@
 
 void Arm32Assembler::MemoryBarrier(ManagedRegister mscratch) {
   CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12);
+  dmb(SY);
+}
+
+
+void Arm32Assembler::dmb(DmbOptions flavor) {
 #if ANDROID_SMP != 0
   int32_t encoding = 0xf57ff05f;  // dmb
-  Emit(encoding);
+  Emit(encoding | flavor);
 #endif
 }
 
 
-void Arm32Assembler::cbz(Register rn, Label* target) {
+void Arm32Assembler::cbz(Register rn ATTRIBUTE_UNUSED, Label* target ATTRIBUTE_UNUSED) {
   LOG(FATAL) << "cbz is not supported on ARM32";
 }
 
 
-void Arm32Assembler::cbnz(Register rn, Label* target) {
+void Arm32Assembler::cbnz(Register rn ATTRIBUTE_UNUSED, Label* target ATTRIBUTE_UNUSED) {
   LOG(FATAL) << "cbnz is not supported on ARM32";
 }
 
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index 7f9094d..dbabb99 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -228,6 +228,8 @@
   void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE;
   void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE;
 
+  // Memory barriers.
+  void dmb(DmbOptions flavor) OVERRIDE;
 
   // Macros.
   // Add signed constant value to rd. May clobber IP.
@@ -236,14 +238,9 @@
                    Condition cond = AL) OVERRIDE;
   void AddConstantSetFlags(Register rd, Register rn, int32_t value,
                            Condition cond = AL) OVERRIDE;
-  void AddConstantWithCarry(Register rd, Register rn, int32_t value,
-                            Condition cond = AL) {}
 
   // Load and Store. May clobber IP.
   void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
-  void LoadSImmediate(SRegister sd, float value, Condition cond = AL) {}
-  void LoadDImmediate(DRegister dd, double value,
-                      Register scratch, Condition cond = AL) {}
   void MarkExceptionHandler(Label* label) OVERRIDE;
   void LoadFromOffset(LoadOperandType type,
                       Register reg,
@@ -273,7 +270,7 @@
                       Condition cond = AL) OVERRIDE;
 
 
-  static bool IsInstructionForExceptionHandling(uword pc);
+  static bool IsInstructionForExceptionHandling(uintptr_t pc);
 
   // Emit data (e.g. encoded instruction or immediate) to the
   // instruction stream.
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 4904428..053e843 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -152,6 +152,8 @@
 
 
 void Thumb2Assembler::mul(Register rd, Register rn, Register rm, Condition cond) {
+  CheckCondition(cond);
+
   if (rd == rm && !IsHighRegister(rd) && !IsHighRegister(rn) && !force_32bit_) {
     // 16 bit.
     int16_t encoding = B14 | B9 | B8 | B6 |
@@ -159,8 +161,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 +178,10 @@
 
 void Thumb2Assembler::mla(Register rd, Register rn, Register rm, Register ra,
                           Condition cond) {
-  uint32_t op1 = 0b000;
-  uint32_t op2 = 0b00;
+  CheckCondition(cond);
+
+  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 +196,10 @@
 
 void Thumb2Assembler::mls(Register rd, Register rn, Register rm, Register ra,
                           Condition cond) {
-  uint32_t op1 = 0b000;
-  uint32_t op2 = 0b01;
+  CheckCondition(cond);
+
+  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 +214,10 @@
 
 void Thumb2Assembler::umull(Register rd_lo, Register rd_hi, Register rn,
                             Register rm, Condition cond) {
-  uint32_t op1 = 0b010;
-  uint32_t op2 = 0b0000;
+  CheckCondition(cond);
+
+  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 +231,10 @@
 
 
 void Thumb2Assembler::sdiv(Register rd, Register rn, Register rm, Condition cond) {
-  uint32_t op1 = 0b001;
-  uint32_t op2 = 0b1111;
+  CheckCondition(cond);
+
+  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 +248,10 @@
 
 
 void Thumb2Assembler::udiv(Register rd, Register rn, Register rm, Condition cond) {
-  uint32_t op1 = 0b001;
-  uint32_t op2 = 0b1111;
+  CheckCondition(cond);
+
+  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 |
@@ -293,6 +305,7 @@
 
 
 void Thumb2Assembler::ldrd(Register rd, const Address& ad, Condition cond) {
+  CheckCondition(cond);
   CHECK_EQ(rd % 2, 0);
   // This is different from other loads.  The encoding is like ARM.
   int32_t encoding = B31 | B30 | B29 | B27 | B22 | B20 |
@@ -304,6 +317,7 @@
 
 
 void Thumb2Assembler::strd(Register rd, const Address& ad, Condition cond) {
+  CheckCondition(cond);
   CHECK_EQ(rd % 2, 0);
   // This is different from other loads.  The encoding is like ARM.
   int32_t encoding = B31 | B30 | B29 | B27 | B22 |
@@ -609,9 +623,9 @@
 }
 
 
-bool Thumb2Assembler::Is32BitDataProcessing(Condition cond,
+bool Thumb2Assembler::Is32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED,
                                             Opcode opcode,
-                                            int set_cc,
+                                            bool set_cc ATTRIBUTE_UNUSED,
                                             Register rn,
                                             Register rd,
                                             const ShifterOperand& so) {
@@ -727,35 +741,35 @@
 }
 
 
-void Thumb2Assembler::Emit32BitDataProcessing(Condition cond,
+void Thumb2Assembler::Emit32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED,
                                               Opcode opcode,
-                                              int set_cc,
+                                              bool set_cc,
                                               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 +778,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 |
@@ -789,7 +803,7 @@
       }
       encoding = B31 | B30 | B29 | B28 |
           thumb_opcode << 21 |
-          set_cc << 20 |
+          (set_cc ? 1 : 0) << 20 |
           rn << 16 |
           rd << 8 |
           imm;
@@ -798,7 +812,7 @@
      // Register (possibly shifted)
      encoding = B31 | B30 | B29 | B27 | B25 |
          thumb_opcode << 21 |
-         set_cc << 20 |
+         (set_cc ? 1 : 0) << 20 |
          rn << 16 |
          rd << 8 |
          so.encodingThumb();
@@ -809,7 +823,7 @@
 
 void Thumb2Assembler::Emit16BitDataProcessing(Condition cond,
                                               Opcode opcode,
-                                              int set_cc,
+                                              bool set_cc,
                                               Register rn,
                                               Register rd,
                                               const ShifterOperand& so) {
@@ -817,9 +831,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 +853,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 +874,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;
   }
 
@@ -936,9 +950,9 @@
 
 
 // ADD and SUB are complex enough to warrant their own emitter.
-void Thumb2Assembler::Emit16BitAddSub(Condition cond,
+void Thumb2Assembler::Emit16BitAddSub(Condition cond ATTRIBUTE_UNUSED,
                                       Opcode opcode,
-                                      int set_cc,
+                                      bool set_cc ATTRIBUTE_UNUSED,
                                       Register rn,
                                       Register rd,
                                       const ShifterOperand& so) {
@@ -962,17 +976,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 +995,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 +1009,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 +1023,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 +1039,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 +1061,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;
          }
@@ -1075,7 +1089,7 @@
 
 void Thumb2Assembler::EmitDataProcessing(Condition cond,
                                          Opcode opcode,
-                                         int set_cc,
+                                         bool set_cc,
                                          Register rn,
                                          Register rd,
                                          const ShifterOperand& so) {
@@ -1094,11 +1108,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 +1120,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 +1128,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 +1150,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 +1166,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 +1218,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 +1318,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 +1336,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 +1349,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;
@@ -1405,7 +1419,7 @@
 
 
 void Thumb2Assembler::EmitMultiMemOp(Condition cond,
-                                     BlockAddressMode am,
+                                     BlockAddressMode bam,
                                      bool load,
                                      Register base,
                                      RegList regs) {
@@ -1417,7 +1431,7 @@
     must_be_32bit = true;
   }
 
-  uint32_t w_bit = am == IA_W || am == DB_W || am == DA_W || am == IB_W;
+  bool w_bit = bam == IA_W || bam == DB_W || bam == DA_W || bam == IB_W;
   // 16 bit always uses writeback.
   if (!w_bit) {
     must_be_32bit = true;
@@ -1425,20 +1439,20 @@
 
   if (must_be_32bit) {
     uint32_t op = 0;
-    switch (am) {
+    switch (bam) {
       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:
       case DA_W:
       case IB_W:
-        LOG(FATAL) << "LDM/STM mode not supported on thumb: " << am;
+        LOG(FATAL) << "LDM/STM mode not supported on thumb: " << bam;
     }
     if (load) {
       // Cannot have SP in the list.
@@ -1534,9 +1548,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 +1571,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 +1652,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 +1854,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 +2006,7 @@
       mask |= ToItMask(i3, firstcond0, 1);
       SetItCondition(i3, firstcond, 3);
       if (i3 != kItOmitted) {
-        mask |= 0b0001;
+        mask |= 1U /* 0b0001 */;
       }
     }
   }
@@ -2107,8 +2121,8 @@
         branch->ResetSize(Branch::k16Bit);
 
         // Now add a compare instruction in the place the branch was.
-        int16_t cmp = B13 | B11 | static_cast<int16_t>(branch->GetRegister()) << 8;
-        buffer_.Store<int16_t>(branch_location, cmp);
+        buffer_.Store<int16_t>(branch_location,
+                               B13 | B11 | static_cast<int16_t>(branch->GetRegister()) << 8);
 
         // Since have moved made a hole in the code we need to reload the
         // current pc.
@@ -2354,7 +2368,6 @@
   }
 }
 
-
 void Thumb2Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
   ShifterOperand shifter_op;
   if (ShifterOperand::CanHoldThumb(rd, R0, MOV, value, &shifter_op)) {
@@ -2378,7 +2391,7 @@
                                      int32_t offset,
                                      Condition cond) {
   if (!Address::CanHoldLoadOffsetThumb(type, offset)) {
-    CHECK(base != IP);
+    CHECK_NE(base, IP);
     LoadImmediate(IP, offset, cond);
     add(IP, IP, ShifterOperand(base), cond);
     base = IP;
@@ -2406,6 +2419,7 @@
       break;
     default:
       LOG(FATAL) << "UNREACHABLE";
+      UNREACHABLE();
   }
 }
 
@@ -2453,12 +2467,26 @@
                                     Register base,
                                     int32_t offset,
                                     Condition cond) {
+  Register tmp_reg = kNoRegister;
   if (!Address::CanHoldStoreOffsetThumb(type, offset)) {
-    CHECK(reg != IP);
-    CHECK(base != IP);
-    LoadImmediate(IP, offset, cond);
-    add(IP, IP, ShifterOperand(base), cond);
-    base = IP;
+    CHECK_NE(base, IP);
+    if (reg != IP) {
+      tmp_reg = IP;
+    } else {
+      // Be careful not to use IP twice (for `reg` and to build the
+      // Address object used by the store instruction(s) below).
+      // Instead, save R5 on the stack (or R6 if R5 is not available),
+      // use it as secondary temporary register, and restore it after
+      // the store instruction has been emitted.
+      tmp_reg = base != R5 ? R5 : R6;
+      Push(tmp_reg);
+      if (base == SP) {
+        offset += kRegisterSize;
+      }
+    }
+    LoadImmediate(tmp_reg, offset, cond);
+    add(tmp_reg, tmp_reg, ShifterOperand(base), cond);
+    base = tmp_reg;
     offset = 0;
   }
   CHECK(Address::CanHoldStoreOffsetThumb(type, offset));
@@ -2477,6 +2505,11 @@
       break;
     default:
       LOG(FATAL) << "UNREACHABLE";
+      UNREACHABLE();
+  }
+  if (tmp_reg != kNoRegister && tmp_reg != IP) {
+    DCHECK(tmp_reg == R5 || tmp_reg == R6);
+    Pop(tmp_reg);
   }
 }
 
@@ -2519,9 +2552,14 @@
 
 void Thumb2Assembler::MemoryBarrier(ManagedRegister mscratch) {
   CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12);
+  dmb(SY);
+}
+
+
+void Thumb2Assembler::dmb(DmbOptions flavor) {
 #if ANDROID_SMP != 0
-  int32_t encoding = 0xf3bf8f5f;  // dmb in T1 encoding.
-  Emit32(encoding);
+  int32_t encoding = 0xf3bf8f50;  // dmb in T1 encoding.
+  Emit32(encoding | flavor);
 #endif
 }
 
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index ee33bf2..9ccdef7 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -259,6 +259,9 @@
   void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE;
   void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE;
 
+  // Memory barriers.
+  void dmb(DmbOptions flavor) OVERRIDE;
+
   // Macros.
   // Add signed constant value to rd. May clobber IP.
   void AddConstant(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
@@ -266,14 +269,9 @@
                    Condition cond = AL) OVERRIDE;
   void AddConstantSetFlags(Register rd, Register rn, int32_t value,
                            Condition cond = AL) OVERRIDE;
-  void AddConstantWithCarry(Register rd, Register rn, int32_t value,
-                            Condition cond = AL) {}
 
   // Load and Store. May clobber IP.
   void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
-  void LoadSImmediate(SRegister sd, float value, Condition cond = AL) {}
-  void LoadDImmediate(DRegister dd, double value,
-                      Register scratch, Condition cond = AL) {}
   void MarkExceptionHandler(Label* label) OVERRIDE;
   void LoadFromOffset(LoadOperandType type,
                       Register reg,
@@ -303,7 +301,7 @@
                       Condition cond = AL) OVERRIDE;
 
 
-  static bool IsInstructionForExceptionHandling(uword pc);
+  static bool IsInstructionForExceptionHandling(uintptr_t pc);
 
   // Emit data (e.g. encoded instruction or immediate) to the.
   // instruction stream.
@@ -321,40 +319,40 @@
  private:
   // Emit a single 32 or 16 bit data processing instruction.
   void EmitDataProcessing(Condition cond,
-                  Opcode opcode,
-                  int set_cc,
-                  Register rn,
-                  Register rd,
-                  const ShifterOperand& so);
+                          Opcode opcode,
+                          bool set_cc,
+                          Register rn,
+                          Register rd,
+                          const ShifterOperand& so);
 
   // Must the instruction be 32 bits or can it possibly be encoded
   // in 16 bits?
   bool Is32BitDataProcessing(Condition cond,
-                  Opcode opcode,
-                  int set_cc,
-                  Register rn,
-                  Register rd,
-                  const ShifterOperand& so);
+                             Opcode opcode,
+                             bool set_cc,
+                             Register rn,
+                             Register rd,
+                             const ShifterOperand& so);
 
   // Emit a 32 bit data processing instruction.
   void Emit32BitDataProcessing(Condition cond,
-                  Opcode opcode,
-                  int set_cc,
-                  Register rn,
-                  Register rd,
-                  const ShifterOperand& so);
+                               Opcode opcode,
+                               bool set_cc,
+                               Register rn,
+                               Register rd,
+                               const ShifterOperand& so);
 
   // Emit a 16 bit data processing instruction.
   void Emit16BitDataProcessing(Condition cond,
-                  Opcode opcode,
-                  int set_cc,
-                  Register rn,
-                  Register rd,
-                  const ShifterOperand& so);
+                               Opcode opcode,
+                               bool set_cc,
+                               Register rn,
+                               Register rd,
+                               const ShifterOperand& so);
 
   void Emit16BitAddSub(Condition cond,
                        Opcode opcode,
-                       int set_cc,
+                       bool set_cc,
                        Register rn,
                        Register rd,
                        const ShifterOperand& so);
@@ -362,12 +360,12 @@
   uint16_t EmitCompareAndBranch(Register rn, uint16_t prev, bool n);
 
   void EmitLoadStore(Condition cond,
-                 bool load,
-                 bool byte,
-                 bool half,
-                 bool is_signed,
-                 Register rd,
-                 const Address& ad);
+                     bool load,
+                     bool byte,
+                     bool half,
+                     bool is_signed,
+                     Register rd,
+                     const Address& ad);
 
   void EmitMemOpAddressMode3(Condition cond,
                              int32_t mode,
diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h
index 3e4cd43..1513296 100644
--- a/compiler/utils/arm/constants_arm.h
+++ b/compiler/utils/arm/constants_arm.h
@@ -38,15 +38,16 @@
 // Constants for specific fields are defined in their respective named enums.
 // General constants are in an anonymous enum in class Instr.
 
-
-// We support both VFPv3-D16 and VFPv3-D32 profiles, but currently only one at
-// a time, so that compile time optimizations can be applied.
-// Warning: VFPv3-D32 is untested.
-#define VFPv3_D16
-#if defined(VFPv3_D16) == defined(VFPv3_D32)
-#error "Exactly one of VFPv3_D16 or VFPv3_D32 can be defined at a time."
-#endif
-
+// 4 bits option for the dmb instruction.
+// Order and values follows those of the ARM Architecture Reference Manual.
+enum DmbOptions {
+  SY = 0xf,
+  ST = 0xe,
+  ISH = 0xb,
+  ISHST = 0xa,
+  NSH = 0x7,
+  NSHST = 0x6
+};
 
 enum ScaleFactor {
   TIMES_1 = 0,
@@ -56,26 +57,23 @@
 };
 
 // Values for double-precision floating point registers.
-enum DRegister {
-  D0  =  0,
-  D1  =  1,
-  D2  =  2,
-  D3  =  3,
-  D4  =  4,
-  D5  =  5,
-  D6  =  6,
-  D7  =  7,
-  D8  =  8,
-  D9  =  9,
+enum DRegister {  // private marker to avoid generate-operator-out.py from processing.
+  D0  = 0,
+  D1  = 1,
+  D2  = 2,
+  D3  = 3,
+  D4  = 4,
+  D5  = 5,
+  D6  = 6,
+  D7  = 7,
+  D8  = 8,
+  D9  = 9,
   D10 = 10,
   D11 = 11,
   D12 = 12,
   D13 = 13,
   D14 = 14,
   D15 = 15,
-#ifdef VFPv3_D16
-  kNumberOfDRegisters = 16,
-#else
   D16 = 16,
   D17 = 17,
   D18 = 18,
@@ -93,7 +91,6 @@
   D30 = 30,
   D31 = 31,
   kNumberOfDRegisters = 32,
-#endif
   kNumberOfOverlappingDRegisters = 16,
   kNoDRegister = -1,
 };
@@ -101,18 +98,18 @@
 
 
 // Values for the condition field as defined in section A3.2.
-enum Condition {
+enum Condition {  // private marker to avoid generate-operator-out.py from processing.
   kNoCondition = -1,
-  EQ =  0,  // equal
-  NE =  1,  // not equal
-  CS =  2,  // carry set/unsigned higher or same
-  CC =  3,  // carry clear/unsigned lower
-  MI =  4,  // minus/negative
-  PL =  5,  // plus/positive or zero
-  VS =  6,  // overflow
-  VC =  7,  // no overflow
-  HI =  8,  // unsigned higher
-  LS =  9,  // unsigned lower or same
+  EQ = 0,   // equal
+  NE = 1,   // not equal
+  CS = 2,   // carry set/unsigned higher or same
+  CC = 3,   // carry clear/unsigned lower
+  MI = 4,   // minus/negative
+  PL = 5,   // plus/positive or zero
+  VS = 6,   // overflow
+  VC = 7,   // no overflow
+  HI = 8,   // unsigned higher
+  LS = 9,   // unsigned lower or same
   GE = 10,  // signed greater than or equal
   LT = 11,  // signed less than
   GT = 12,  // signed greater than
@@ -128,16 +125,16 @@
 // as defined in section A3.4
 enum Opcode {
   kNoOperand = -1,
-  AND =  0,  // Logical AND
-  EOR =  1,  // Logical Exclusive OR
-  SUB =  2,  // Subtract
-  RSB =  3,  // Reverse Subtract
-  ADD =  4,  // Add
-  ADC =  5,  // Add with Carry
-  SBC =  6,  // Subtract with Carry
-  RSC =  7,  // Reverse Subtract with Carry
-  TST =  8,  // Test
-  TEQ =  9,  // Test Equivalence
+  AND = 0,   // Logical AND
+  EOR = 1,   // Logical Exclusive OR
+  SUB = 2,   // Subtract
+  RSB = 3,   // Reverse Subtract
+  ADD = 4,   // Add
+  ADC = 5,   // Add with Carry
+  SBC = 6,   // Subtract with Carry
+  RSC = 7,   // Reverse Subtract with Carry
+  TST = 8,   // Test
+  TEQ = 9,   // Test Equivalence
   CMP = 10,  // Compare
   CMN = 11,  // Compare Negated
   ORR = 12,  // Logical (inclusive) OR
@@ -146,7 +143,7 @@
   MVN = 15,  // Move Not
   kMaxOperand = 16
 };
-
+std::ostream& operator<<(std::ostream& os, const Opcode& rhs);
 
 // Shifter types for Data-processing operands as defined in section A5.1.2.
 enum Shift {
@@ -158,11 +155,11 @@
   RRX = 4,  // Rotate right with extend.
   kMaxShift
 };
-
+std::ostream& operator<<(std::ostream& os, const Shift& rhs);
 
 // Constants used for the decoding or encoding of the individual fields of
 // instructions. Based on the "Figure 3-1 ARM instruction set summary".
-enum InstructionFields {
+enum InstructionFields {  // private marker to avoid generate-operator-out.py from processing.
   kConditionShift = 28,
   kConditionBits = 4,
   kTypeShift = 25,
@@ -223,7 +220,7 @@
 // Example: Test whether the instruction at ptr does set the condition code
 // bits.
 //
-// bool InstructionSetsConditionCodes(byte* ptr) {
+// bool InstructionSetsConditionCodes(uint8_t* ptr) {
 //   Instr* instr = Instr::At(ptr);
 //   int type = instr->TypeField();
 //   return ((type == 0) || (type == 1)) && instr->HasS();
@@ -435,7 +432,7 @@
   // reference to an instruction is to convert a pointer. There is no way
   // to allocate or create instances of class Instr.
   // Use the At(pc) function to create references to Instr.
-  static Instr* At(uword pc) { return reinterpret_cast<Instr*>(pc); }
+  static Instr* At(uintptr_t pc) { return reinterpret_cast<Instr*>(pc); }
   Instr* Next() { return this + kInstrSize; }
 
  private:
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 3f90f21..02011b8 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 {
 
@@ -40,17 +42,17 @@
 }
 
 size_t Arm64Assembler::CodeSize() const {
-  return ___ SizeOfCodeGenerated();
+  return vixl_masm_->BufferCapacity() - vixl_masm_->RemainingBufferSpace();
 }
 
 void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) {
   // Copy the instructions from the buffer.
-  MemoryRegion from(reinterpret_cast<void*>(vixl_buf_), CodeSize());
+  MemoryRegion from(vixl_masm_->GetStartAddress<void*>(), CodeSize());
   region.CopyFrom(0, from);
 }
 
 void Arm64Assembler::GetCurrentThread(ManagedRegister tr) {
-  ___ Mov(reg_x(tr.AsArm64().AsCoreRegister()), reg_x(ETR));
+  ___ Mov(reg_x(tr.AsArm64().AsXRegister()), reg_x(ETR));
 }
 
 void Arm64Assembler::GetCurrentThread(FrameOffset offset, ManagedRegister /* scratch */) {
@@ -69,13 +71,13 @@
   AddConstant(SP, adjust);
 }
 
-void Arm64Assembler::AddConstant(Register rd, int32_t value, Condition cond) {
+void Arm64Assembler::AddConstant(XRegister rd, int32_t value, Condition cond) {
   AddConstant(rd, rd, value, cond);
 }
 
-void Arm64Assembler::AddConstant(Register rd, Register rn, int32_t value,
+void Arm64Assembler::AddConstant(XRegister rd, XRegister 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,12 +87,12 @@
     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);
   }
 }
 
 void Arm64Assembler::StoreWToOffset(StoreOperandType type, WRegister source,
-                                    Register base, int32_t offset) {
+                                    XRegister base, int32_t offset) {
   switch (type) {
     case kStoreByte:
       ___ Strb(reg_w(source), MEM_OP(reg_x(base), offset));
@@ -106,16 +108,16 @@
   }
 }
 
-void Arm64Assembler::StoreToOffset(Register source, Register base, int32_t offset) {
+void Arm64Assembler::StoreToOffset(XRegister source, XRegister base, int32_t offset) {
   CHECK_NE(source, SP);
   ___ Str(reg_x(source), MEM_OP(reg_x(base), offset));
 }
 
-void Arm64Assembler::StoreSToOffset(SRegister source, Register base, int32_t offset) {
+void Arm64Assembler::StoreSToOffset(SRegister source, XRegister base, int32_t offset) {
   ___ Str(reg_s(source), MEM_OP(reg_x(base), offset));
 }
 
-void Arm64Assembler::StoreDToOffset(DRegister source, Register base, int32_t offset) {
+void Arm64Assembler::StoreDToOffset(DRegister source, XRegister base, int32_t offset) {
   ___ Str(reg_d(source), MEM_OP(reg_x(base), offset));
 }
 
@@ -126,9 +128,9 @@
   } else if (src.IsWRegister()) {
     CHECK_EQ(4u, size);
     StoreWToOffset(kStoreWord, src.AsWRegister(), SP, offs.Int32Value());
-  } else if (src.IsCoreRegister()) {
+  } else if (src.IsXRegister()) {
     CHECK_EQ(8u, size);
-    StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value());
+    StoreToOffset(src.AsXRegister(), SP, offs.Int32Value());
   } else if (src.IsSRegister()) {
     StoreSToOffset(src.AsSRegister(), SP, offs.Int32Value());
   } else {
@@ -139,41 +141,41 @@
 
 void Arm64Assembler::StoreRef(FrameOffset offs, ManagedRegister m_src) {
   Arm64ManagedRegister src = m_src.AsArm64();
-  CHECK(src.IsCoreRegister()) << src;
-  StoreWToOffset(kStoreWord, src.AsOverlappingCoreRegisterLow(), SP,
+  CHECK(src.IsXRegister()) << src;
+  StoreWToOffset(kStoreWord, src.AsOverlappingWRegister(), SP,
                  offs.Int32Value());
 }
 
 void Arm64Assembler::StoreRawPtr(FrameOffset offs, ManagedRegister m_src) {
   Arm64ManagedRegister src = m_src.AsArm64();
-  CHECK(src.IsCoreRegister()) << src;
-  StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value());
+  CHECK(src.IsXRegister()) << src;
+  StoreToOffset(src.AsXRegister(), SP, offs.Int32Value());
 }
 
 void Arm64Assembler::StoreImmediateToFrame(FrameOffset offs, uint32_t imm,
                                            ManagedRegister m_scratch) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  LoadImmediate(scratch.AsCoreRegister(), imm);
-  StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), SP,
+  CHECK(scratch.IsXRegister()) << scratch;
+  LoadImmediate(scratch.AsXRegister(), imm);
+  StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), SP,
                  offs.Int32Value());
 }
 
 void Arm64Assembler::StoreImmediateToThread64(ThreadOffset<8> offs, uint32_t imm,
                                             ManagedRegister m_scratch) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  LoadImmediate(scratch.AsCoreRegister(), imm);
-  StoreToOffset(scratch.AsCoreRegister(), ETR, offs.Int32Value());
+  CHECK(scratch.IsXRegister()) << scratch;
+  LoadImmediate(scratch.AsXRegister(), imm);
+  StoreToOffset(scratch.AsXRegister(), ETR, offs.Int32Value());
 }
 
 void Arm64Assembler::StoreStackOffsetToThread64(ThreadOffset<8> tr_offs,
                                               FrameOffset fr_offs,
                                               ManagedRegister m_scratch) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
-  StoreToOffset(scratch.AsCoreRegister(), ETR, tr_offs.Int32Value());
+  CHECK(scratch.IsXRegister()) << scratch;
+  AddConstant(scratch.AsXRegister(), SP, fr_offs.Int32Value());
+  StoreToOffset(scratch.AsXRegister(), ETR, tr_offs.Int32Value());
 }
 
 void Arm64Assembler::StoreStackPointerToThread64(ThreadOffset<8> tr_offs) {
@@ -187,15 +189,15 @@
                                    FrameOffset in_off, ManagedRegister m_scratch) {
   Arm64ManagedRegister source = m_source.AsArm64();
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  StoreToOffset(source.AsCoreRegister(), SP, dest_off.Int32Value());
-  LoadFromOffset(scratch.AsCoreRegister(), SP, in_off.Int32Value());
-  StoreToOffset(scratch.AsCoreRegister(), SP, dest_off.Int32Value() + 8);
+  StoreToOffset(source.AsXRegister(), SP, dest_off.Int32Value());
+  LoadFromOffset(scratch.AsXRegister(), SP, in_off.Int32Value());
+  StoreToOffset(scratch.AsXRegister(), SP, dest_off.Int32Value() + 8);
 }
 
 // Load routines.
-void Arm64Assembler::LoadImmediate(Register dest, int32_t value,
+void Arm64Assembler::LoadImmediate(XRegister 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,15 +207,15 @@
       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);
     }
   }
 }
 
 void Arm64Assembler::LoadWFromOffset(LoadOperandType type, WRegister dest,
-                                     Register base, int32_t offset) {
+                                     XRegister base, int32_t offset) {
   switch (type) {
     case kLoadSignedByte:
       ___ Ldrsb(reg_w(dest), MEM_OP(reg_x(base), offset));
@@ -237,36 +239,36 @@
 
 // Note: We can extend this member by adding load type info - see
 // sign extended A64 load variants.
-void Arm64Assembler::LoadFromOffset(Register dest, Register base,
+void Arm64Assembler::LoadFromOffset(XRegister dest, XRegister base,
                                     int32_t offset) {
   CHECK_NE(dest, SP);
   ___ Ldr(reg_x(dest), MEM_OP(reg_x(base), offset));
 }
 
-void Arm64Assembler::LoadSFromOffset(SRegister dest, Register base,
+void Arm64Assembler::LoadSFromOffset(SRegister dest, XRegister base,
                                      int32_t offset) {
   ___ Ldr(reg_s(dest), MEM_OP(reg_x(base), offset));
 }
 
-void Arm64Assembler::LoadDFromOffset(DRegister dest, Register base,
+void Arm64Assembler::LoadDFromOffset(DRegister dest, XRegister base,
                                      int32_t offset) {
   ___ Ldr(reg_d(dest), MEM_OP(reg_x(base), offset));
 }
 
-void Arm64Assembler::Load(Arm64ManagedRegister dest, Register base,
+void Arm64Assembler::Load(Arm64ManagedRegister dest, XRegister base,
                           int32_t offset, size_t size) {
   if (dest.IsNoRegister()) {
     CHECK_EQ(0u, size) << dest;
   } else if (dest.IsWRegister()) {
     CHECK_EQ(4u, size) << dest;
     ___ Ldr(reg_w(dest.AsWRegister()), MEM_OP(reg_x(base), offset));
-  } else if (dest.IsCoreRegister()) {
-    CHECK_NE(dest.AsCoreRegister(), SP) << dest;
+  } else if (dest.IsXRegister()) {
+    CHECK_NE(dest.AsXRegister(), SP) << dest;
     if (size == 4u) {
-      ___ Ldr(reg_w(dest.AsOverlappingCoreRegisterLow()), MEM_OP(reg_x(base), offset));
+      ___ Ldr(reg_w(dest.AsOverlappingWRegister()), MEM_OP(reg_x(base), offset));
     } else {
       CHECK_EQ(8u, size) << dest;
-      ___ Ldr(reg_x(dest.AsCoreRegister()), MEM_OP(reg_x(base), offset));
+      ___ Ldr(reg_x(dest.AsXRegister()), MEM_OP(reg_x(base), offset));
     }
   } else if (dest.IsSRegister()) {
     ___ Ldr(reg_s(dest.AsSRegister()), MEM_OP(reg_x(base), offset));
@@ -286,33 +288,37 @@
 
 void Arm64Assembler::LoadRef(ManagedRegister m_dst, FrameOffset offs) {
   Arm64ManagedRegister dst = m_dst.AsArm64();
-  CHECK(dst.IsCoreRegister()) << dst;
-  LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), SP, offs.Int32Value());
+  CHECK(dst.IsXRegister()) << dst;
+  LoadWFromOffset(kLoadWord, dst.AsOverlappingWRegister(), SP, offs.Int32Value());
 }
 
 void Arm64Assembler::LoadRef(ManagedRegister m_dst, ManagedRegister m_base,
                              MemberOffset offs) {
   Arm64ManagedRegister dst = m_dst.AsArm64();
   Arm64ManagedRegister base = m_base.AsArm64();
-  CHECK(dst.IsCoreRegister() && base.IsCoreRegister());
-  LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), base.AsCoreRegister(),
+  CHECK(dst.IsXRegister() && base.IsXRegister());
+  LoadWFromOffset(kLoadWord, dst.AsOverlappingWRegister(), base.AsXRegister(),
                   offs.Int32Value());
+  if (kPoisonHeapReferences) {
+    WRegister ref_reg = dst.AsOverlappingWRegister();
+    ___ Neg(reg_w(ref_reg), vixl::Operand(reg_w(ref_reg)));
+  }
 }
 
 void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, Offset offs) {
   Arm64ManagedRegister dst = m_dst.AsArm64();
   Arm64ManagedRegister base = m_base.AsArm64();
-  CHECK(dst.IsCoreRegister() && base.IsCoreRegister());
+  CHECK(dst.IsXRegister() && base.IsXRegister());
   // Remove dst and base form the temp list - higher level API uses IP1, IP0.
   vixl::UseScratchRegisterScope temps(vixl_masm_);
-  temps.Exclude(reg_x(dst.AsCoreRegister()), reg_x(base.AsCoreRegister()));
-  ___ Ldr(reg_x(dst.AsCoreRegister()), MEM_OP(reg_x(base.AsCoreRegister()), offs.Int32Value()));
+  temps.Exclude(reg_x(dst.AsXRegister()), reg_x(base.AsXRegister()));
+  ___ Ldr(reg_x(dst.AsXRegister()), MEM_OP(reg_x(base.AsXRegister()), offs.Int32Value()));
 }
 
 void Arm64Assembler::LoadRawPtrFromThread64(ManagedRegister m_dst, ThreadOffset<8> offs) {
   Arm64ManagedRegister dst = m_dst.AsArm64();
-  CHECK(dst.IsCoreRegister()) << dst;
-  LoadFromOffset(dst.AsCoreRegister(), ETR, offs.Int32Value());
+  CHECK(dst.IsXRegister()) << dst;
+  LoadFromOffset(dst.AsXRegister(), ETR, offs.Int32Value());
 }
 
 // Copying routines.
@@ -320,15 +326,15 @@
   Arm64ManagedRegister dst = m_dst.AsArm64();
   Arm64ManagedRegister src = m_src.AsArm64();
   if (!dst.Equals(src)) {
-    if (dst.IsCoreRegister()) {
+    if (dst.IsXRegister()) {
       if (size == 4) {
         CHECK(src.IsWRegister());
-        ___ Mov(reg_x(dst.AsCoreRegister()), reg_w(src.AsWRegister()));
+        ___ Mov(reg_x(dst.AsXRegister()), reg_w(src.AsWRegister()));
       } else {
-        if (src.IsCoreRegister()) {
-          ___ Mov(reg_x(dst.AsCoreRegister()), reg_x(src.AsCoreRegister()));
+        if (src.IsXRegister()) {
+          ___ Mov(reg_x(dst.AsXRegister()), reg_x(src.AsXRegister()));
         } else {
-          ___ Mov(reg_x(dst.AsCoreRegister()), reg_w(src.AsWRegister()));
+          ___ Mov(reg_x(dst.AsXRegister()), reg_w(src.AsWRegister()));
         }
       }
     } else if (dst.IsWRegister()) {
@@ -349,41 +355,41 @@
                                           ThreadOffset<8> tr_offs,
                                           ManagedRegister m_scratch) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  LoadFromOffset(scratch.AsCoreRegister(), ETR, tr_offs.Int32Value());
-  StoreToOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
+  CHECK(scratch.IsXRegister()) << scratch;
+  LoadFromOffset(scratch.AsXRegister(), ETR, tr_offs.Int32Value());
+  StoreToOffset(scratch.AsXRegister(), SP, fr_offs.Int32Value());
 }
 
 void Arm64Assembler::CopyRawPtrToThread64(ThreadOffset<8> tr_offs,
                                         FrameOffset fr_offs,
                                         ManagedRegister m_scratch) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  LoadFromOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
-  StoreToOffset(scratch.AsCoreRegister(), ETR, tr_offs.Int32Value());
+  CHECK(scratch.IsXRegister()) << scratch;
+  LoadFromOffset(scratch.AsXRegister(), SP, fr_offs.Int32Value());
+  StoreToOffset(scratch.AsXRegister(), ETR, tr_offs.Int32Value());
 }
 
 void Arm64Assembler::CopyRef(FrameOffset dest, FrameOffset src,
                              ManagedRegister m_scratch) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(),
+  CHECK(scratch.IsXRegister()) << scratch;
+  LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(),
                   SP, src.Int32Value());
-  StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(),
+  StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(),
                  SP, dest.Int32Value());
 }
 
 void Arm64Assembler::Copy(FrameOffset dest, FrameOffset src,
                           ManagedRegister m_scratch, size_t size) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(scratch.IsCoreRegister()) << scratch;
+  CHECK(scratch.IsXRegister()) << scratch;
   CHECK(size == 4 || size == 8) << size;
   if (size == 4) {
-    LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, src.Int32Value());
-    StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), SP, dest.Int32Value());
+    LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), SP, src.Int32Value());
+    StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), SP, dest.Int32Value());
   } else if (size == 8) {
-    LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value());
-    StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value());
+    LoadFromOffset(scratch.AsXRegister(), SP, src.Int32Value());
+    StoreToOffset(scratch.AsXRegister(), SP, dest.Int32Value());
   } else {
     UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
   }
@@ -393,16 +399,16 @@
                           ManagedRegister m_scratch, size_t size) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
   Arm64ManagedRegister base = src_base.AsArm64();
-  CHECK(base.IsCoreRegister()) << base;
-  CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch;
+  CHECK(base.IsXRegister()) << base;
+  CHECK(scratch.IsXRegister() || scratch.IsWRegister()) << scratch;
   CHECK(size == 4 || size == 8) << size;
   if (size == 4) {
-    LoadWFromOffset(kLoadWord, scratch.AsWRegister(), base.AsCoreRegister(),
+    LoadWFromOffset(kLoadWord, scratch.AsWRegister(), base.AsXRegister(),
                    src_offset.Int32Value());
     StoreWToOffset(kStoreWord, scratch.AsWRegister(), SP, dest.Int32Value());
   } else if (size == 8) {
-    LoadFromOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), src_offset.Int32Value());
-    StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value());
+    LoadFromOffset(scratch.AsXRegister(), base.AsXRegister(), src_offset.Int32Value());
+    StoreToOffset(scratch.AsXRegister(), SP, dest.Int32Value());
   } else {
     UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
   }
@@ -412,16 +418,16 @@
                           ManagedRegister m_scratch, size_t size) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
   Arm64ManagedRegister base = m_dest_base.AsArm64();
-  CHECK(base.IsCoreRegister()) << base;
-  CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch;
+  CHECK(base.IsXRegister()) << base;
+  CHECK(scratch.IsXRegister() || scratch.IsWRegister()) << scratch;
   CHECK(size == 4 || size == 8) << size;
   if (size == 4) {
     LoadWFromOffset(kLoadWord, scratch.AsWRegister(), SP, src.Int32Value());
-    StoreWToOffset(kStoreWord, scratch.AsWRegister(), base.AsCoreRegister(),
+    StoreWToOffset(kStoreWord, scratch.AsWRegister(), base.AsXRegister(),
                    dest_offs.Int32Value());
   } else if (size == 8) {
-    LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value());
-    StoreToOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), dest_offs.Int32Value());
+    LoadFromOffset(scratch.AsXRegister(), SP, src.Int32Value());
+    StoreToOffset(scratch.AsXRegister(), base.AsXRegister(), dest_offs.Int32Value());
   } else {
     UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
   }
@@ -438,25 +444,25 @@
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
   Arm64ManagedRegister src = m_src.AsArm64();
   Arm64ManagedRegister dest = m_dest.AsArm64();
-  CHECK(dest.IsCoreRegister()) << dest;
-  CHECK(src.IsCoreRegister()) << src;
-  CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch;
+  CHECK(dest.IsXRegister()) << dest;
+  CHECK(src.IsXRegister()) << src;
+  CHECK(scratch.IsXRegister() || scratch.IsWRegister()) << scratch;
   CHECK(size == 4 || size == 8) << size;
   if (size == 4) {
     if (scratch.IsWRegister()) {
-      LoadWFromOffset(kLoadWord, scratch.AsWRegister(), src.AsCoreRegister(),
+      LoadWFromOffset(kLoadWord, scratch.AsWRegister(), src.AsXRegister(),
                     src_offset.Int32Value());
-      StoreWToOffset(kStoreWord, scratch.AsWRegister(), dest.AsCoreRegister(),
+      StoreWToOffset(kStoreWord, scratch.AsWRegister(), dest.AsXRegister(),
                    dest_offset.Int32Value());
     } else {
-      LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), src.AsCoreRegister(),
+      LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), src.AsXRegister(),
                     src_offset.Int32Value());
-      StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), dest.AsCoreRegister(),
+      StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), dest.AsXRegister(),
                    dest_offset.Int32Value());
     }
   } else if (size == 8) {
-    LoadFromOffset(scratch.AsCoreRegister(), src.AsCoreRegister(), src_offset.Int32Value());
-    StoreToOffset(scratch.AsCoreRegister(), dest.AsCoreRegister(), dest_offset.Int32Value());
+    LoadFromOffset(scratch.AsXRegister(), src.AsXRegister(), src_offset.Int32Value());
+    StoreToOffset(scratch.AsXRegister(), dest.AsXRegister(), dest_offset.Int32Value());
   } else {
     UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
   }
@@ -468,7 +474,7 @@
   UNIMPLEMENTED(FATAL) << "Unimplemented Copy() variant";
 }
 
-void Arm64Assembler::MemoryBarrier(ManagedRegister m_scratch) {
+void Arm64Assembler::MemoryBarrier(ManagedRegister m_scratch ATTRIBUTE_UNUSED) {
   // TODO: Should we check that m_scratch is IP? - see arm.
 #if ANDROID_SMP != 0
   ___ Dmb(vixl::InnerShareable, vixl::BarrierAll);
@@ -508,31 +514,31 @@
 void Arm64Assembler::Call(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) {
   Arm64ManagedRegister base = m_base.AsArm64();
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(base.IsCoreRegister()) << base;
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  LoadFromOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), offs.Int32Value());
-  ___ Blr(reg_x(scratch.AsCoreRegister()));
+  CHECK(base.IsXRegister()) << base;
+  CHECK(scratch.IsXRegister()) << scratch;
+  LoadFromOffset(scratch.AsXRegister(), base.AsXRegister(), offs.Int32Value());
+  ___ Blr(reg_x(scratch.AsXRegister()));
 }
 
 void Arm64Assembler::JumpTo(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) {
   Arm64ManagedRegister base = m_base.AsArm64();
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(base.IsCoreRegister()) << base;
-  CHECK(scratch.IsCoreRegister()) << scratch;
+  CHECK(base.IsXRegister()) << base;
+  CHECK(scratch.IsXRegister()) << scratch;
   // Remove base and scratch form the temp list - higher level API uses IP1, IP0.
   vixl::UseScratchRegisterScope temps(vixl_masm_);
-  temps.Exclude(reg_x(base.AsCoreRegister()), reg_x(scratch.AsCoreRegister()));
-  ___ Ldr(reg_x(scratch.AsCoreRegister()), MEM_OP(reg_x(base.AsCoreRegister()), offs.Int32Value()));
-  ___ Br(reg_x(scratch.AsCoreRegister()));
+  temps.Exclude(reg_x(base.AsXRegister()), reg_x(scratch.AsXRegister()));
+  ___ Ldr(reg_x(scratch.AsXRegister()), MEM_OP(reg_x(base.AsXRegister()), offs.Int32Value()));
+  ___ Br(reg_x(scratch.AsXRegister()));
 }
 
 void Arm64Assembler::Call(FrameOffset base, Offset offs, ManagedRegister m_scratch) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(scratch.IsCoreRegister()) << scratch;
+  CHECK(scratch.IsXRegister()) << scratch;
   // Call *(*(SP + base) + offset)
-  LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, base.Int32Value());
-  LoadFromOffset(scratch.AsCoreRegister(), scratch.AsCoreRegister(), offs.Int32Value());
-  ___ Blr(reg_x(scratch.AsCoreRegister()));
+  LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), SP, base.Int32Value());
+  LoadFromOffset(scratch.AsXRegister(), scratch.AsXRegister(), offs.Int32Value());
+  ___ Blr(reg_x(scratch.AsXRegister()));
 }
 
 void Arm64Assembler::CallFromThread64(ThreadOffset<8> /*offset*/, ManagedRegister /*scratch*/) {
@@ -544,59 +550,59 @@
   Arm64ManagedRegister out_reg = m_out_reg.AsArm64();
   Arm64ManagedRegister in_reg = m_in_reg.AsArm64();
   // For now we only hold stale handle scope entries in x registers.
-  CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
-  CHECK(out_reg.IsCoreRegister()) << out_reg;
+  CHECK(in_reg.IsNoRegister() || in_reg.IsXRegister()) << in_reg;
+  CHECK(out_reg.IsXRegister()) << out_reg;
   if (null_allowed) {
     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
     // the address in the handle scope holding the reference.
     // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
     if (in_reg.IsNoRegister()) {
-      LoadWFromOffset(kLoadWord, out_reg.AsOverlappingCoreRegisterLow(), SP,
+      LoadWFromOffset(kLoadWord, out_reg.AsOverlappingWRegister(), SP,
                       handle_scope_offs.Int32Value());
       in_reg = out_reg;
     }
-    ___ Cmp(reg_w(in_reg.AsOverlappingCoreRegisterLow()), 0);
+    ___ Cmp(reg_w(in_reg.AsOverlappingWRegister()), 0);
     if (!out_reg.Equals(in_reg)) {
-      LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
+      LoadImmediate(out_reg.AsXRegister(), 0, eq);
     }
-    AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), NE);
+    AddConstant(out_reg.AsXRegister(), SP, handle_scope_offs.Int32Value(), ne);
   } else {
-    AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), AL);
+    AddConstant(out_reg.AsXRegister(), SP, handle_scope_offs.Int32Value(), al);
   }
 }
 
 void Arm64Assembler::CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handle_scope_offset,
                                      ManagedRegister m_scratch, bool null_allowed) {
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
-  CHECK(scratch.IsCoreRegister()) << scratch;
+  CHECK(scratch.IsXRegister()) << scratch;
   if (null_allowed) {
-    LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP,
+    LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), SP,
                     handle_scope_offset.Int32Value());
     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
     // the address in the handle scope holding the reference.
     // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
-    ___ Cmp(reg_w(scratch.AsOverlappingCoreRegisterLow()), 0);
+    ___ Cmp(reg_w(scratch.AsOverlappingWRegister()), 0);
     // Move this logic in add constants with flags.
-    AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE);
+    AddConstant(scratch.AsXRegister(), SP, handle_scope_offset.Int32Value(), ne);
   } else {
-    AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL);
+    AddConstant(scratch.AsXRegister(), SP, handle_scope_offset.Int32Value(), al);
   }
-  StoreToOffset(scratch.AsCoreRegister(), SP, out_off.Int32Value());
+  StoreToOffset(scratch.AsXRegister(), SP, out_off.Int32Value());
 }
 
 void Arm64Assembler::LoadReferenceFromHandleScope(ManagedRegister m_out_reg,
                                            ManagedRegister m_in_reg) {
   Arm64ManagedRegister out_reg = m_out_reg.AsArm64();
   Arm64ManagedRegister in_reg = m_in_reg.AsArm64();
-  CHECK(out_reg.IsCoreRegister()) << out_reg;
-  CHECK(in_reg.IsCoreRegister()) << in_reg;
+  CHECK(out_reg.IsXRegister()) << out_reg;
+  CHECK(in_reg.IsXRegister()) << in_reg;
   vixl::Label exit;
   if (!out_reg.Equals(in_reg)) {
     // FIXME: Who sets the flags here?
-    LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
+    LoadImmediate(out_reg.AsXRegister(), 0, eq);
   }
-  ___ Cbz(reg_x(in_reg.AsCoreRegister()), &exit);
-  LoadFromOffset(out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0);
+  ___ Cbz(reg_x(in_reg.AsXRegister()), &exit);
+  LoadFromOffset(out_reg.AsXRegister(), in_reg.AsXRegister(), 0);
   ___ Bind(&exit);
 }
 
@@ -605,13 +611,13 @@
   Arm64ManagedRegister scratch = m_scratch.AsArm64();
   Arm64Exception *current_exception = new Arm64Exception(scratch, stack_adjust);
   exception_blocks_.push_back(current_exception);
-  LoadFromOffset(scratch.AsCoreRegister(), ETR, Thread::ExceptionOffset<8>().Int32Value());
-  ___ Cbnz(reg_x(scratch.AsCoreRegister()), current_exception->Entry());
+  LoadFromOffset(scratch.AsXRegister(), ETR, Thread::ExceptionOffset<8>().Int32Value());
+  ___ Cbnz(reg_x(scratch.AsXRegister()), current_exception->Entry());
 }
 
 void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) {
   vixl::UseScratchRegisterScope temps(vixl_masm_);
-  temps.Exclude(reg_x(exception->scratch_.AsCoreRegister()));
+  temps.Exclude(reg_x(exception->scratch_.AsXRegister()));
   vixl::Register temp = temps.AcquireX();
 
   // Bind exception poll entry.
@@ -621,7 +627,7 @@
   }
   // Pass exception object as argument.
   // Don't care about preserving X0 as this won't return.
-  ___ Mov(reg_x(X0), reg_x(exception->scratch_.AsCoreRegister()));
+  ___ Mov(reg_x(X0), reg_x(exception->scratch_.AsXRegister()));
   ___ Ldr(temp, MEM_OP(reg_x(ETR), QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value()));
 
   // Move ETR(Callee saved) back to TR(Caller saved) reg. We use ETR on calls
@@ -640,7 +646,7 @@
                         const std::vector<ManagedRegister>& callee_save_regs,
                         const ManagedRegisterEntrySpills& entry_spills) {
   CHECK_ALIGNED(frame_size, kStackAlignment);
-  CHECK(X0 == method_reg.AsArm64().AsCoreRegister());
+  CHECK(X0 == method_reg.AsArm64().AsXRegister());
 
   // TODO: *create APCS FP - end of FP chain;
   //       *add support for saving a different set of callee regs.
@@ -694,8 +700,8 @@
       // only increment stack offset.
       ManagedRegisterSpill spill = entry_spills.at(i);
       offset += spill.getSize();
-    } else if (reg.IsCoreRegister()) {
-      StoreToOffset(reg.AsCoreRegister(), SP, offset);
+    } else if (reg.IsXRegister()) {
+      StoreToOffset(reg.AsXRegister(), SP, offset);
       offset += 8;
     } else if (reg.IsWRegister()) {
       StoreWToOffset(kStoreWord, reg.AsWRegister(), SP, offset);
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index ab4999a..a69be25 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -27,35 +27,18 @@
 #include "utils/assembler.h"
 #include "offsets.h"
 #include "utils.h"
+
+// TODO: make vixl clean wrt -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
 #include "a64/macro-assembler-a64.h"
 #include "a64/disasm-a64.h"
+#pragma GCC diagnostic pop
 
 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,
@@ -81,12 +64,12 @@
 
 class Arm64Assembler FINAL : public Assembler {
  public:
-  Arm64Assembler() : vixl_buf_(new byte[kBufferSizeArm64]),
-  vixl_masm_(new vixl::MacroAssembler(vixl_buf_, kBufferSizeArm64)) {}
+  // We indicate the size of the initial code generation buffer to the VIXL
+  // assembler. From there we it will automatically manage the buffer.
+  Arm64Assembler() : vixl_masm_(new vixl::MacroAssembler(kArm64BaseBufferSize)) {}
 
   virtual ~Arm64Assembler() {
     delete vixl_masm_;
-    delete[] vixl_buf_;
   }
 
   // Emit slow paths queued during assembly.
@@ -195,7 +178,7 @@
 
  private:
   static vixl::Register reg_x(int code) {
-    CHECK(code < kNumberOfCoreRegisters) << code;
+    CHECK(code < kNumberOfXRegisters) << code;
     if (code == SP) {
       return vixl::sp;
     } else if (code == XZR) {
@@ -205,6 +188,12 @@
   }
 
   static vixl::Register reg_w(int code) {
+    CHECK(code < kNumberOfWRegisters) << code;
+    if (code == WSP) {
+      return vixl::wsp;
+    } else if (code == WZR) {
+      return vixl::wzr;
+    }
     return vixl::Register::WRegFromCode(code);
   }
 
@@ -220,30 +209,28 @@
   void EmitExceptionPoll(Arm64Exception *exception);
 
   void StoreWToOffset(StoreOperandType type, WRegister source,
-                      Register base, int32_t offset);
-  void StoreToOffset(Register source, Register base, int32_t offset);
-  void StoreSToOffset(SRegister source, Register base, int32_t offset);
-  void StoreDToOffset(DRegister source, Register base, int32_t offset);
+                      XRegister base, int32_t offset);
+  void StoreToOffset(XRegister source, XRegister base, int32_t offset);
+  void StoreSToOffset(SRegister source, XRegister base, int32_t offset);
+  void StoreDToOffset(DRegister source, XRegister base, int32_t offset);
 
-  void LoadImmediate(Register dest, int32_t value, Condition cond = AL);
-  void Load(Arm64ManagedRegister dst, Register src, int32_t src_offset, size_t size);
+  void LoadImmediate(XRegister dest, int32_t value, vixl::Condition cond = vixl::al);
+  void Load(Arm64ManagedRegister dst, XRegister 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);
-
-  // Vixl buffer.
-  byte* vixl_buf_;
-
-  // Vixl assembler.
-  vixl::MacroAssembler* vixl_masm_;
+                      XRegister base, int32_t offset);
+  void LoadFromOffset(XRegister dest, XRegister base, int32_t offset);
+  void LoadSFromOffset(SRegister dest, XRegister base, int32_t offset);
+  void LoadDFromOffset(DRegister dest, XRegister base, int32_t offset);
+  void AddConstant(XRegister rd, int32_t value, vixl::Condition cond = vixl::al);
+  void AddConstant(XRegister rd, XRegister rn, int32_t value, vixl::Condition cond = vixl::al);
 
   // List of exception blocks to generate at the end of the code cache.
   std::vector<Arm64Exception*> exception_blocks_;
 
+ public:
+  // Vixl assembler.
+  vixl::MacroAssembler* const vixl_masm_;
+
   // Used for testing.
   friend class Arm64ManagedRegister_VixlRegisters_Test;
 };
diff --git a/compiler/utils/arm64/constants_arm64.h b/compiler/utils/arm64/constants_arm64.h
index 0cbbb1e..ffb54d3 100644
--- a/compiler/utils/arm64/constants_arm64.h
+++ b/compiler/utils/arm64/constants_arm64.h
@@ -31,8 +31,7 @@
 
 constexpr unsigned int kJniRefSpillRegsSize = 11;
 
-// Vixl buffer size.
-constexpr size_t kBufferSizeArm64 = 4096*2;
+constexpr size_t kArm64BaseBufferSize = 4096;
 
 }  // namespace arm64
 }  // namespace art
diff --git a/compiler/utils/arm64/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc
index 8977313..47924bf 100644
--- a/compiler/utils/arm64/managed_register_arm64.cc
+++ b/compiler/utils/arm64/managed_register_arm64.cc
@@ -27,7 +27,7 @@
 //  * [W0, W15]
 //  * [D0, D31]
 //  * [S0, S31]
-// static const int kNumberOfAvailableCoreRegisters = (X15 - X0) + 1;
+// static const int kNumberOfAvailableXRegisters = (X15 - X0) + 1;
 // static const int kNumberOfAvailableWRegisters = (W15 - W0) + 1;
 // static const int kNumberOfAvailableDRegisters = kNumberOfDRegisters;
 // static const int kNumberOfAvailableSRegisters = kNumberOfSRegisters;
@@ -42,22 +42,14 @@
 // 63__________0 D[n]
 bool Arm64ManagedRegister::Overlaps(const Arm64ManagedRegister& other) const {
   if (IsNoRegister() || other.IsNoRegister()) return false;
-  if ((IsGPRegister() && other.IsGPRegister()) ||
-      (IsFPRegister() && other.IsFPRegister())) {
-    return (RegNo() == other.RegNo());
-  }
-  return false;
+  return (IsGPRegister() == other.IsGPRegister()) && (RegNo() == other.RegNo());
 }
 
 int Arm64ManagedRegister::RegNo() const {
   CHECK(!IsNoRegister());
   int no;
-  if (IsCoreRegister()) {
-    if (IsZeroRegister()) {
-      no = static_cast<int>(X31);
-    } else {
-      no = static_cast<int>(AsCoreRegister());
-    }
+  if (IsXRegister()) {
+    no = static_cast<int>(AsXRegister());
   } else if (IsWRegister()) {
     no = static_cast<int>(AsWRegister());
   } else if (IsDRegister()) {
@@ -71,12 +63,12 @@
 }
 
 int Arm64ManagedRegister::RegIdLow() const {
-  CHECK(IsCoreRegister() || IsDRegister());
+  CHECK(IsXRegister() || IsDRegister());
   int low = RegNo();
-  if (IsCoreRegister()) {
-    low += kNumberOfCoreRegIds;
+  if (IsXRegister()) {
+    low += kNumberOfXRegIds;
   } else if (IsDRegister()) {
-    low += kNumberOfCoreRegIds + kNumberOfWRegIds + kNumberOfDRegIds;
+    low += kNumberOfXRegIds + kNumberOfWRegIds + kNumberOfDRegIds;
   }
   return low;
 }
@@ -86,7 +78,7 @@
   CHECK(IsWRegister() || IsSRegister());
   int high = RegNo();
   if (IsSRegister()) {
-    high += kNumberOfCoreRegIds + kNumberOfWRegIds;
+    high += kNumberOfXRegIds + kNumberOfWRegIds;
   }
   return high;
 }
@@ -94,8 +86,8 @@
 void Arm64ManagedRegister::Print(std::ostream& os) const {
   if (!IsValidManagedRegister()) {
     os << "No Register";
-  } else if (IsCoreRegister()) {
-    os << "XCore: " << static_cast<int>(AsCoreRegister());
+  } else if (IsXRegister()) {
+    os << "XCore: " << static_cast<int>(AsXRegister());
   } else if (IsWRegister()) {
     os << "WCore: " << static_cast<int>(AsWRegister());
   } else if (IsDRegister()) {
diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h
index a0f520f..e1d6f31 100644
--- a/compiler/utils/arm64/managed_register_arm64.h
+++ b/compiler/utils/arm64/managed_register_arm64.h
@@ -24,29 +24,29 @@
 namespace art {
 namespace arm64 {
 
-const int kNumberOfCoreRegIds = kNumberOfCoreRegisters;
+const int kNumberOfXRegIds = kNumberOfXRegisters;
 const int kNumberOfWRegIds = kNumberOfWRegisters;
 const int kNumberOfDRegIds = kNumberOfDRegisters;
 const int kNumberOfSRegIds = kNumberOfSRegisters;
 
-const int kNumberOfRegIds = kNumberOfCoreRegIds + kNumberOfWRegIds +
+const int kNumberOfRegIds = kNumberOfXRegIds + kNumberOfWRegIds +
   kNumberOfDRegIds + kNumberOfSRegIds;
 
 // Register ids map:
-//  [0..X[  core registers 64bit (enum Register)
+//  [0..X[  core registers 64bit (enum XRegister)
 //  [X..W[  core registers 32bit (enum WRegister)
 //  [W..D[  double precision VFP registers (enum DRegister)
 //  [D..S[  single precision VFP registers (enum SRegister)
 //
 // where:
-//  X = kNumberOfCoreRegIds
+//  X = kNumberOfXRegIds
 //  W = X + kNumberOfWRegIds
 //  D = W + kNumberOfDRegIds
 //  S = D + kNumberOfSRegIds
 //
 // An instance of class 'ManagedRegister' represents a single Arm64
 // register. A register can be one of the following:
-//  * core register 64bit context (enum Register)
+//  * core register 64bit context (enum XRegister)
 //  * core register 32bit context (enum WRegister)
 //  * VFP double precision register (enum DRegister)
 //  * VFP single precision register (enum SRegister)
@@ -55,76 +55,74 @@
 
 class Arm64ManagedRegister : public ManagedRegister {
  public:
-  Register AsCoreRegister() const {
-    CHECK(IsCoreRegister());
-    return static_cast<Register>(id_);
+  XRegister AsXRegister() const {
+    CHECK(IsXRegister());
+    return static_cast<XRegister>(id_);
   }
 
   WRegister AsWRegister() const {
     CHECK(IsWRegister());
-    return static_cast<WRegister>(id_ - kNumberOfCoreRegIds);
+    return static_cast<WRegister>(id_ - kNumberOfXRegIds);
   }
 
   DRegister AsDRegister() const {
     CHECK(IsDRegister());
-    return static_cast<DRegister>(id_ - kNumberOfCoreRegIds - kNumberOfWRegIds);
+    return static_cast<DRegister>(id_ - kNumberOfXRegIds - kNumberOfWRegIds);
   }
 
   SRegister AsSRegister() const {
     CHECK(IsSRegister());
-    return static_cast<SRegister>(id_ - kNumberOfCoreRegIds - kNumberOfWRegIds -
+    return static_cast<SRegister>(id_ - kNumberOfXRegIds - kNumberOfWRegIds -
                                   kNumberOfDRegIds);
   }
 
-  WRegister AsOverlappingCoreRegisterLow() const {
+  WRegister AsOverlappingWRegister() const {
     CHECK(IsValidManagedRegister());
-    if (IsZeroRegister()) return W31;
-    return static_cast<WRegister>(AsCoreRegister());
+    if (IsZeroRegister()) return WZR;
+    return static_cast<WRegister>(AsXRegister());
   }
 
-  // FIXME: Find better naming.
-  Register AsOverlappingWRegisterCore() const {
+  XRegister AsOverlappingXRegister() const {
     CHECK(IsValidManagedRegister());
-    return static_cast<Register>(AsWRegister());
+    return static_cast<XRegister>(AsWRegister());
   }
 
-  SRegister AsOverlappingDRegisterLow() const {
+  SRegister AsOverlappingSRegister() const {
     CHECK(IsValidManagedRegister());
     return static_cast<SRegister>(AsDRegister());
   }
 
-  // FIXME: Find better naming.
-  DRegister AsOverlappingSRegisterD() const {
+  DRegister AsOverlappingDRegister() const {
     CHECK(IsValidManagedRegister());
     return static_cast<DRegister>(AsSRegister());
   }
 
-  bool IsCoreRegister() const {
+  bool IsXRegister() const {
     CHECK(IsValidManagedRegister());
-    return (0 <= id_) && (id_ < kNumberOfCoreRegIds);
+    return (0 <= id_) && (id_ < kNumberOfXRegIds);
   }
 
   bool IsWRegister() const {
     CHECK(IsValidManagedRegister());
-    const int test = id_ - kNumberOfCoreRegIds;
+    const int test = id_ - kNumberOfXRegIds;
     return (0 <= test) && (test < kNumberOfWRegIds);
   }
 
   bool IsDRegister() const {
     CHECK(IsValidManagedRegister());
-    const int test = id_ - (kNumberOfCoreRegIds + kNumberOfWRegIds);
+    const int test = id_ - (kNumberOfXRegIds + kNumberOfWRegIds);
     return (0 <= test) && (test < kNumberOfDRegIds);
   }
 
   bool IsSRegister() const {
     CHECK(IsValidManagedRegister());
-    const int test = id_ - (kNumberOfCoreRegIds + kNumberOfWRegIds +
+    const int test = id_ - (kNumberOfXRegIds + kNumberOfWRegIds +
                             kNumberOfDRegIds);
     return (0 <= test) && (test < kNumberOfSRegIds);
   }
 
   bool IsGPRegister() const {
-    return IsCoreRegister() || IsWRegister();
+    return IsXRegister() || IsWRegister();
   }
 
   bool IsFPRegister() const {
@@ -134,7 +132,7 @@
   bool IsSameType(Arm64ManagedRegister test) const {
     CHECK(IsValidManagedRegister() && test.IsValidManagedRegister());
     return
-      (IsCoreRegister() && test.IsCoreRegister()) ||
+      (IsXRegister() && test.IsXRegister()) ||
       (IsWRegister() && test.IsWRegister()) ||
       (IsDRegister() && test.IsDRegister()) ||
       (IsSRegister() && test.IsSRegister());
@@ -147,29 +145,29 @@
 
   void Print(std::ostream& os) const;
 
-  static Arm64ManagedRegister FromCoreRegister(Register r) {
+  static Arm64ManagedRegister FromXRegister(XRegister r) {
     CHECK_NE(r, kNoRegister);
     return FromRegId(r);
   }
 
   static Arm64ManagedRegister FromWRegister(WRegister r) {
     CHECK_NE(r, kNoWRegister);
-    return FromRegId(r + kNumberOfCoreRegIds);
+    return FromRegId(r + kNumberOfXRegIds);
   }
 
   static Arm64ManagedRegister FromDRegister(DRegister r) {
     CHECK_NE(r, kNoDRegister);
-    return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfWRegIds));
+    return FromRegId(r + (kNumberOfXRegIds + kNumberOfWRegIds));
   }
 
   static Arm64ManagedRegister FromSRegister(SRegister r) {
     CHECK_NE(r, kNoSRegister);
-    return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfWRegIds +
+    return FromRegId(r + (kNumberOfXRegIds + kNumberOfWRegIds +
                           kNumberOfDRegIds));
   }
 
   // Returns the X register overlapping W register r.
-  static Arm64ManagedRegister FromWRegisterCore(WRegister r) {
+  static Arm64ManagedRegister FromWRegisterX(WRegister r) {
     CHECK_NE(r, kNoWRegister);
     return FromRegId(r);
   }
@@ -177,7 +175,7 @@
   // Return the D register overlapping S register r.
   static Arm64ManagedRegister FromSRegisterD(SRegister r) {
     CHECK_NE(r, kNoSRegister);
-    return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfWRegIds));
+    return FromRegId(r + (kNumberOfXRegIds + kNumberOfWRegIds));
   }
 
  private:
@@ -186,11 +184,11 @@
   }
 
   bool IsStackPointer() const {
-    return IsCoreRegister() && (id_ == SP);
+    return IsXRegister() && (id_ == SP);
   }
 
   bool IsZeroRegister() const {
-    return IsCoreRegister() && (id_ == XZR);
+    return IsXRegister() && (id_ == XZR);
   }
 
   int RegId() const {
diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc
index f149f1b..32c2e62 100644
--- a/compiler/utils/arm64/managed_register_arm64_test.cc
+++ b/compiler/utils/arm64/managed_register_arm64_test.cc
@@ -29,84 +29,84 @@
 }
 
 // X Register test.
-TEST(Arm64ManagedRegister, CoreRegister) {
-  Arm64ManagedRegister reg = Arm64ManagedRegister::FromCoreRegister(X0);
+TEST(Arm64ManagedRegister, XRegister) {
+  Arm64ManagedRegister reg = Arm64ManagedRegister::FromXRegister(X0);
   Arm64ManagedRegister wreg = Arm64ManagedRegister::FromWRegister(W0);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(reg.IsCoreRegister());
+  EXPECT_TRUE(reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(wreg));
-  EXPECT_EQ(X0, reg.AsCoreRegister());
+  EXPECT_EQ(X0, reg.AsXRegister());
 
-  reg = Arm64ManagedRegister::FromCoreRegister(X1);
+  reg = Arm64ManagedRegister::FromXRegister(X1);
   wreg = Arm64ManagedRegister::FromWRegister(W1);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(reg.IsCoreRegister());
+  EXPECT_TRUE(reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(wreg));
-  EXPECT_EQ(X1, reg.AsCoreRegister());
+  EXPECT_EQ(X1, reg.AsXRegister());
 
-  reg = Arm64ManagedRegister::FromCoreRegister(X7);
+  reg = Arm64ManagedRegister::FromXRegister(X7);
   wreg = Arm64ManagedRegister::FromWRegister(W7);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(reg.IsCoreRegister());
+  EXPECT_TRUE(reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(wreg));
-  EXPECT_EQ(X7, reg.AsCoreRegister());
+  EXPECT_EQ(X7, reg.AsXRegister());
 
-  reg = Arm64ManagedRegister::FromCoreRegister(X15);
+  reg = Arm64ManagedRegister::FromXRegister(X15);
   wreg = Arm64ManagedRegister::FromWRegister(W15);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(reg.IsCoreRegister());
+  EXPECT_TRUE(reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(wreg));
-  EXPECT_EQ(X15, reg.AsCoreRegister());
+  EXPECT_EQ(X15, reg.AsXRegister());
 
-  reg = Arm64ManagedRegister::FromCoreRegister(X19);
+  reg = Arm64ManagedRegister::FromXRegister(X19);
   wreg = Arm64ManagedRegister::FromWRegister(W19);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(reg.IsCoreRegister());
+  EXPECT_TRUE(reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(wreg));
-  EXPECT_EQ(X19, reg.AsCoreRegister());
+  EXPECT_EQ(X19, reg.AsXRegister());
 
-  reg = Arm64ManagedRegister::FromCoreRegister(X16);
+  reg = Arm64ManagedRegister::FromXRegister(X16);
   wreg = Arm64ManagedRegister::FromWRegister(W16);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(reg.IsCoreRegister());
+  EXPECT_TRUE(reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(wreg));
-  EXPECT_EQ(IP0, reg.AsCoreRegister());
+  EXPECT_EQ(IP0, reg.AsXRegister());
 
-  reg = Arm64ManagedRegister::FromCoreRegister(SP);
+  reg = Arm64ManagedRegister::FromXRegister(SP);
   wreg = Arm64ManagedRegister::FromWRegister(WZR);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(reg.IsCoreRegister());
+  EXPECT_TRUE(reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
-  EXPECT_TRUE(reg.Overlaps(wreg));
-  EXPECT_EQ(SP, reg.AsCoreRegister());
+  EXPECT_TRUE(!reg.Overlaps(wreg));
+  EXPECT_EQ(SP, reg.AsXRegister());
 }
 
 // W register test.
 TEST(Arm64ManagedRegister, WRegister) {
   Arm64ManagedRegister reg = Arm64ManagedRegister::FromWRegister(W0);
-  Arm64ManagedRegister xreg = Arm64ManagedRegister::FromCoreRegister(X0);
+  Arm64ManagedRegister xreg = Arm64ManagedRegister::FromXRegister(X0);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
@@ -114,9 +114,9 @@
   EXPECT_EQ(W0, reg.AsWRegister());
 
   reg = Arm64ManagedRegister::FromWRegister(W5);
-  xreg = Arm64ManagedRegister::FromCoreRegister(X5);
+  xreg = Arm64ManagedRegister::FromXRegister(X5);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
@@ -124,9 +124,9 @@
   EXPECT_EQ(W5, reg.AsWRegister());
 
   reg = Arm64ManagedRegister::FromWRegister(W6);
-  xreg = Arm64ManagedRegister::FromCoreRegister(X6);
+  xreg = Arm64ManagedRegister::FromXRegister(X6);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
@@ -134,9 +134,9 @@
   EXPECT_EQ(W6, reg.AsWRegister());
 
   reg = Arm64ManagedRegister::FromWRegister(W18);
-  xreg = Arm64ManagedRegister::FromCoreRegister(X18);
+  xreg = Arm64ManagedRegister::FromXRegister(X18);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
@@ -144,9 +144,9 @@
   EXPECT_EQ(W18, reg.AsWRegister());
 
   reg = Arm64ManagedRegister::FromWRegister(W29);
-  xreg = Arm64ManagedRegister::FromCoreRegister(FP);
+  xreg = Arm64ManagedRegister::FromXRegister(FP);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
@@ -154,14 +154,13 @@
   EXPECT_EQ(W29, reg.AsWRegister());
 
   reg = Arm64ManagedRegister::FromWRegister(WZR);
-  xreg = Arm64ManagedRegister::FromCoreRegister(SP);
+  xreg = Arm64ManagedRegister::FromXRegister(SP);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(reg.IsWRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
-  EXPECT_TRUE(reg.Overlaps(xreg));
-  EXPECT_EQ(W31, reg.AsWRegister());
+  EXPECT_TRUE(!reg.Overlaps(xreg));
 }
 
 // D Register test.
@@ -169,49 +168,49 @@
   Arm64ManagedRegister reg = Arm64ManagedRegister::FromDRegister(D0);
   Arm64ManagedRegister sreg = Arm64ManagedRegister::FromSRegister(S0);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(sreg));
   EXPECT_EQ(D0, reg.AsDRegister());
-  EXPECT_EQ(S0, reg.AsOverlappingDRegisterLow());
+  EXPECT_EQ(S0, reg.AsOverlappingSRegister());
   EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D0)));
 
   reg = Arm64ManagedRegister::FromDRegister(D1);
   sreg = Arm64ManagedRegister::FromSRegister(S1);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(sreg));
   EXPECT_EQ(D1, reg.AsDRegister());
-  EXPECT_EQ(S1, reg.AsOverlappingDRegisterLow());
+  EXPECT_EQ(S1, reg.AsOverlappingSRegister());
   EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D1)));
 
   reg = Arm64ManagedRegister::FromDRegister(D20);
   sreg = Arm64ManagedRegister::FromSRegister(S20);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(sreg));
   EXPECT_EQ(D20, reg.AsDRegister());
-  EXPECT_EQ(S20, reg.AsOverlappingDRegisterLow());
+  EXPECT_EQ(S20, reg.AsOverlappingSRegister());
   EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D20)));
 
   reg = Arm64ManagedRegister::FromDRegister(D31);
   sreg = Arm64ManagedRegister::FromSRegister(S31);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(reg.IsDRegister());
   EXPECT_TRUE(!reg.IsSRegister());
   EXPECT_TRUE(reg.Overlaps(sreg));
   EXPECT_EQ(D31, reg.AsDRegister());
-  EXPECT_EQ(S31, reg.AsOverlappingDRegisterLow());
+  EXPECT_EQ(S31, reg.AsOverlappingSRegister());
   EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D31)));
 }
 
@@ -220,101 +219,90 @@
   Arm64ManagedRegister reg = Arm64ManagedRegister::FromSRegister(S0);
   Arm64ManagedRegister dreg = Arm64ManagedRegister::FromDRegister(D0);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(reg.IsSRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(reg.Overlaps(dreg));
   EXPECT_EQ(S0, reg.AsSRegister());
-  EXPECT_EQ(D0, reg.AsOverlappingSRegisterD());
+  EXPECT_EQ(D0, reg.AsOverlappingDRegister());
   EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S0)));
 
   reg = Arm64ManagedRegister::FromSRegister(S5);
   dreg = Arm64ManagedRegister::FromDRegister(D5);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(reg.IsSRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(reg.Overlaps(dreg));
   EXPECT_EQ(S5, reg.AsSRegister());
-  EXPECT_EQ(D5, reg.AsOverlappingSRegisterD());
+  EXPECT_EQ(D5, reg.AsOverlappingDRegister());
   EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S5)));
 
   reg = Arm64ManagedRegister::FromSRegister(S7);
   dreg = Arm64ManagedRegister::FromDRegister(D7);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(reg.IsSRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(reg.Overlaps(dreg));
   EXPECT_EQ(S7, reg.AsSRegister());
-  EXPECT_EQ(D7, reg.AsOverlappingSRegisterD());
+  EXPECT_EQ(D7, reg.AsOverlappingDRegister());
   EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S7)));
 
   reg = Arm64ManagedRegister::FromSRegister(S31);
   dreg = Arm64ManagedRegister::FromDRegister(D31);
   EXPECT_TRUE(!reg.IsNoRegister());
-  EXPECT_TRUE(!reg.IsCoreRegister());
+  EXPECT_TRUE(!reg.IsXRegister());
   EXPECT_TRUE(!reg.IsWRegister());
   EXPECT_TRUE(reg.IsSRegister());
   EXPECT_TRUE(!reg.IsDRegister());
   EXPECT_TRUE(reg.Overlaps(dreg));
   EXPECT_EQ(S31, reg.AsSRegister());
-  EXPECT_EQ(D31, reg.AsOverlappingSRegisterD());
+  EXPECT_EQ(D31, reg.AsOverlappingDRegister());
   EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S31)));
 }
 
 TEST(Arm64ManagedRegister, Equals) {
   ManagedRegister no_reg = ManagedRegister::NoRegister();
   EXPECT_TRUE(no_reg.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromCoreRegister(X1)));
+  EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromXRegister(X1)));
   EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromWRegister(W0)));
   EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromDRegister(D0)));
   EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromSRegister(S0)));
 
-  Arm64ManagedRegister reg_X0 = Arm64ManagedRegister::FromCoreRegister(X0);
+  Arm64ManagedRegister reg_X0 = Arm64ManagedRegister::FromXRegister(X0);
   EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(reg_X0.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromCoreRegister(X1)));
+  EXPECT_TRUE(reg_X0.Equals(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromXRegister(X1)));
   EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromWRegister(W0)));
   EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromDRegister(D0)));
 
-  Arm64ManagedRegister reg_X1 = Arm64ManagedRegister::FromCoreRegister(X1);
+  Arm64ManagedRegister reg_X1 = Arm64ManagedRegister::FromXRegister(X1);
   EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(reg_X1.Equals(Arm64ManagedRegister::FromCoreRegister(X1)));
+  EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(reg_X1.Equals(Arm64ManagedRegister::FromXRegister(X1)));
   EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromDRegister(D0)));
   EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromDRegister(D1)));
   EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromSRegister(S1)));
 
-  Arm64ManagedRegister reg_X31 = Arm64ManagedRegister::FromCoreRegister(X31);
-  EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(SP)));
-  EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(XZR)));
-  EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromWRegister(W31)));
-  EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromWRegister(WZR)));
-  EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromSRegister(S0)));
-  EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromDRegister(D0)));
-
-  Arm64ManagedRegister reg_SP = Arm64ManagedRegister::FromCoreRegister(SP);
+  Arm64ManagedRegister reg_SP = Arm64ManagedRegister::FromXRegister(SP);
   EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(reg_SP.Equals(Arm64ManagedRegister::FromCoreRegister(X31)));
-  EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromCoreRegister(XZR)));
-  EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromWRegister(W31)));
+  EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromXRegister(XZR)));
   EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromDRegister(D0)));
 
   Arm64ManagedRegister reg_W8 = Arm64ManagedRegister::FromWRegister(W8);
   EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromCoreRegister(X8)));
+  EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromXRegister(X8)));
   EXPECT_TRUE(reg_W8.Equals(Arm64ManagedRegister::FromWRegister(W8)));
   EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromDRegister(D0)));
   EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromSRegister(S0)));
@@ -323,8 +311,8 @@
 
   Arm64ManagedRegister reg_W12 = Arm64ManagedRegister::FromWRegister(W12);
   EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromCoreRegister(X8)));
+  EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromXRegister(X8)));
   EXPECT_TRUE(reg_W12.Equals(Arm64ManagedRegister::FromWRegister(W12)));
   EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromDRegister(D0)));
   EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromSRegister(S0)));
@@ -333,8 +321,8 @@
 
   Arm64ManagedRegister reg_S0 = Arm64ManagedRegister::FromSRegister(S0);
   EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromCoreRegister(X1)));
+  EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromXRegister(X1)));
   EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromWRegister(W0)));
   EXPECT_TRUE(reg_S0.Equals(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromSRegister(S1)));
@@ -343,8 +331,8 @@
 
   Arm64ManagedRegister reg_S1 = Arm64ManagedRegister::FromSRegister(S1);
   EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromCoreRegister(X1)));
+  EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromXRegister(X1)));
   EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromWRegister(W0)));
   EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(reg_S1.Equals(Arm64ManagedRegister::FromSRegister(S1)));
@@ -353,8 +341,8 @@
 
   Arm64ManagedRegister reg_S31 = Arm64ManagedRegister::FromSRegister(S31);
   EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromCoreRegister(X1)));
+  EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromXRegister(X1)));
   EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromWRegister(W0)));
   EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(reg_S31.Equals(Arm64ManagedRegister::FromSRegister(S31)));
@@ -363,7 +351,7 @@
 
   Arm64ManagedRegister reg_D0 = Arm64ManagedRegister::FromDRegister(D0);
   EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
+  EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromXRegister(X0)));
   EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromSRegister(S0)));
@@ -373,8 +361,8 @@
 
   Arm64ManagedRegister reg_D15 = Arm64ManagedRegister::FromDRegister(D15);
   EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::NoRegister()));
-  EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromCoreRegister(X1)));
+  EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromXRegister(X1)));
   EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromWRegister(W0)));
   EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromSRegister(S31)));
@@ -384,17 +372,17 @@
 }
 
 TEST(Arm64ManagedRegister, Overlaps) {
-  Arm64ManagedRegister reg = Arm64ManagedRegister::FromCoreRegister(X0);
+  Arm64ManagedRegister reg = Arm64ManagedRegister::FromXRegister(X0);
   Arm64ManagedRegister reg_o = Arm64ManagedRegister::FromWRegister(W0);
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X0)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP)));
+  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X0)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(SP)));
   EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W0)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
-  EXPECT_EQ(X0, reg_o.AsOverlappingWRegisterCore());
-  EXPECT_EQ(W0, reg.AsOverlappingCoreRegisterLow());
+  EXPECT_EQ(X0, reg_o.AsOverlappingXRegister());
+  EXPECT_EQ(W0, reg.AsOverlappingWRegister());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -406,17 +394,17 @@
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15)));
 
-  reg = Arm64ManagedRegister::FromCoreRegister(X10);
+  reg = Arm64ManagedRegister::FromXRegister(X10);
   reg_o = Arm64ManagedRegister::FromWRegister(W10);
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X10)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP)));
+  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X10)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(SP)));
   EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W10)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
-  EXPECT_EQ(X10, reg_o.AsOverlappingWRegisterCore());
-  EXPECT_EQ(W10, reg.AsOverlappingCoreRegisterLow());
+  EXPECT_EQ(X10, reg_o.AsOverlappingXRegister());
+  EXPECT_EQ(W10, reg.AsOverlappingWRegister());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -428,17 +416,17 @@
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15)));
 
-  reg = Arm64ManagedRegister::FromCoreRegister(IP1);
+  reg = Arm64ManagedRegister::FromXRegister(IP1);
   reg_o = Arm64ManagedRegister::FromWRegister(W17);
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X17)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP)));
+  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X17)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(SP)));
   EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W17)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
-  EXPECT_EQ(X17, reg_o.AsOverlappingWRegisterCore());
-  EXPECT_EQ(W17, reg.AsOverlappingCoreRegisterLow());
+  EXPECT_EQ(X17, reg_o.AsOverlappingXRegister());
+  EXPECT_EQ(W17, reg.AsOverlappingWRegister());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -450,19 +438,15 @@
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15)));
 
-  reg = Arm64ManagedRegister::FromCoreRegister(XZR);
+  reg = Arm64ManagedRegister::FromXRegister(XZR);
   reg_o = Arm64ManagedRegister::FromWRegister(WZR);
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1)));
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP)));
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(SP)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W19)));
-  EXPECT_EQ(X31, reg_o.AsOverlappingWRegisterCore());
-  EXPECT_EQ(SP, reg_o.AsOverlappingWRegisterCore());
-  EXPECT_NE(XZR, reg_o.AsOverlappingWRegisterCore());
-  EXPECT_EQ(W31, reg.AsOverlappingCoreRegisterLow());
+  EXPECT_NE(SP, reg_o.AsOverlappingXRegister());
+  EXPECT_EQ(XZR, reg_o.AsOverlappingXRegister());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -474,17 +458,13 @@
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15)));
 
-  reg = Arm64ManagedRegister::FromCoreRegister(SP);
+  reg = Arm64ManagedRegister::FromXRegister(SP);
   reg_o = Arm64ManagedRegister::FromWRegister(WZR);
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15)));
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31)));
-  EXPECT_EQ(X31, reg_o.AsOverlappingWRegisterCore());
-  EXPECT_EQ(W31, reg.AsOverlappingCoreRegisterLow());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -497,15 +477,15 @@
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15)));
 
   reg = Arm64ManagedRegister::FromWRegister(W1);
-  reg_o = Arm64ManagedRegister::FromCoreRegister(X1);
+  reg_o = Arm64ManagedRegister::FromXRegister(X1);
   EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1)));
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15)));
+  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31)));
-  EXPECT_EQ(W1, reg_o.AsOverlappingCoreRegisterLow());
-  EXPECT_EQ(X1, reg.AsOverlappingWRegisterCore());
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30)));
+  EXPECT_EQ(W1, reg_o.AsOverlappingWRegister());
+  EXPECT_EQ(X1, reg.AsOverlappingXRegister());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -518,15 +498,15 @@
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15)));
 
   reg = Arm64ManagedRegister::FromWRegister(W21);
-  reg_o = Arm64ManagedRegister::FromCoreRegister(X21);
+  reg_o = Arm64ManagedRegister::FromXRegister(X21);
   EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W21)));
-  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X21)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15)));
+  EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X21)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31)));
-  EXPECT_EQ(W21, reg_o.AsOverlappingCoreRegisterLow());
-  EXPECT_EQ(X21, reg.AsOverlappingWRegisterCore());
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30)));
+  EXPECT_EQ(W21, reg_o.AsOverlappingWRegister());
+  EXPECT_EQ(X21, reg.AsOverlappingXRegister());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -541,15 +521,15 @@
 
   reg = Arm64ManagedRegister::FromSRegister(S1);
   reg_o = Arm64ManagedRegister::FromDRegister(D1);
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X30)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31)));
-  EXPECT_EQ(S1, reg_o.AsOverlappingDRegisterLow());
-  EXPECT_EQ(D1, reg.AsOverlappingSRegisterD());
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30)));
+  EXPECT_EQ(S1, reg_o.AsOverlappingSRegister());
+  EXPECT_EQ(D1, reg.AsOverlappingDRegister());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -564,15 +544,15 @@
 
   reg = Arm64ManagedRegister::FromSRegister(S15);
   reg_o = Arm64ManagedRegister::FromDRegister(D15);
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X30)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31)));
-  EXPECT_EQ(S15, reg_o.AsOverlappingDRegisterLow());
-  EXPECT_EQ(D15, reg.AsOverlappingSRegisterD());
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30)));
+  EXPECT_EQ(S15, reg_o.AsOverlappingSRegister());
+  EXPECT_EQ(D15, reg.AsOverlappingDRegister());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -587,15 +567,15 @@
 
   reg = Arm64ManagedRegister::FromDRegister(D15);
   reg_o = Arm64ManagedRegister::FromSRegister(S15);
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X30)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1)));
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12)));
-  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31)));
-  EXPECT_EQ(S15, reg.AsOverlappingDRegisterLow());
-  EXPECT_EQ(D15, reg_o.AsOverlappingSRegisterD());
+  EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30)));
+  EXPECT_EQ(S15, reg.AsOverlappingSRegister());
+  EXPECT_EQ(D15, reg_o.AsOverlappingDRegister());
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0)));
   EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15)));
   EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2)));
@@ -642,9 +622,6 @@
   EXPECT_TRUE(vixl::x28.Is(Arm64Assembler::reg_x(X28)));
   EXPECT_TRUE(vixl::x29.Is(Arm64Assembler::reg_x(X29)));
   EXPECT_TRUE(vixl::x30.Is(Arm64Assembler::reg_x(X30)));
-  // FIXME: Take a look here.
-  EXPECT_TRUE(vixl::sp.Is(Arm64Assembler::reg_x(X31)));
-  EXPECT_TRUE(!vixl::x31.Is(Arm64Assembler::reg_x(X31)));
 
   EXPECT_TRUE(vixl::x18.Is(Arm64Assembler::reg_x(TR)));
   EXPECT_TRUE(vixl::ip0.Is(Arm64Assembler::reg_x(IP0)));
@@ -686,8 +663,9 @@
   EXPECT_TRUE(vixl::w28.Is(Arm64Assembler::reg_w(W28)));
   EXPECT_TRUE(vixl::w29.Is(Arm64Assembler::reg_w(W29)));
   EXPECT_TRUE(vixl::w30.Is(Arm64Assembler::reg_w(W30)));
-  EXPECT_TRUE(vixl::w31.Is(Arm64Assembler::reg_w(W31)));
+  EXPECT_TRUE(vixl::w31.Is(Arm64Assembler::reg_w(WZR)));
   EXPECT_TRUE(vixl::wzr.Is(Arm64Assembler::reg_w(WZR)));
+  EXPECT_TRUE(vixl::wsp.Is(Arm64Assembler::reg_w(WSP)));
 
   // D Registers.
   EXPECT_TRUE(vixl::d0.Is(Arm64Assembler::reg_d(D0)));
diff --git a/compiler/utils/array_ref.h b/compiler/utils/array_ref.h
index 2d70b7d..1a7f2e8 100644
--- a/compiler/utils/array_ref.h
+++ b/compiler/utils/array_ref.h
@@ -68,27 +68,24 @@
 
   template <typename U, size_t size>
   constexpr ArrayRef(U (&array)[size],
-                     typename std::enable_if<std::is_same<T, const U>::value, tag>::type t = tag())
+                     typename std::enable_if<std::is_same<T, const U>::value, tag>::type
+                         t ATTRIBUTE_UNUSED = tag())
     : array_(array), size_(size) {
   }
 
-  constexpr ArrayRef(T* array, size_t size)
-      : array_(array), size_(size) {
+  constexpr ArrayRef(T* array_in, size_t size_in)
+      : array_(array_in), size_(size_in) {
   }
 
-  template <typename U>
-  constexpr ArrayRef(U* array, size_t size,
-                     typename std::enable_if<std::is_same<T, const U>::value, tag>::type t = tag())
-      : 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,
-           typename std::enable_if<std::is_same<T, const U>::value, tag>::tag t = tag())
+  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 ATTRIBUTE_UNUSED = tag())
       : array_(v.data()), size_(v.size()) {
   }
 
@@ -167,6 +164,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..6834512 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -30,8 +30,8 @@
 
 namespace art {
 
-static byte* NewContents(size_t capacity) {
-  return new byte[capacity];
+static uint8_t* NewContents(size_t capacity) {
+  return new uint8_t[capacity];
 }
 
 
@@ -85,13 +85,14 @@
   size_t new_capacity = std::min(old_capacity * 2, old_capacity + 1 * MB);
 
   // Allocate the new data area and copy contents of the old one to it.
-  byte* new_contents = NewContents(new_capacity);
+  uint8_t* new_contents = NewContents(new_capacity);
   memmove(reinterpret_cast<void*>(new_contents),
           reinterpret_cast<void*>(contents_),
           old_size);
 
   // 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.
@@ -124,77 +125,91 @@
   }
 }
 
-void Assembler::StoreImmediateToThread32(ThreadOffset<4> dest, uint32_t imm,
-                                         ManagedRegister scratch) {
+void Assembler::StoreImmediateToThread32(ThreadOffset<4> dest ATTRIBUTE_UNUSED,
+                                         uint32_t imm ATTRIBUTE_UNUSED,
+                                         ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::StoreImmediateToThread64(ThreadOffset<8> dest, uint32_t imm,
-                                         ManagedRegister scratch) {
+void Assembler::StoreImmediateToThread64(ThreadOffset<8> dest ATTRIBUTE_UNUSED,
+                                         uint32_t imm ATTRIBUTE_UNUSED,
+                                         ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::StoreStackOffsetToThread32(ThreadOffset<4> thr_offs,
-                                           FrameOffset fr_offs,
-                                           ManagedRegister scratch) {
+void Assembler::StoreStackOffsetToThread32(ThreadOffset<4> thr_offs ATTRIBUTE_UNUSED,
+                                           FrameOffset fr_offs ATTRIBUTE_UNUSED,
+                                           ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::StoreStackOffsetToThread64(ThreadOffset<8> thr_offs,
-                                           FrameOffset fr_offs,
-                                           ManagedRegister scratch) {
+void Assembler::StoreStackOffsetToThread64(ThreadOffset<8> thr_offs ATTRIBUTE_UNUSED,
+                                           FrameOffset fr_offs ATTRIBUTE_UNUSED,
+                                           ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::StoreStackPointerToThread32(ThreadOffset<4> thr_offs) {
+void Assembler::StoreStackPointerToThread32(ThreadOffset<4> thr_offs ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::StoreStackPointerToThread64(ThreadOffset<8> thr_offs) {
+void Assembler::StoreStackPointerToThread64(ThreadOffset<8> thr_offs ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::LoadFromThread32(ManagedRegister dest, ThreadOffset<4> src, size_t size) {
+void Assembler::LoadFromThread32(ManagedRegister dest ATTRIBUTE_UNUSED,
+                                 ThreadOffset<4> src ATTRIBUTE_UNUSED,
+                                 size_t size ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::LoadFromThread64(ManagedRegister dest, ThreadOffset<8> src, size_t size) {
+void Assembler::LoadFromThread64(ManagedRegister dest ATTRIBUTE_UNUSED,
+                                 ThreadOffset<8> src ATTRIBUTE_UNUSED,
+                                 size_t size ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::LoadRawPtrFromThread32(ManagedRegister dest, ThreadOffset<4> offs) {
+void Assembler::LoadRawPtrFromThread32(ManagedRegister dest ATTRIBUTE_UNUSED,
+                                       ThreadOffset<4> offs ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::LoadRawPtrFromThread64(ManagedRegister dest, ThreadOffset<8> offs) {
+void Assembler::LoadRawPtrFromThread64(ManagedRegister dest ATTRIBUTE_UNUSED,
+                                       ThreadOffset<8> offs ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::CopyRawPtrFromThread32(FrameOffset fr_offs, ThreadOffset<4> thr_offs,
-                                       ManagedRegister scratch) {
+void Assembler::CopyRawPtrFromThread32(FrameOffset fr_offs ATTRIBUTE_UNUSED,
+                                       ThreadOffset<4> thr_offs ATTRIBUTE_UNUSED,
+                                       ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::CopyRawPtrFromThread64(FrameOffset fr_offs, ThreadOffset<8> thr_offs,
-                                       ManagedRegister scratch) {
+void Assembler::CopyRawPtrFromThread64(FrameOffset fr_offs ATTRIBUTE_UNUSED,
+                                       ThreadOffset<8> thr_offs ATTRIBUTE_UNUSED,
+                                       ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::CopyRawPtrToThread32(ThreadOffset<4> thr_offs, FrameOffset fr_offs,
-                                     ManagedRegister scratch) {
+void Assembler::CopyRawPtrToThread32(ThreadOffset<4> thr_offs ATTRIBUTE_UNUSED,
+                                     FrameOffset fr_offs ATTRIBUTE_UNUSED,
+                                     ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::CopyRawPtrToThread64(ThreadOffset<8> thr_offs, FrameOffset fr_offs,
-                                     ManagedRegister scratch) {
+void Assembler::CopyRawPtrToThread64(ThreadOffset<8> thr_offs ATTRIBUTE_UNUSED,
+                                     FrameOffset fr_offs ATTRIBUTE_UNUSED,
+                                     ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::CallFromThread32(ThreadOffset<4> offset, ManagedRegister scratch) {
+void Assembler::CallFromThread32(ThreadOffset<4> offset ATTRIBUTE_UNUSED,
+                                 ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
-void Assembler::CallFromThread64(ThreadOffset<8> offset, ManagedRegister scratch) {
+void Assembler::CallFromThread64(ThreadOffset<8> offset ATTRIBUTE_UNUSED,
+                                 ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
 
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index f72f5e5..ad7e98d 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -56,19 +56,19 @@
 
 class ExternalLabel {
  public:
-  ExternalLabel(const char* name, uword address)
-      : name_(name), address_(address) {
-    DCHECK(name != nullptr);
+  ExternalLabel(const char* name_in, uintptr_t address_in)
+      : name_(name_in), address_(address_in) {
+    DCHECK(name_in != nullptr);
   }
 
   const char* name() const { return name_; }
-  uword address() const {
+  uintptr_t address() const {
     return address_;
   }
 
  private:
   const char* name_;
-  const uword address_;
+  const uintptr_t address_;
 };
 
 class Label {
@@ -84,12 +84,12 @@
   // for unused labels.
   int Position() const {
     CHECK(!IsUnused());
-    return IsBound() ? -position_ - kPointerSize : position_ - kPointerSize;
+    return IsBound() ? -position_ - sizeof(void*) : position_ - sizeof(void*);
   }
 
   int LinkPosition() const {
     CHECK(IsLinked());
-    return position_ - kPointerSize;
+    return position_ - sizeof(void*);
   }
 
   bool IsBound() const { return position_ < 0; }
@@ -105,19 +105,20 @@
 
   void BindTo(int position) {
     CHECK(!IsBound());
-    position_ = -position - kPointerSize;
+    position_ = -position - sizeof(void*);
     CHECK(IsBound());
   }
 
   void LinkTo(int position) {
     CHECK(!IsBound());
-    position_ = position + kPointerSize;
+    position_ = position + sizeof(void*);
     CHECK(IsLinked());
   }
 
   friend class arm::ArmAssembler;
   friend class arm::Arm32Assembler;
   friend class arm::Thumb2Assembler;
+  friend class arm64::Arm64Assembler;
   friend class mips::MipsAssembler;
   friend class x86::X86Assembler;
   friend class x86_64::X86_64Assembler;
@@ -139,10 +140,10 @@
   int position_;
 
   AssemblerFixup* previous() const { return previous_; }
-  void set_previous(AssemblerFixup* previous) { previous_ = previous; }
+  void set_previous(AssemblerFixup* previous_in) { previous_ = previous_in; }
 
   int position() const { return position_; }
-  void set_position(int position) { position_ = position; }
+  void set_position(int position_in) { position_ = position_in; }
 
   friend class AssemblerBuffer;
 };
@@ -236,7 +237,7 @@
     return cursor_ - contents_;
   }
 
-  byte* contents() const { return contents_; }
+  uint8_t* contents() const { return contents_; }
 
   // Copy the assembled instructions into the specified memory block
   // and apply all fixups.
@@ -316,9 +317,9 @@
   // for a single, fast space check per instruction.
   static const int kMinimumGap = 32;
 
-  byte* contents_;
-  byte* cursor_;
-  byte* limit_;
+  uint8_t* contents_;
+  uint8_t* cursor_;
+  uint8_t* limit_;
   AssemblerFixup* fixup_;
 #ifndef NDEBUG
   bool fixups_processed_;
@@ -327,8 +328,8 @@
   // Head of linked list of slow paths
   SlowPath* slow_path_;
 
-  byte* cursor() const { return cursor_; }
-  byte* limit() const { return limit_; }
+  uint8_t* cursor() const { return cursor_; }
+  uint8_t* limit() const { return limit_; }
   size_t Capacity() const {
     CHECK_GE(limit_, contents_);
     return (limit_ - contents_) + kMinimumGap;
@@ -340,7 +341,7 @@
 
   // Compute the limit based on the data area and the capacity. See
   // description of kMinimumGap for the reasoning behind the value.
-  static byte* ComputeLimit(byte* data, size_t capacity) {
+  static uint8_t* ComputeLimit(uint8_t* data, size_t capacity) {
     return data + capacity - kMinimumGap;
   }
 
@@ -365,7 +366,7 @@
   }
 
   // TODO: Implement with disassembler.
-  virtual void Comment(const char* format, ...) { }
+  virtual void Comment(const char* format, ...) { UNUSED(format); }
 
   // Emit code that will create an activation on the stack
   virtual void BuildFrame(size_t frame_size, ManagedRegister method_reg,
@@ -499,6 +500,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_test.h b/compiler/utils/assembler_test.h
index 3742913..91237ae 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -24,7 +24,6 @@
 #include <cstdio>
 #include <cstdlib>
 #include <fstream>
-#include <iostream>
 #include <iterator>
 #include <sys/stat.h>
 
@@ -118,9 +117,8 @@
     std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
     for (auto reg : registers) {
       for (int64_t imm : imms) {
-        Imm* new_imm = CreateImmediate(imm);
-        (assembler_.get()->*f)(*reg, *new_imm);
-        delete new_imm;
+        Imm new_imm = CreateImmediate(imm);
+        (assembler_.get()->*f)(*reg, new_imm);
         std::string base = fmt;
 
         size_t reg_index = base.find("{reg}");
@@ -154,9 +152,8 @@
     std::string str;
     std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
     for (int64_t imm : imms) {
-      Imm* new_imm = CreateImmediate(imm);
-      (assembler_.get()->*f)(*new_imm);
-      delete new_imm;
+      Imm new_imm = CreateImmediate(imm);
+      (assembler_.get()->*f)(new_imm);
       std::string base = fmt;
 
       size_t imm_index = base.find("{imm}");
@@ -333,7 +330,7 @@
   }
 
   // Create an immediate from the specific value.
-  virtual Imm* CreateImmediate(int64_t imm_value) = 0;
+  virtual Imm CreateImmediate(int64_t imm_value) = 0;
 
  private:
   // Driver() assembles and compares the results. If the results are not equal and we have a
@@ -373,7 +370,7 @@
         }
       } else {
         // This will output the assembly.
-        EXPECT_EQ(*data, *res.code) << "Outputs (and disassembly) not identical.";
+        EXPECT_EQ(*res.code, *data) << "Outputs (and disassembly) not identical.";
       }
     }
   }
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..fde65e7 100644
--- a/compiler/utils/growable_array.h
+++ b/compiler/utils/growable_array.h
@@ -19,76 +19,23 @@
 
 #include <stdint.h>
 #include <stddef.h>
-#include "arena_allocator.h"
+
+#include "arena_object.h"
 
 namespace art {
 
-// 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 {
+class GrowableArray : public ArenaObject<kArenaAllocGrowableArray> {
   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)
+    GrowableArray(ArenaAllocator* arena, size_t init_length)
       : arena_(arena),
         num_allocated_(init_length),
-        num_used_(0),
-        kind_(kind) {
+        num_used_(0) {
       elem_list_ = static_cast<T*>(arena_->Alloc(sizeof(T) * init_length,
                                                  kArenaAllocGrowableArray));
-    };
+    }
 
 
     // Expand the list size to at least new length.
@@ -105,7 +52,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 +83,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 +114,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_; }
 
@@ -199,16 +146,10 @@
 
     T* GetRawStorage() const { return elem_list_; }
 
-    static void* operator new(size_t size, ArenaAllocator* arena) {
-      return arena->Alloc(sizeof(GrowableArray<T>), kArenaAllocGrowableArray);
-    };
-    static void operator delete(void* p) {}  // Nop.
-
   private:
     ArenaAllocator* const arena_;
     size_t num_allocated_;
     size_t num_used_;
-    OatListKind kind_;
     T* elem_list_;
 };
 
diff --git a/compiler/utils/scoped_arena_allocator.cc b/compiler/utils/scoped_arena_allocator.cc
index aeb2f76..2616150 100644
--- a/compiler/utils/scoped_arena_allocator.cc
+++ b/compiler/utils/scoped_arena_allocator.cc
@@ -115,10 +115,18 @@
 }
 
 ScopedArenaAllocator::~ScopedArenaAllocator() {
-  Reset();
+  DoReset();
 }
 
 void ScopedArenaAllocator::Reset() {
+  DoReset();
+  // If this allocator was Create()d, we need to move the arena_stack_->top_ptr_ past *this.
+  if (mark_ptr_ == reinterpret_cast<uint8_t*>(this)) {
+    arena_stack_->top_ptr_ = mark_ptr_ + RoundUp(sizeof(ScopedArenaAllocator), 8);
+  }
+}
+
+void ScopedArenaAllocator::DoReset() {
   DebugStackReference::CheckTop();
   DebugStackRefCounter::CheckNoRefs();
   arena_stack_->UpdatePeakStatsAndRestore(*this);
diff --git a/compiler/utils/scoped_arena_allocator.h b/compiler/utils/scoped_arena_allocator.h
index 62ea330..523f158 100644
--- a/compiler/utils/scoped_arena_allocator.h
+++ b/compiler/utils/scoped_arena_allocator.h
@@ -132,6 +132,8 @@
   uint8_t* mark_ptr_;
   uint8_t* mark_end_;
 
+  void DoReset();
+
   template <typename T>
   friend class ScopedArenaAllocatorAdapter;
 
diff --git a/compiler/utils/scoped_arena_containers.h b/compiler/utils/scoped_arena_containers.h
index 0de7403..df93b27 100644
--- a/compiler/utils/scoped_arena_containers.h
+++ b/compiler/utils/scoped_arena_containers.h
@@ -140,12 +140,15 @@
   const_pointer address(const_reference x) const { return &x; }
 
   pointer allocate(size_type n, ScopedArenaAllocatorAdapter<void>::pointer hint = nullptr) {
+    UNUSED(hint);
     DCHECK_LE(n, max_size());
     DebugStackIndirectTopRef::CheckTop();
     return reinterpret_cast<T*>(arena_stack_->Alloc(n * sizeof(T),
                                                     ArenaAllocatorAdapterKind::Kind()));
   }
   void deallocate(pointer p, size_type n) {
+    UNUSED(p);
+    UNUSED(n);
     DebugStackIndirectTopRef::CheckTop();
   }
 
diff --git a/compiler/utils/stack_checks.h b/compiler/utils/stack_checks.h
index ce01077..e762f7d 100644
--- a/compiler/utils/stack_checks.h
+++ b/compiler/utils/stack_checks.h
@@ -35,6 +35,7 @@
 //
 // A frame is considered large when it's above kLargeFrameSize.
 static inline bool FrameNeedsStackCheck(size_t size, InstructionSet isa) {
+  UNUSED(isa);
   return size >= kLargeFrameSize;
 }
 
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index b6a5c20..4ddf979 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 {
@@ -242,6 +243,17 @@
 }
 
 
+void X86Assembler::movw(const Address& dst, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOperandSizeOverride();
+  EmitUint8(0xC7);
+  EmitOperand(0, dst);
+  CHECK(imm.is_uint16() || imm.is_int16());
+  EmitUint8(imm.value() & 0xFF);
+  EmitUint8(imm.value() >> 8);
+}
+
+
 void X86Assembler::leal(Register dst, const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x8D);
@@ -265,6 +277,14 @@
 }
 
 
+void X86Assembler::movaps(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x0F);
+  EmitUint8(0x28);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
 void X86Assembler::movss(XmmRegister dst, const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF3);
@@ -745,6 +765,7 @@
   EmitRegisterOperand(dst, src);
 }
 
+
 void X86Assembler::xchgl(Register reg, const Address& address) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x87);
@@ -752,6 +773,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);
@@ -1263,7 +1291,7 @@
   pushl(Immediate(High32Bits(constant)));
   pushl(Immediate(Low32Bits(constant)));
   movsd(dst, Address(ESP, 0));
-  addl(ESP, Immediate(2 * kWordSize));
+  addl(ESP, Immediate(2 * sizeof(intptr_t)));
 }
 
 
@@ -1275,7 +1303,7 @@
     uint32_t d;
   } float_negate_constant __attribute__((aligned(16))) =
       { 0x80000000, 0x00000000, 0x80000000, 0x00000000 };
-  xorps(f, Address::Absolute(reinterpret_cast<uword>(&float_negate_constant)));
+  xorps(f, Address::Absolute(reinterpret_cast<uintptr_t>(&float_negate_constant)));
 }
 
 
@@ -1285,7 +1313,7 @@
     uint64_t b;
   } double_negate_constant __attribute__((aligned(16))) =
       {0x8000000000000000LL, 0x8000000000000000LL};
-  xorpd(d, Address::Absolute(reinterpret_cast<uword>(&double_negate_constant)));
+  xorpd(d, Address::Absolute(reinterpret_cast<uintptr_t>(&double_negate_constant)));
 }
 
 
@@ -1295,7 +1323,7 @@
     uint64_t b;
   } double_abs_constant __attribute__((aligned(16))) =
       {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL};
-  andpd(reg, Address::Absolute(reinterpret_cast<uword>(&double_abs_constant)));
+  andpd(reg, Address::Absolute(reinterpret_cast<uintptr_t>(&double_abs_constant)));
 }
 
 
@@ -1407,20 +1435,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 +1511,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..de4e6de 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -29,14 +29,15 @@
 namespace art {
 namespace x86 {
 
-class Immediate {
+class Immediate : public ValueObject {
  public:
-  explicit Immediate(int32_t value) : value_(value) {}
+  explicit Immediate(int32_t value_in) : value_(value_in) {}
 
   int32_t value() const { return value_; }
 
   bool is_int8() const { return IsInt(8, value_); }
   bool is_uint8() const { return IsUint(8, value_); }
+  bool is_int16() const { return IsInt(16, value_); }
   bool is_uint16() const { return IsUint(16, value_); }
 
  private:
@@ -46,7 +47,7 @@
 };
 
 
-class Operand {
+class Operand : public ValueObject {
  public:
   uint8_t mod() const {
     return (encoding_at(0) >> 6) & 3;
@@ -89,16 +90,16 @@
   // Operand can be sub classed (e.g: Address).
   Operand() : length_(0) { }
 
-  void SetModRM(int mod, Register rm) {
-    CHECK_EQ(mod & ~3, 0);
-    encoding_[0] = (mod << 6) | rm;
+  void SetModRM(int mod_in, Register rm_in) {
+    CHECK_EQ(mod_in & ~3, 0);
+    encoding_[0] = (mod_in << 6) | rm_in;
     length_ = 1;
   }
 
-  void SetSIB(ScaleFactor scale, Register index, Register base) {
+  void SetSIB(ScaleFactor scale_in, Register index_in, Register base_in) {
     CHECK_EQ(length_, 1);
-    CHECK_EQ(scale & ~3, 0);
-    encoding_[1] = (scale << 6) | (index << 3) | base;
+    CHECK_EQ(scale_in & ~3, 0);
+    encoding_[1] = (scale_in << 6) | (index_in << 3) | base_in;
     length_ = 2;
   }
 
@@ -115,83 +116,80 @@
   }
 
  private:
-  byte length_;
-  byte encoding_[6];
+  uint8_t length_;
+  uint8_t encoding_[6];
 
   explicit Operand(Register reg) { SetModRM(3, reg); }
 
   // Get the operand encoding byte at the given index.
-  uint8_t encoding_at(int index) const {
-    CHECK_GE(index, 0);
-    CHECK_LT(index, length_);
-    return encoding_[index];
+  uint8_t encoding_at(int index_in) const {
+    CHECK_GE(index_in, 0);
+    CHECK_LT(index_in, length_);
+    return encoding_[index_in];
   }
 
   friend class X86Assembler;
-
-  DISALLOW_COPY_AND_ASSIGN(Operand);
 };
 
 
 class Address : public Operand {
  public:
-  Address(Register base, int32_t disp) {
-    Init(base, disp);
+  Address(Register base_in, int32_t disp) {
+    Init(base_in, disp);
   }
 
-  Address(Register base, Offset disp) {
-    Init(base, disp.Int32Value());
+  Address(Register base_in, Offset disp) {
+    Init(base_in, disp.Int32Value());
   }
 
-  Address(Register base, FrameOffset disp) {
-    CHECK_EQ(base, ESP);
+  Address(Register base_in, FrameOffset disp) {
+    CHECK_EQ(base_in, ESP);
     Init(ESP, disp.Int32Value());
   }
 
-  Address(Register base, MemberOffset disp) {
-    Init(base, disp.Int32Value());
+  Address(Register base_in, MemberOffset disp) {
+    Init(base_in, disp.Int32Value());
   }
 
-  void Init(Register base, int32_t disp) {
-    if (disp == 0 && base != EBP) {
-      SetModRM(0, base);
-      if (base == ESP) SetSIB(TIMES_1, ESP, base);
+  void Init(Register base_in, int32_t disp) {
+    if (disp == 0 && base_in != EBP) {
+      SetModRM(0, base_in);
+      if (base_in == ESP) SetSIB(TIMES_1, ESP, base_in);
     } else if (disp >= -128 && disp <= 127) {
-      SetModRM(1, base);
-      if (base == ESP) SetSIB(TIMES_1, ESP, base);
+      SetModRM(1, base_in);
+      if (base_in == ESP) SetSIB(TIMES_1, ESP, base_in);
       SetDisp8(disp);
     } else {
-      SetModRM(2, base);
-      if (base == ESP) SetSIB(TIMES_1, ESP, base);
+      SetModRM(2, base_in);
+      if (base_in == ESP) SetSIB(TIMES_1, ESP, base_in);
       SetDisp32(disp);
     }
   }
 
-
-  Address(Register index, ScaleFactor scale, int32_t disp) {
-    CHECK_NE(index, ESP);  // Illegal addressing mode.
+  Address(Register index_in, ScaleFactor scale_in, int32_t disp) {
+    CHECK_NE(index_in, ESP);  // Illegal addressing mode.
     SetModRM(0, ESP);
-    SetSIB(scale, index, EBP);
+    SetSIB(scale_in, index_in, EBP);
     SetDisp32(disp);
   }
 
-  Address(Register base, Register index, ScaleFactor scale, int32_t disp) {
-    CHECK_NE(index, ESP);  // Illegal addressing mode.
-    if (disp == 0 && base != EBP) {
+  Address(Register base_in, Register index_in, ScaleFactor scale_in, int32_t disp) {
+    CHECK_NE(index_in, ESP);  // Illegal addressing mode.
+    if (disp == 0 && base_in != EBP) {
       SetModRM(0, ESP);
-      SetSIB(scale, index, base);
+      SetSIB(scale_in, index_in, base_in);
     } else if (disp >= -128 && disp <= 127) {
       SetModRM(1, ESP);
-      SetSIB(scale, index, base);
+      SetSIB(scale_in, index_in, base_in);
       SetDisp8(disp);
     } else {
       SetModRM(2, ESP);
-      SetSIB(scale, index, base);
+      SetSIB(scale_in, index_in, base_in);
       SetDisp32(disp);
     }
   }
 
-  static Address Absolute(uword addr) {
+  static Address Absolute(uintptr_t addr) {
     Address result;
     result.SetModRM(0, EBP);
     result.SetDisp32(addr);
@@ -204,14 +202,12 @@
 
  private:
   Address() {}
-
-  DISALLOW_COPY_AND_ASSIGN(Address);
 };
 
 
 class X86Assembler FINAL : public Assembler {
  public:
-  explicit X86Assembler() {}
+  explicit X86Assembler() : cfi_cfa_offset_(0), cfi_pc_(0) {}
   virtual ~X86Assembler() {}
 
   /*
@@ -251,6 +247,7 @@
   void movsxw(Register dst, const Address& src);
   void movw(Register dst, const Address& src);
   void movw(const Address& dst, Register src);
+  void movw(const Address& dst, const Immediate& imm);
 
   void leal(Register dst, const Address& src);
 
@@ -258,6 +255,7 @@
 
   void setb(Condition condition, Register dst);
 
+  void movaps(XmmRegister dst, XmmRegister src);
   void movss(XmmRegister dst, const Address& src);
   void movss(const Address& dst, XmmRegister src);
   void movss(XmmRegister dst, XmmRegister src);
@@ -337,6 +335,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 +571,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 +595,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.cc b/compiler/utils/x86/managed_register_x86.cc
index 021fe88..69e6fce 100644
--- a/compiler/utils/x86/managed_register_x86.cc
+++ b/compiler/utils/x86/managed_register_x86.cc
@@ -51,7 +51,11 @@
 };
 
 std::ostream& operator<<(std::ostream& os, const RegisterPair& reg) {
-  os << X86ManagedRegister::FromRegisterPair(reg);
+  if (reg == kNoRegisterPair) {
+    os << "kNoRegisterPair";
+  } else {
+    os << X86ManagedRegister::FromRegisterPair(reg);
+  }
   return os;
 }
 
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..5b70658 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 {
@@ -233,6 +234,7 @@
 
 void X86_64Assembler::movb(const Address& dst, const Immediate& imm) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex32(dst);
   EmitUint8(0xC6);
   EmitOperand(Register::RAX, dst);
   CHECK(imm.is_int8());
@@ -290,6 +292,18 @@
 }
 
 
+void X86_64Assembler::movw(const Address& dst, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOperandSizeOverride();
+  EmitOptionalRex32(dst);
+  EmitUint8(0xC7);
+  EmitOperand(Register::RAX, dst);
+  CHECK(imm.is_uint16() || imm.is_int16());
+  EmitUint8(imm.value() & 0xFF);
+  EmitUint8(imm.value() >> 8);
+}
+
+
 void X86_64Assembler::leaq(CpuRegister dst, const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitRex64(dst, src);
@@ -298,6 +312,15 @@
 }
 
 
+void X86_64Assembler::movaps(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x28);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
 void X86_64Assembler::movss(XmmRegister dst, const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF3);
@@ -328,10 +351,26 @@
 }
 
 
+void X86_64Assembler::movsxd(CpuRegister dst, CpuRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitRex64(dst);
+  EmitUint8(0x63);
+  EmitRegisterOperand(dst.LowBits(), src.LowBits());
+}
+
+
+void X86_64Assembler::movsxd(CpuRegister dst, const Address& src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitRex64(dst);
+  EmitUint8(0x63);
+  EmitOperand(dst.LowBits(), src);
+}
+
+
 void X86_64Assembler::movd(XmmRegister dst, CpuRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
-  EmitOptionalRex32(dst, src);
+  EmitRex64(dst, src);
   EmitUint8(0x0F);
   EmitUint8(0x6E);
   EmitOperand(dst.LowBits(), Operand(src));
@@ -341,7 +380,7 @@
 void X86_64Assembler::movd(CpuRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
-  EmitOptionalRex32(src, dst);
+  EmitRex64(src, dst);
   EmitUint8(0x0F);
   EmitUint8(0x7E);
   EmitOperand(src.LowBits(), Operand(dst));
@@ -838,6 +877,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);
@@ -1191,7 +1238,7 @@
 
 void X86_64Assembler::imull(CpuRegister reg, const Immediate& imm) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-  EmitOptionalRex32(reg);
+  EmitOptionalRex32(reg, reg);
   EmitUint8(0x69);
   EmitOperand(reg.LowBits(), Operand(reg));
   EmitImmediate(imm);
@@ -1207,6 +1254,34 @@
 }
 
 
+void X86_64Assembler::imulq(CpuRegister dst, CpuRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitRex64(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xAF);
+  EmitRegisterOperand(dst.LowBits(), src.LowBits());
+}
+
+
+void X86_64Assembler::imulq(CpuRegister reg, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  CHECK(imm.is_int32());  // imulq only supports 32b immediate.
+  EmitRex64(reg);
+  EmitUint8(0x69);
+  EmitOperand(reg.LowBits(), Operand(reg));
+  EmitImmediate(imm);
+}
+
+
+void X86_64Assembler::imulq(CpuRegister reg, const Address& address) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitRex64(reg, address);
+  EmitUint8(0x0F);
+  EmitUint8(0xAF);
+  EmitOperand(reg.LowBits(), address);
+}
+
+
 void X86_64Assembler::imull(CpuRegister reg) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitOptionalRex32(reg);
@@ -1239,7 +1314,6 @@
 }
 
 
-
 void X86_64Assembler::shll(CpuRegister reg, const Immediate& imm) {
   EmitGenericShift(false, 4, reg, imm);
 }
@@ -1283,6 +1357,14 @@
 }
 
 
+void X86_64Assembler::negq(CpuRegister reg) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitRex64(reg);
+  EmitUint8(0xF7);
+  EmitOperand(3, Operand(reg));
+}
+
+
 void X86_64Assembler::notl(CpuRegister reg) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitOptionalRex32(reg);
@@ -1291,6 +1373,14 @@
 }
 
 
+void X86_64Assembler::notq(CpuRegister reg) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitRex64(reg);
+  EmitUint8(0xF7);
+  EmitOperand(2, Operand(reg));
+}
+
+
 void X86_64Assembler::enter(const Immediate& imm) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xC8);
@@ -1459,7 +1549,7 @@
   pushq(Immediate(High32Bits(constant)));
   pushq(Immediate(Low32Bits(constant)));
   movsd(dst, Address(CpuRegister(RSP), 0));
-  addq(CpuRegister(RSP), Immediate(2 * kWordSize));
+  addq(CpuRegister(RSP), Immediate(2 * sizeof(intptr_t)));
 }
 
 
@@ -1471,7 +1561,7 @@
     uint32_t d;
   } float_negate_constant __attribute__((aligned(16))) =
       { 0x80000000, 0x00000000, 0x80000000, 0x00000000 };
-  xorps(f, Address::Absolute(reinterpret_cast<uword>(&float_negate_constant)));
+  xorps(f, Address::Absolute(reinterpret_cast<uintptr_t>(&float_negate_constant)));
 }
 
 
@@ -1481,7 +1571,7 @@
     uint64_t b;
   } double_negate_constant __attribute__((aligned(16))) =
       {0x8000000000000000LL, 0x8000000000000000LL};
-  xorpd(d, Address::Absolute(reinterpret_cast<uword>(&double_negate_constant)));
+  xorpd(d, Address::Absolute(reinterpret_cast<uintptr_t>(&double_negate_constant)));
 }
 
 
@@ -1491,7 +1581,7 @@
     uint64_t b;
   } double_abs_constant __attribute__((aligned(16))) =
       {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL};
-  andpd(reg, Address::Absolute(reinterpret_cast<uword>(&double_abs_constant)));
+  andpd(reg, Address::Absolute(reinterpret_cast<uintptr_t>(&double_abs_constant)));
 }
 
 
@@ -1690,6 +1780,10 @@
   EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex());
 }
 
+void X86_64Assembler::EmitRex64(XmmRegister dst, CpuRegister src) {
+  EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex());
+}
+
 void X86_64Assembler::EmitRex64(CpuRegister dst, const Operand& operand) {
   uint8_t rex = 0x48 | operand.rex();  // REX.W000
   if (dst.NeedsRex()) {
@@ -1714,11 +1808,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 +1835,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 +1852,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 +1922,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) {
@@ -1944,7 +2076,10 @@
                            MemberOffset offs) {
   X86_64ManagedRegister dest = mdest.AsX86_64();
   CHECK(dest.IsCpuRegister() && dest.IsCpuRegister());
-  movq(dest.AsCpuRegister(), Address(base.AsX86_64().AsCpuRegister(), offs));
+  movl(dest.AsCpuRegister(), Address(base.AsX86_64().AsCpuRegister(), offs));
+  if (kPoisonHeapReferences) {
+    negl(dest.AsCpuRegister());
+  }
 }
 
 void X86_64Assembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base,
@@ -2229,4 +2364,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..42d774a 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -36,14 +36,15 @@
 //
 // Note: As we support cross-compilation, the value type must be int64_t. Please be aware of
 // conversion rules in expressions regarding negation, especially size_t on 32b.
-class Immediate {
+class Immediate : public ValueObject {
  public:
-  explicit Immediate(int64_t value) : value_(value) {}
+  explicit Immediate(int64_t value_in) : value_(value_in) {}
 
   int64_t value() const { return value_; }
 
   bool is_int8() const { return IsInt(8, value_); }
   bool is_uint8() const { return IsUint(8, value_); }
+  bool is_int16() const { return IsInt(16, value_); }
   bool is_uint16() const { return IsUint(16, value_); }
   bool is_int32() const {
     // This does not work on 32b machines: return IsInt(32, value_);
@@ -53,12 +54,10 @@
 
  private:
   const int64_t value_;
-
-  DISALLOW_COPY_AND_ASSIGN(Immediate);
 };
 
 
-class Operand {
+class Operand : public ValueObject {
  public:
   uint8_t mod() const {
     return (encoding_at(0) >> 6) & 3;
@@ -106,26 +105,26 @@
   // Operand can be sub classed (e.g: Address).
   Operand() : rex_(0), length_(0) { }
 
-  void SetModRM(uint8_t mod, CpuRegister rm) {
-    CHECK_EQ(mod & ~3, 0);
-    if (rm.NeedsRex()) {
+  void SetModRM(uint8_t mod_in, CpuRegister rm_in) {
+    CHECK_EQ(mod_in & ~3, 0);
+    if (rm_in.NeedsRex()) {
       rex_ |= 0x41;  // REX.000B
     }
-    encoding_[0] = (mod << 6) | rm.LowBits();
+    encoding_[0] = (mod_in << 6) | rm_in.LowBits();
     length_ = 1;
   }
 
-  void SetSIB(ScaleFactor scale, CpuRegister index, CpuRegister base) {
+  void SetSIB(ScaleFactor scale_in, CpuRegister index_in, CpuRegister base_in) {
     CHECK_EQ(length_, 1);
-    CHECK_EQ(scale & ~3, 0);
-    if (base.NeedsRex()) {
+    CHECK_EQ(scale_in & ~3, 0);
+    if (base_in.NeedsRex()) {
       rex_ |= 0x41;  // REX.000B
     }
-    if (index.NeedsRex()) {
+    if (index_in.NeedsRex()) {
       rex_ |= 0x42;  // REX.00X0
     }
-    encoding_[1] = (scale << 6) | (static_cast<uint8_t>(index.LowBits()) << 3) |
-        static_cast<uint8_t>(base.LowBits());
+    encoding_[1] = (scale_in << 6) | (static_cast<uint8_t>(index_in.LowBits()) << 3) |
+        static_cast<uint8_t>(base_in.LowBits());
     length_ = 2;
   }
 
@@ -149,84 +148,82 @@
   explicit Operand(CpuRegister reg) : rex_(0), length_(0) { SetModRM(3, reg); }
 
   // Get the operand encoding byte at the given index.
-  uint8_t encoding_at(int index) const {
-    CHECK_GE(index, 0);
-    CHECK_LT(index, length_);
-    return encoding_[index];
+  uint8_t encoding_at(int index_in) const {
+    CHECK_GE(index_in, 0);
+    CHECK_LT(index_in, length_);
+    return encoding_[index_in];
   }
 
   friend class X86_64Assembler;
-
-  DISALLOW_COPY_AND_ASSIGN(Operand);
 };
 
 
 class Address : public Operand {
  public:
-  Address(CpuRegister base, int32_t disp) {
-    Init(base, disp);
+  Address(CpuRegister base_in, int32_t disp) {
+    Init(base_in, disp);
   }
 
-  Address(CpuRegister base, Offset disp) {
-    Init(base, disp.Int32Value());
+  Address(CpuRegister base_in, Offset disp) {
+    Init(base_in, disp.Int32Value());
   }
 
-  Address(CpuRegister base, FrameOffset disp) {
-    CHECK_EQ(base.AsRegister(), RSP);
+  Address(CpuRegister base_in, FrameOffset disp) {
+    CHECK_EQ(base_in.AsRegister(), RSP);
     Init(CpuRegister(RSP), disp.Int32Value());
   }
 
-  Address(CpuRegister base, MemberOffset disp) {
-    Init(base, disp.Int32Value());
+  Address(CpuRegister base_in, MemberOffset disp) {
+    Init(base_in, disp.Int32Value());
   }
 
-  void Init(CpuRegister base, int32_t disp) {
-    if (disp == 0 && base.AsRegister() != RBP) {
-      SetModRM(0, base);
-      if (base.AsRegister() == RSP) {
-        SetSIB(TIMES_1, CpuRegister(RSP), base);
+  void Init(CpuRegister base_in, int32_t disp) {
+    if (disp == 0 && base_in.AsRegister() != RBP) {
+      SetModRM(0, base_in);
+      if (base_in.AsRegister() == RSP) {
+        SetSIB(TIMES_1, CpuRegister(RSP), base_in);
       }
     } else if (disp >= -128 && disp <= 127) {
-      SetModRM(1, base);
-      if (base.AsRegister() == RSP) {
-        SetSIB(TIMES_1, CpuRegister(RSP), base);
+      SetModRM(1, base_in);
+      if (base_in.AsRegister() == RSP) {
+        SetSIB(TIMES_1, CpuRegister(RSP), base_in);
       }
       SetDisp8(disp);
     } else {
-      SetModRM(2, base);
-      if (base.AsRegister() == RSP) {
-        SetSIB(TIMES_1, CpuRegister(RSP), base);
+      SetModRM(2, base_in);
+      if (base_in.AsRegister() == RSP) {
+        SetSIB(TIMES_1, CpuRegister(RSP), base_in);
       }
       SetDisp32(disp);
     }
   }
 
 
-  Address(CpuRegister index, ScaleFactor scale, int32_t disp) {
-    CHECK_NE(index.AsRegister(), RSP);  // Illegal addressing mode.
+  Address(CpuRegister index_in, ScaleFactor scale_in, int32_t disp) {
+    CHECK_NE(index_in.AsRegister(), RSP);  // Illegal addressing mode.
     SetModRM(0, CpuRegister(RSP));
-    SetSIB(scale, index, CpuRegister(RBP));
+    SetSIB(scale_in, index_in, CpuRegister(RBP));
     SetDisp32(disp);
   }
 
-  Address(CpuRegister base, CpuRegister index, ScaleFactor scale, int32_t disp) {
-    CHECK_NE(index.AsRegister(), RSP);  // Illegal addressing mode.
-    if (disp == 0 && base.AsRegister() != RBP) {
+  Address(CpuRegister base_in, CpuRegister index_in, ScaleFactor scale_in, int32_t disp) {
+    CHECK_NE(index_in.AsRegister(), RSP);  // Illegal addressing mode.
+    if (disp == 0 && base_in.AsRegister() != RBP) {
       SetModRM(0, CpuRegister(RSP));
-      SetSIB(scale, index, base);
+      SetSIB(scale_in, index_in, base_in);
     } else if (disp >= -128 && disp <= 127) {
       SetModRM(1, CpuRegister(RSP));
-      SetSIB(scale, index, base);
+      SetSIB(scale_in, index_in, base_in);
       SetDisp8(disp);
     } else {
       SetModRM(2, CpuRegister(RSP));
-      SetSIB(scale, index, base);
+      SetSIB(scale_in, index_in, base_in);
       SetDisp32(disp);
     }
   }
 
   // If no_rip is true then the Absolute address isn't RIP relative.
-  static Address Absolute(uword addr, bool no_rip = false) {
+  static Address Absolute(uintptr_t addr, bool no_rip = false) {
     Address result;
     if (no_rip) {
       result.SetModRM(0, CpuRegister(RSP));
@@ -246,14 +243,12 @@
 
  private:
   Address() {}
-
-  DISALLOW_COPY_AND_ASSIGN(Address);
 };
 
 
 class X86_64Assembler FINAL : public Assembler {
  public:
-  X86_64Assembler() {}
+  X86_64Assembler() : cfi_cfa_offset_(0), cfi_pc_(0) {}
   virtual ~X86_64Assembler() {}
 
   /*
@@ -295,13 +290,19 @@
   void movsxw(CpuRegister dst, const Address& src);
   void movw(CpuRegister dst, const Address& src);
   void movw(const Address& dst, CpuRegister src);
+  void movw(const Address& dst, const Immediate& imm);
 
   void leaq(CpuRegister dst, const Address& src);
 
+  void movaps(XmmRegister dst, XmmRegister src);
+
   void movss(XmmRegister dst, const Address& src);
   void movss(const Address& dst, XmmRegister src);
   void movss(XmmRegister dst, XmmRegister src);
 
+  void movsxd(CpuRegister dst, CpuRegister src);
+  void movsxd(CpuRegister dst, const Address& src);
+
   void movd(XmmRegister dst, CpuRegister src);
   void movd(CpuRegister dst, XmmRegister src);
 
@@ -378,6 +379,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);
@@ -430,6 +433,10 @@
   void imull(CpuRegister reg, const Immediate& imm);
   void imull(CpuRegister reg, const Address& address);
 
+  void imulq(CpuRegister dst, CpuRegister src);
+  void imulq(CpuRegister reg, const Immediate& imm);
+  void imulq(CpuRegister reg, const Address& address);
+
   void imull(CpuRegister reg);
   void imull(const Address& address);
 
@@ -446,7 +453,10 @@
   void shrq(CpuRegister reg, const Immediate& imm);
 
   void negl(CpuRegister reg);
+  void negq(CpuRegister reg);
+
   void notl(CpuRegister reg);
+  void notq(CpuRegister reg);
 
   void enter(const Immediate& imm);
   void leave();
@@ -614,6 +624,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);
@@ -650,11 +666,15 @@
   void EmitRex64(CpuRegister reg);
   void EmitRex64(CpuRegister dst, CpuRegister src);
   void EmitRex64(CpuRegister dst, const Operand& operand);
+  void EmitRex64(XmmRegister dst, CpuRegister src);
 
   // Emit a REX prefix to normalize byte registers plus necessary register bit encodings.
   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);
 };
 
@@ -667,13 +687,17 @@
 }
 
 inline void X86_64Assembler::EmitInt64(int64_t value) {
-  buffer_.Emit<int64_t>(value);
+  // Write this 64-bit value as two 32-bit words for alignment reasons
+  // (this is essentially when running on ARM, which does not allow
+  // 64-bit unaligned accesses).  We assume little-endianness here.
+  EmitInt32(Low32Bits(value));
+  EmitInt32(High32Bits(value));
 }
 
 inline void X86_64Assembler::EmitRegisterOperand(uint8_t rm, uint8_t reg) {
   CHECK_GE(rm, 0);
   CHECK_LT(rm, 8);
-  buffer_.Emit<uint8_t>(0xC0 + (rm << 3) + reg);
+  buffer_.Emit<uint8_t>((0xC0 | (reg & 7)) + (rm << 3));
 }
 
 inline void X86_64Assembler::EmitXmmRegisterOperand(uint8_t rm, XmmRegister reg) {
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 4ed7b20..0e8ea5b 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,12 +63,17 @@
     }
   }
 
+  void TearDown() OVERRIDE {
+    AssemblerTest::TearDown();
+    STLDeleteElements(&registers_);
+  }
+
   std::vector<x86_64::CpuRegister*> GetRegisters() OVERRIDE {
     return registers_;
   }
 
-  x86_64::Immediate* CreateImmediate(int64_t imm_value) OVERRIDE {
-    return new x86_64::Immediate(imm_value);
+  x86_64::Immediate CreateImmediate(int64_t imm_value) OVERRIDE {
+    return x86_64::Immediate(imm_value);
   }
 
  private:
@@ -106,6 +112,9 @@
   DriverStr(RepeatRI(&x86_64::X86_64Assembler::addq, 4U, "addq ${imm}, %{reg}"), "addqi");
 }
 
+TEST_F(AssemblerX86_64Test, ImulqRegs) {
+  DriverStr(RepeatRR(&x86_64::X86_64Assembler::imulq, "imulq %{reg2}, %{reg1}"), "imulq");
+}
 
 TEST_F(AssemblerX86_64Test, SubqRegs) {
   DriverStr(RepeatRR(&x86_64::X86_64Assembler::subq, "subq %{reg2}, %{reg1}"), "subq");
@@ -125,6 +134,32 @@
   DriverStr(RepeatRI(&x86_64::X86_64Assembler::xorq, 4U, "xorq ${imm}, %{reg}"), "xorqi");
 }
 
+TEST_F(AssemblerX86_64Test, Movaps) {
+  GetAssembler()->movaps(x86_64::XmmRegister(x86_64::XMM0), x86_64::XmmRegister(x86_64::XMM8));
+  DriverStr("movaps %xmm8, %xmm0", "movaps");
+}
+
+TEST_F(AssemblerX86_64Test, Movd) {
+  GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM0), x86_64::CpuRegister(x86_64::R11));
+  GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM0), x86_64::CpuRegister(x86_64::RAX));
+  GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM8), x86_64::CpuRegister(x86_64::R11));
+  GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM8), x86_64::CpuRegister(x86_64::RAX));
+  GetAssembler()->movd(x86_64::CpuRegister(x86_64::R11), x86_64::XmmRegister(x86_64::XMM0));
+  GetAssembler()->movd(x86_64::CpuRegister(x86_64::RAX), x86_64::XmmRegister(x86_64::XMM0));
+  GetAssembler()->movd(x86_64::CpuRegister(x86_64::R11), x86_64::XmmRegister(x86_64::XMM8));
+  GetAssembler()->movd(x86_64::CpuRegister(x86_64::RAX), x86_64::XmmRegister(x86_64::XMM8));
+  const char* expected =
+    "movd %r11, %xmm0\n"
+    "movd %rax, %xmm0\n"
+    "movd %r11, %xmm8\n"
+    "movd %rax, %xmm8\n"
+    "movd %xmm0, %r11\n"
+    "movd %xmm0, %rax\n"
+    "movd %xmm8, %r11\n"
+    "movd %xmm8, %rax\n";
+  DriverStr(expected, "movd");
+}
+
 TEST_F(AssemblerX86_64Test, Movl) {
   GetAssembler()->movl(x86_64::CpuRegister(x86_64::R8), x86_64::CpuRegister(x86_64::R11));
   GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::CpuRegister(x86_64::R11));
@@ -151,6 +186,15 @@
   DriverStr(expected, "movw");
 }
 
+TEST_F(AssemblerX86_64Test, IMulImmediate) {
+  GetAssembler()->imull(x86_64::CpuRegister(x86_64::RAX), x86_64::Immediate(0x40000));
+  GetAssembler()->imull(x86_64::CpuRegister(x86_64::R8), x86_64::Immediate(0x40000));
+  const char* expected =
+    "imull $0x40000,%eax,%eax\n"
+    "imull $0x40000,%r8d,%r8d\n";
+  DriverStr(expected, "imul");
+}
+
 
 std::string setcc_test_fn(x86_64::X86_64Assembler* assembler) {
   // From Condition
@@ -219,6 +263,7 @@
     }
   }
 
+  STLDeleteElements(&registers);
   return str.str();
 }
 
diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h
index ca9eae3..2a5b43d 100644
--- a/compiler/utils/x86_64/constants_x86_64.h
+++ b/compiler/utils/x86_64/constants_x86_64.h
@@ -48,6 +48,7 @@
 class XmmRegister {
  public:
   explicit XmmRegister(FloatRegister r) : reg_(r) {}
+  explicit XmmRegister(int r) : reg_(FloatRegister(r)) {}
   FloatRegister AsFloatRegister() const {
     return reg_;
   }
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/compiler/vector_output_stream.cc b/compiler/vector_output_stream.cc
index e5ff729..3d33673 100644
--- a/compiler/vector_output_stream.cc
+++ b/compiler/vector_output_stream.cc
@@ -20,8 +20,8 @@
 
 namespace art {
 
-VectorOutputStream::VectorOutputStream(const std::string& location, std::vector<uint8_t>& vector)
-  : OutputStream(location), offset_(vector.size()), vector_(vector) {}
+VectorOutputStream::VectorOutputStream(const std::string& location, std::vector<uint8_t>* vector)
+  : OutputStream(location), offset_(vector->size()), vector_(vector) {}
 
 off_t VectorOutputStream::Seek(off_t offset, Whence whence) {
   CHECK(whence == kSeekSet || whence == kSeekCurrent || whence == kSeekEnd) << whence;
@@ -36,7 +36,7 @@
       break;
     }
     case kSeekEnd: {
-      new_offset = vector_.size() + offset;
+      new_offset = vector_->size() + offset;
       break;
     }
   }
diff --git a/compiler/vector_output_stream.h b/compiler/vector_output_stream.h
index 09daa12..3c5877c 100644
--- a/compiler/vector_output_stream.h
+++ b/compiler/vector_output_stream.h
@@ -25,21 +25,21 @@
 
 namespace art {
 
-class VectorOutputStream : public OutputStream {
+class VectorOutputStream FINAL : public OutputStream {
  public:
-  VectorOutputStream(const std::string& location, std::vector<uint8_t>& vector);
+  VectorOutputStream(const std::string& location, std::vector<uint8_t>* vector);
 
   virtual ~VectorOutputStream() {}
 
   bool WriteFully(const void* buffer, size_t byte_count) {
-    if (static_cast<size_t>(offset_) == vector_.size()) {
+    if (static_cast<size_t>(offset_) == vector_->size()) {
       const uint8_t* start = reinterpret_cast<const uint8_t*>(buffer);
-      vector_.insert(vector_.end(), &start[0], &start[byte_count]);
+      vector_->insert(vector_->end(), &start[0], &start[byte_count]);
       offset_ += byte_count;
     } else {
       off_t new_offset = offset_ + byte_count;
       EnsureCapacity(new_offset);
-      memcpy(&vector_[offset_], buffer, byte_count);
+      memcpy(&(*vector_)[offset_], buffer, byte_count);
       offset_ = new_offset;
     }
     return true;
@@ -49,13 +49,13 @@
 
  private:
   void EnsureCapacity(off_t new_offset) {
-    if (new_offset > static_cast<off_t>(vector_.size())) {
-      vector_.resize(new_offset);
+    if (new_offset > static_cast<off_t>(vector_->size())) {
+      vector_->resize(new_offset);
     }
   }
 
   off_t offset_;
-  std::vector<uint8_t>& vector_;
+  std::vector<uint8_t>* const vector_;
 
   DISALLOW_COPY_AND_ASSIGN(VectorOutputStream);
 };
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index 0bab429..0ef20d6 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -24,10 +24,11 @@
 LOCAL_MODULE := dalvikvm
 LOCAL_MODULE_TAGS := optional
 LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := dalvikvm.cc ../sigchainlib/sigchain.cc
+LOCAL_SRC_FILES := dalvikvm.cc
 LOCAL_CFLAGS := $(dalvikvm_cflags)
 LOCAL_C_INCLUDES := art/runtime
 LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper
+LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
 LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk
@@ -51,10 +52,11 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_CLANG := true
 LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := dalvikvm.cc ../sigchainlib/sigchain.cc
+LOCAL_SRC_FILES := dalvikvm.cc
 LOCAL_CFLAGS := $(dalvikvm_cflags)
 LOCAL_C_INCLUDES := art/runtime
 LOCAL_SHARED_LIBRARIES := libnativehelper
+LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
 LOCAL_LDFLAGS := -ldl -lpthread
 # Mac OS linker doesn't understand --export-dynamic.
 ifneq ($(HOST_OS),darwin)
diff --git a/dalvikvm/dalvikvm.cc b/dalvikvm/dalvikvm.cc
index 67794c8..7839aa8 100644
--- a/dalvikvm/dalvikvm.cc
+++ b/dalvikvm/dalvikvm.cc
@@ -152,7 +152,7 @@
   }
 
   if (curr_opt > option_count) {
-    fprintf(stderr, "curr_opt(%d) >= option_count(%d)\n", curr_opt, option_count);
+    fprintf(stderr, "curr_opt(%d) > option_count(%d)\n", curr_opt, option_count);
     abort();
     return EXIT_FAILURE;
   }
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 28db711..4f39c42 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)
-  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler,art/compiler,host,ndebug))
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler libziparchive-host,art/compiler,host,ndebug))
 endif
-ifeq ($(ART_BUILD_DEBUG),true)
-  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler,art/compiler,host,debug))
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler libziparchive-host,art/compiler,host,debug))
 endif
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 833a678..d87faeb 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -30,6 +30,10 @@
 #include <sys/utsname.h>
 #endif
 
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include "cutils/trace.h"
+
+#include "base/dumpable.h"
 #include "base/stl_util.h"
 #include "base/stringpiece.h"
 #include "base/timing_logger.h"
@@ -44,9 +48,8 @@
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
-#include "elf_fixup.h"
-#include "elf_patcher.h"
-#include "elf_stripper.h"
+#include "elf_file.h"
+#include "elf_writer.h"
 #include "gc/space/image_space.h"
 #include "gc/space/space-inl.h"
 #include "image_writer.h"
@@ -92,7 +95,7 @@
   va_end(ap);
 }
 
-static void Usage(const char* fmt, ...) {
+[[noreturn]] static void Usage(const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   UsageErrorV(fmt, ap);
@@ -163,7 +166,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
@@ -229,366 +239,19 @@
   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);
 }
 
-class Dex2Oat {
- public:
-  static bool Create(Dex2Oat** p_dex2oat,
-                     const RuntimeOptions& runtime_options,
-                     const CompilerOptions& compiler_options,
-                     Compiler::Kind compiler_kind,
-                     InstructionSet instruction_set,
-                     InstructionSetFeatures instruction_set_features,
-                     VerificationResults* verification_results,
-                     DexFileToMethodInlinerMap* method_inliner_map,
-                     size_t thread_count)
-      SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
-    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));
-    if (!dex2oat->CreateRuntime(runtime_options, instruction_set)) {
-      *p_dex2oat = nullptr;
-      return false;
-    }
-    *p_dex2oat = dex2oat.release();
-    return true;
-  }
-
-  ~Dex2Oat() {
-    delete runtime_;
-    LogCompletionTime();
-  }
-
-  void LogCompletionTime() {
-    LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_)
-              << " (threads: " << thread_count_ << ")";
-  }
-
-
-  // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
-  std::set<std::string>* ReadImageClassesFromFile(const char* image_classes_filename) {
-    std::unique_ptr<std::ifstream> image_classes_file(new std::ifstream(image_classes_filename,
-                                                                  std::ifstream::in));
-    if (image_classes_file.get() == nullptr) {
-      LOG(ERROR) << "Failed to open image classes file " << image_classes_filename;
-      return nullptr;
-    }
-    std::unique_ptr<std::set<std::string>> result(ReadImageClasses(*image_classes_file));
-    image_classes_file->close();
-    return result.release();
-  }
-
-  std::set<std::string>* ReadImageClasses(std::istream& image_classes_stream) {
-    std::unique_ptr<std::set<std::string>> image_classes(new std::set<std::string>);
-    while (image_classes_stream.good()) {
-      std::string dot;
-      std::getline(image_classes_stream, dot);
-      if (StartsWith(dot, "#") || dot.empty()) {
-        continue;
-      }
-      std::string descriptor(DotToDescriptor(dot.c_str()));
-      image_classes->insert(descriptor);
-    }
-    return image_classes.release();
-  }
-
-  // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
-  std::set<std::string>* ReadImageClassesFromZip(const char* zip_filename,
-                                                         const char* image_classes_filename,
-                                                         std::string* error_msg) {
-    std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename, error_msg));
-    if (zip_archive.get() == nullptr) {
-      return nullptr;
-    }
-    std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename, error_msg));
-    if (zip_entry.get() == nullptr) {
-      *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename,
-                                zip_filename, error_msg->c_str());
-      return nullptr;
-    }
-    std::unique_ptr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(zip_filename,
-                                                                          image_classes_filename,
-                                                                          error_msg));
-    if (image_classes_file.get() == nullptr) {
-      *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", image_classes_filename,
-                                zip_filename, error_msg->c_str());
-      return nullptr;
-    }
-    const std::string image_classes_string(reinterpret_cast<char*>(image_classes_file->Begin()),
-                                           image_classes_file->Size());
-    std::istringstream image_classes_stream(image_classes_string);
-    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);
-
-    // Handle and ClassLoader creation needs to come after Runtime::Create
-    jobject class_loader = nullptr;
-    Thread* self = Thread::Current();
-    if (!boot_image_option.empty()) {
-      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      std::vector<const DexFile*> class_path_files(dex_files);
-      OpenClassPathFiles(runtime_->GetClassPathString(), class_path_files);
-      ScopedObjectAccess soa(self);
-      for (size_t i = 0; i < class_path_files.size(); i++) {
-        class_linker->RegisterDexFile(*class_path_files[i]);
-      }
-      soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader);
-      ScopedLocalRef<jobject> class_loader_local(soa.Env(),
-          soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
-      class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
-      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->GetCompiler()->SetBitcodeFileName(*driver.get(), bitcode_filename);
-
-    driver->CompileAll(class_loader, dex_files, &timings);
-
-    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);
-      gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
-      image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
-      image_file_location_oat_data_begin =
-          reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin());
-      image_file_location = image_space->GetImageFilename();
-      image_patch_delta = image_space->GetImageHeader().GetPatchDelta();
-    }
-
-    if (!image_file_location.empty()) {
-      key_value_store->Put(OatHeader::kImageLocationKey, image_file_location);
-    }
-
-    OatWriter oat_writer(dex_files, image_file_location_oat_checksum,
-                         image_file_location_oat_data_begin,
-                         image_patch_delta,
-                         driver.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;
-      }
-    }
-
-    return driver.release();
-  }
-
-  bool CreateImageFile(const std::string& image_filename,
-                       uintptr_t image_base,
-                       const std::string& oat_filename,
-                       const std::string& oat_location,
-                       const CompilerDriver& compiler)
-      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,
-                              compiler_options_->GetCompilePic())) {
-        LOG(ERROR) << "Failed to create image file " << image_filename;
-        return false;
-      }
-      oat_data_begin = image_writer.GetOatDataBegin();
-    }
-
-    std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
-    if (oat_file.get() == nullptr) {
-      PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
-      return false;
-    }
-    // Do not fix up the ELF file if we are --compile-pic
-    if (!compiler_options_->GetCompilePic()) {
-      if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
-        LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
-        return false;
-      }
-    }
-
-    return true;
-  }
-
- private:
-  explicit Dex2Oat(const CompilerOptions* compiler_options,
-                   Compiler::Kind compiler_kind,
-                   InstructionSet instruction_set,
-                   InstructionSetFeatures instruction_set_features,
-                   VerificationResults* verification_results,
-                   DexFileToMethodInlinerMap* method_inliner_map,
-                   size_t thread_count)
-      : compiler_options_(compiler_options),
-        compiler_kind_(compiler_kind),
-        instruction_set_(instruction_set),
-        instruction_set_features_(instruction_set_features),
-        verification_results_(verification_results),
-        method_inliner_map_(method_inliner_map),
-        runtime_(nullptr),
-        thread_count_(thread_count),
-        start_ns_(NanoTime()) {
-    CHECK(compiler_options != nullptr);
-    CHECK(verification_results != nullptr);
-    CHECK(method_inliner_map != nullptr);
-  }
-
-  bool CreateRuntime(const RuntimeOptions& runtime_options, InstructionSet instruction_set)
-      SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
-    if (!Runtime::Create(runtime_options, false)) {
-      LOG(ERROR) << "Failed to create runtime";
-      return false;
-    }
-    Runtime* runtime = Runtime::Current();
-    runtime->SetInstructionSet(instruction_set);
-    for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
-      Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
-      if (!runtime->HasCalleeSaveMethod(type)) {
-        runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(type), type);
-      }
-    }
-    runtime->GetClassLinker()->FixupDexCaches(runtime->GetResolutionMethod());
-    runtime->GetClassLinker()->RunRootClinits();
-    runtime_ = runtime;
-    return true;
-  }
-
-  // Appends to dex_files any elements of class_path that it doesn't already
-  // contain. This will open those dex files as necessary.
-  static void OpenClassPathFiles(const std::string& class_path,
-                                 std::vector<const DexFile*>& dex_files) {
-    std::vector<std::string> parsed;
-    Split(class_path, ':', parsed);
-    // Take Locks::mutator_lock_ so that lock ordering on the ClassLinker::dex_lock_ is maintained.
-    ScopedObjectAccess soa(Thread::Current());
-    for (size_t i = 0; i < parsed.size(); ++i) {
-      if (DexFilesContains(dex_files, parsed[i])) {
-        continue;
-      }
-      std::string error_msg;
-      if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, &dex_files)) {
-        LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg;
-      }
-    }
-  }
-
-  // Returns true if dex_files has a dex with the named location.
-  static bool DexFilesContains(const std::vector<const DexFile*>& dex_files,
-                               const std::string& location) {
-    for (size_t i = 0; i < dex_files.size(); ++i) {
-      if (dex_files[i]->GetLocation() == location) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  const CompilerOptions* const compiler_options_;
-  const Compiler::Kind compiler_kind_;
-
-  const InstructionSet instruction_set_;
-  const InstructionSetFeatures instruction_set_features_;
-
-  VerificationResults* const verification_results_;
-  DexFileToMethodInlinerMap* const method_inliner_map_;
-  Runtime* runtime_;
-  size_t thread_count_;
-  uint64_t start_ns_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
-};
-
-static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
-                           const std::vector<const char*>& dex_locations,
-                           std::vector<const DexFile*>& dex_files) {
-  size_t failure_count = 0;
-  for (size_t i = 0; i < dex_filenames.size(); i++) {
-    const char* dex_filename = dex_filenames[i];
-    const char* dex_location = dex_locations[i];
-    ATRACE_BEGIN(StringPrintf("Opening dex file '%s'", dex_filenames[i]).c_str());
-    std::string error_msg;
-    if (!OS::FileExists(dex_filename)) {
-      LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
-      continue;
-    }
-    if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) {
-      LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
-      ++failure_count;
-    }
-    ATRACE_END();
-  }
-  return failure_count;
-}
-
 // The primary goal of the watchdog is to prevent stuck build servers
 // during development when fatal aborts lead to a cascade of failures
 // that result in a deadlock.
@@ -660,7 +323,7 @@
     Message('W', message);
   }
 
-  static void Fatal(const std::string& message) {
+  [[noreturn]] static void Fatal(const std::string& message) {
     Message('F', message);
     exit(1);
   }
@@ -703,17 +366,13 @@
   // When setting timeouts, keep in mind that the build server may not be as fast as your desktop.
   // Debug builds are slower so they have larger timeouts.
   static const unsigned int kSlowdownFactor = kIsDebugBuild ? 5U : 1U;
-#if ART_USE_PORTABLE_COMPILER
-  // 2 minutes scaled by kSlowdownFactor.
-  static const unsigned int kWatchDogWarningSeconds = kSlowdownFactor * 2 * 60;
-  // 30 minutes scaled by kSlowdownFactor.
-  static const unsigned int kWatchDogTimeoutSeconds = kSlowdownFactor * 30 * 60;
-#else
-  // 1 minutes scaled by kSlowdownFactor.
-  static const unsigned int kWatchDogWarningSeconds = kSlowdownFactor * 1 * 60;
-  // 6 minutes scaled by kSlowdownFactor.
-  static const unsigned int kWatchDogTimeoutSeconds = kSlowdownFactor * 6 * 60;
-#endif
+
+  static const unsigned int kWatchDogWarningSeconds = kUsePortableCompiler ?
+      kSlowdownFactor * 2 * 60 :   // 2 minutes scaled by kSlowdownFactor (portable).
+      kSlowdownFactor * 1 * 60;    // 1 minute scaled by kSlowdownFactor  (not-portable).
+  static const unsigned int kWatchDogTimeoutSeconds = kUsePortableCompiler ?
+      kSlowdownFactor * 30 * 60 :  // 30 minutes scaled by kSlowdownFactor (portable).
+      kSlowdownFactor * 6 * 60;    // 6 minutes scaled by kSlowdownFactor  (not-portable).
 
   bool is_watch_dog_enabled_;
   bool shutting_down_;
@@ -723,42 +382,8 @@
   pthread_attr_t attr_;
   pthread_t pthread_;
 };
-const unsigned int WatchDog::kWatchDogWarningSeconds;
-const unsigned int WatchDog::kWatchDogTimeoutSeconds;
 
-// Given a set of instruction features from the build, parse it.  The
-// input 'str' is a comma separated list of feature names.  Parse it and
-// return the InstructionSetFeatures object.
-static InstructionSetFeatures ParseFeatureList(std::string str) {
-  InstructionSetFeatures result;
-  typedef std::vector<std::string> FeatureList;
-  FeatureList features;
-  Split(str, ',', features);
-  for (FeatureList::iterator i = features.begin(); i != features.end(); i++) {
-    std::string feature = Trim(*i);
-    if (feature == "default") {
-      // Nothing to do.
-    } else if (feature == "div") {
-      // Supports divide instruction.
-       result.SetHasDivideInstruction(true);
-    } else if (feature == "nodiv") {
-      // Turn off support for divide instruction.
-      result.SetHasDivideInstruction(false);
-    } else if (feature == "lpae") {
-      // Supports Large Physical Address Extension.
-      result.SetHasLpae(true);
-    } else if (feature == "nolpae") {
-      // Turn off support for Large Physical Address Extension.
-      result.SetHasLpae(false);
-    } else {
-      Usage("Unknown instruction set feature: '%s'", feature.c_str());
-    }
-  }
-  // others...
-  return result;
-}
-
-void ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) {
+static void ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) {
   std::string::size_type colon = s.find(c);
   if (colon == std::string::npos) {
     Usage("Missing char %c in option %s\n", c, s.c_str());
@@ -767,8 +392,8 @@
   *parsed_value = s.substr(colon + 1);
 }
 
-void ParseDouble(const std::string& option, char after_char,
-                 double min, double max, double* parsed_value) {
+static void ParseDouble(const std::string& option, char after_char, double min, double max,
+                        double* parsed_value) {
   std::string substring;
   ParseStringAfterChar(option, after_char, &substring);
   bool sane_val = true;
@@ -790,656 +415,689 @@
   *parsed_value = value;
 }
 
-static void b13564922() {
-#if defined(__linux__) && defined(__arm__)
-  int major, minor;
-  struct utsname uts;
-  if (uname(&uts) != -1 &&
-      sscanf(uts.release, "%d.%d", &major, &minor) == 2 &&
-      ((major < 3) || ((major == 3) && (minor < 4)))) {
-    // Kernels before 3.4 don't handle the ASLR well and we can run out of address
-    // space (http://b/13564922). Work around the issue by inhibiting further mmap() randomization.
-    int old_personality = personality(0xffffffff);
-    if ((old_personality & ADDR_NO_RANDOMIZE) == 0) {
-      int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
-      if (new_personality == -1) {
-        LOG(WARNING) << "personality(. | ADDR_NO_RANDOMIZE) failed.";
-      }
+class Dex2Oat FINAL {
+ public:
+  explicit Dex2Oat(TimingLogger* timings) :
+      compiler_kind_(kUsePortableCompiler ? Compiler::kPortable : Compiler::kQuick),
+      instruction_set_(kRuntimeISA),
+      // Take the default set of instruction features from the build.
+      method_inliner_map_(),
+      runtime_(nullptr),
+      thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
+      start_ns_(NanoTime()),
+      oat_fd_(-1),
+      zip_fd_(-1),
+      image_base_(0U),
+      image_classes_zip_filename_(nullptr),
+      image_classes_filename_(nullptr),
+      image_(false),
+      is_host_(false),
+      dump_stats_(false),
+      dump_passes_(false),
+      dump_timing_(false),
+      dump_slow_timing_(kIsDebugBuild),
+      timings_(timings) {}
+
+  ~Dex2Oat() {
+    if (kIsDebugBuild || (RUNNING_ON_VALGRIND != 0)) {
+      delete runtime_;  // See field declaration for why this is manual.
     }
-  }
-#endif
-}
-
-static int dex2oat(int argc, char** argv) {
-  b13564922();
-
-  original_argc = argc;
-  original_argv = argv;
-
-  TimingLogger timings("compiler", false, false);
-  CumulativeLogger compiler_phases_timings("compilation times");
-
-  InitLogging(argv);
-
-  // Skip over argv[0].
-  argv++;
-  argc--;
-
-  if (argc == 0) {
-    Usage("No arguments specified");
+    LogCompletionTime();
   }
 
-  std::vector<const char*> dex_filenames;
-  std::vector<const char*> dex_locations;
-  int zip_fd = -1;
-  std::string zip_location;
-  std::string oat_filename;
-  std::string oat_symbols;
-  std::string oat_location;
-  int oat_fd = -1;
-  std::string bitcode_filename;
-  const char* image_classes_zip_filename = nullptr;
-  const char* image_classes_filename = nullptr;
-  std::string image_filename;
-  std::string boot_image_filename;
-  uintptr_t image_base = 0;
-  std::string android_root;
-  std::vector<const char*> runtime_args;
-  int thread_count = sysconf(_SC_NPROCESSORS_CONF);
-  Compiler::Kind compiler_kind = kUsePortableCompiler
-      ? Compiler::kPortable
-      : Compiler::kQuick;
-  const char* compiler_filter_string = nullptr;
-  bool compile_pic = false;
-  int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
-  int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
-  int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
-  int tiny_method_threshold = CompilerOptions::kDefaultTinyMethodThreshold;
-  int num_dex_methods_threshold = CompilerOptions::kDefaultNumDexMethodsThreshold;
+  // Parse the arguments from the command line. In case of an unrecognized option or impossible
+  // values/combinations, a usage error will be displayed and exit() is called. Thus, if the method
+  // returns, arguments have been successfully parsed.
+  void ParseArgs(int argc, char** argv) {
+    original_argc = argc;
+    original_argv = argv;
 
-  // Take the default set of instruction features from the build.
-  InstructionSetFeatures instruction_set_features =
-      ParseFeatureList(Runtime::GetDefaultInstructionSetFeatures());
+    InitLogging(argv);
 
-  InstructionSet instruction_set = kRuntimeISA;
+    // Skip over argv[0].
+    argv++;
+    argc--;
 
-  // Profile file to use
-  std::string profile_file;
-  double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold;
-
-  bool is_host = false;
-  bool dump_stats = false;
-  bool dump_timing = false;
-  bool dump_passes = false;
-  bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
-  bool include_debug_symbols = kIsDebugBuild;
-  bool dump_slow_timing = kIsDebugBuild;
-  bool watch_dog_enabled = true;
-  bool generate_gdb_information = kIsDebugBuild;
-
-  // Checks are all explicit until we know the architecture.
-  bool implicit_null_checks = false;
-  bool implicit_so_checks = false;
-  bool implicit_suspend_checks = false;
-
-  for (int i = 0; i < argc; i++) {
-    const StringPiece option(argv[i]);
-    const bool log_options = false;
-    if (log_options) {
-      LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
+    if (argc == 0) {
+      Usage("No arguments specified");
     }
-    if (option.starts_with("--dex-file=")) {
-      dex_filenames.push_back(option.substr(strlen("--dex-file=")).data());
-    } else if (option.starts_with("--dex-location=")) {
-      dex_locations.push_back(option.substr(strlen("--dex-location=")).data());
-    } else if (option.starts_with("--zip-fd=")) {
-      const char* zip_fd_str = option.substr(strlen("--zip-fd=")).data();
-      if (!ParseInt(zip_fd_str, &zip_fd)) {
-        Usage("Failed to parse --zip-fd argument '%s' as an integer", zip_fd_str);
-      }
-      if (zip_fd < 0) {
-        Usage("--zip-fd passed a negative value %d", zip_fd);
-      }
-    } else if (option.starts_with("--zip-location=")) {
-      zip_location = option.substr(strlen("--zip-location=")).data();
-    } else if (option.starts_with("--oat-file=")) {
-      oat_filename = option.substr(strlen("--oat-file=")).data();
-    } else if (option.starts_with("--oat-symbols=")) {
-      oat_symbols = option.substr(strlen("--oat-symbols=")).data();
-    } else if (option.starts_with("--oat-fd=")) {
-      const char* oat_fd_str = option.substr(strlen("--oat-fd=")).data();
-      if (!ParseInt(oat_fd_str, &oat_fd)) {
-        Usage("Failed to parse --oat-fd argument '%s' as an integer", oat_fd_str);
-      }
-      if (oat_fd < 0) {
-        Usage("--oat-fd passed a negative value %d", oat_fd);
-      }
-    } else if (option == "--watch-dog") {
-      watch_dog_enabled = true;
-    } else if (option == "--no-watch-dog") {
-      watch_dog_enabled = false;
-    } else if (option == "--gen-gdb-info") {
-      generate_gdb_information = true;
-      // Debug symbols are needed for gdb information.
-      include_debug_symbols = true;
-    } else if (option == "--no-gen-gdb-info") {
-      generate_gdb_information = false;
-    } else if (option.starts_with("-j")) {
-      const char* thread_count_str = option.substr(strlen("-j")).data();
-      if (!ParseInt(thread_count_str, &thread_count)) {
-        Usage("Failed to parse -j argument '%s' as an integer", thread_count_str);
-      }
-    } else if (option.starts_with("--oat-location=")) {
-      oat_location = option.substr(strlen("--oat-location=")).data();
-    } else if (option.starts_with("--bitcode=")) {
-      bitcode_filename = option.substr(strlen("--bitcode=")).data();
-    } else if (option.starts_with("--image=")) {
-      image_filename = option.substr(strlen("--image=")).data();
-    } else if (option.starts_with("--image-classes=")) {
-      image_classes_filename = option.substr(strlen("--image-classes=")).data();
-    } else if (option.starts_with("--image-classes-zip=")) {
-      image_classes_zip_filename = option.substr(strlen("--image-classes-zip=")).data();
-    } else if (option.starts_with("--base=")) {
-      const char* image_base_str = option.substr(strlen("--base=")).data();
-      char* end;
-      image_base = strtoul(image_base_str, &end, 16);
-      if (end == image_base_str || *end != '\0') {
-        Usage("Failed to parse hexadecimal value for option %s", option.data());
-      }
-    } else if (option.starts_with("--boot-image=")) {
-      boot_image_filename = option.substr(strlen("--boot-image=")).data();
-    } else if (option.starts_with("--android-root=")) {
-      android_root = option.substr(strlen("--android-root=")).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.starts_with("--instruction-set-features=")) {
-      StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
-      instruction_set_features = ParseFeatureList(str.as_string());
-    } else if (option.starts_with("--compiler-backend=")) {
-      StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data();
-      if (backend_str == "Quick") {
-        compiler_kind = Compiler::kQuick;
-      } else if (backend_str == "Optimizing") {
-        compiler_kind = Compiler::kOptimizing;
-      } else if (backend_str == "Portable") {
-        compiler_kind = Compiler::kPortable;
-      }
-    } else if (option.starts_with("--compiler-filter=")) {
-      compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
-    } else if (option == "--compile-pic") {
-      compile_pic = true;
-    } else if (option.starts_with("--huge-method-max=")) {
-      const char* threshold = option.substr(strlen("--huge-method-max=")).data();
-      if (!ParseInt(threshold, &huge_method_threshold)) {
-        Usage("Failed to parse --huge-method-max '%s' as an integer", threshold);
-      }
-      if (huge_method_threshold < 0) {
-        Usage("--huge-method-max passed a negative value %s", huge_method_threshold);
-      }
-    } else if (option.starts_with("--large-method-max=")) {
-      const char* threshold = option.substr(strlen("--large-method-max=")).data();
-      if (!ParseInt(threshold, &large_method_threshold)) {
-        Usage("Failed to parse --large-method-max '%s' as an integer", threshold);
-      }
-      if (large_method_threshold < 0) {
-        Usage("--large-method-max passed a negative value %s", large_method_threshold);
-      }
-    } else if (option.starts_with("--small-method-max=")) {
-      const char* threshold = option.substr(strlen("--small-method-max=")).data();
-      if (!ParseInt(threshold, &small_method_threshold)) {
-        Usage("Failed to parse --small-method-max '%s' as an integer", threshold);
-      }
-      if (small_method_threshold < 0) {
-        Usage("--small-method-max passed a negative value %s", small_method_threshold);
-      }
-    } else if (option.starts_with("--tiny-method-max=")) {
-      const char* threshold = option.substr(strlen("--tiny-method-max=")).data();
-      if (!ParseInt(threshold, &tiny_method_threshold)) {
-        Usage("Failed to parse --tiny-method-max '%s' as an integer", threshold);
-      }
-      if (tiny_method_threshold < 0) {
-        Usage("--tiny-method-max passed a negative value %s", tiny_method_threshold);
-      }
-    } else if (option.starts_with("--num-dex-methods=")) {
-      const char* threshold = option.substr(strlen("--num-dex-methods=")).data();
-      if (!ParseInt(threshold, &num_dex_methods_threshold)) {
-        Usage("Failed to parse --num-dex-methods '%s' as an integer", threshold);
-      }
-      if (num_dex_methods_threshold < 0) {
-        Usage("--num-dex-methods passed a negative value %s", num_dex_methods_threshold);
-      }
-    } else if (option == "--host") {
-      is_host = true;
-    } else if (option == "--runtime-arg") {
-      if (++i >= argc) {
-        Usage("Missing required argument for --runtime-arg");
-      }
+
+    std::string oat_symbols;
+    std::string boot_image_filename;
+    const char* compiler_filter_string = nullptr;
+    bool compile_pic = false;
+    int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
+    int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
+    int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
+    int tiny_method_threshold = CompilerOptions::kDefaultTinyMethodThreshold;
+    int num_dex_methods_threshold = CompilerOptions::kDefaultNumDexMethodsThreshold;
+
+    // Profile file to use
+    double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold;
+
+    bool print_pass_options = false;
+    bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
+    bool include_debug_symbols = kIsDebugBuild;
+    bool watch_dog_enabled = true;
+    bool generate_gdb_information = kIsDebugBuild;
+
+    std::string error_msg;
+
+    for (int i = 0; i < argc; i++) {
+      const StringPiece option(argv[i]);
+      const bool log_options = false;
       if (log_options) {
         LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
       }
-      runtime_args.push_back(argv[i]);
-    } else if (option == "--dump-timing") {
-      dump_timing = true;
-    } else if (option == "--dump-passes") {
-      dump_passes = true;
-    } else if (option == "--dump-stats") {
-      dump_stats = true;
-    } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
-      include_debug_symbols = true;
-    } else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") {
-      include_debug_symbols = false;
-      generate_gdb_information = false;  // Depends on debug symbols, see above.
-    } else if (option.starts_with("--profile-file=")) {
-      profile_file = option.substr(strlen("--profile-file=")).data();
-      VLOG(compiler) << "dex2oat: profile file is " << profile_file;
-    } else if (option == "--no-profile-file") {
-      // No profile
-    } else if (option.starts_with("--top-k-profile-threshold=")) {
-      ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold);
-    } else if (option == "--print-pass-names") {
-      PassDriverMEOpts::PrintPassNames();
-    } else if (option.starts_with("--disable-passes=")) {
-      std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
-      PassDriverMEOpts::CreateDefaultPassList(disable_passes);
-    } else if (option.starts_with("--print-passes=")) {
-      std::string print_passes = option.substr(strlen("--print-passes=")).data();
-      PassDriverMEOpts::SetPrintPassList(print_passes);
-    } else if (option == "--print-all-passes") {
-      PassDriverMEOpts::SetPrintAllPasses();
-    } 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 == "--include-patch-information") {
-      include_patch_information = true;
-    } else if (option == "--no-include-patch-information") {
-      include_patch_information = false;
-    } else {
-      Usage("Unknown argument %s", option.data());
-    }
-  }
-
-  if (oat_filename.empty() && oat_fd == -1) {
-    Usage("Output must be supplied with either --oat-file or --oat-fd");
-  }
-
-  if (!oat_filename.empty() && oat_fd != -1) {
-    Usage("--oat-file should not be used with --oat-fd");
-  }
-
-  if (!oat_symbols.empty() && oat_fd != -1) {
-    Usage("--oat-symbols should not be used with --oat-fd");
-  }
-
-  if (!oat_symbols.empty() && is_host) {
-    Usage("--oat-symbols should not be used with --host");
-  }
-
-  if (oat_fd != -1 && !image_filename.empty()) {
-    Usage("--oat-fd should not be used with --image");
-  }
-
-  if (android_root.empty()) {
-    const char* android_root_env_var = getenv("ANDROID_ROOT");
-    if (android_root_env_var == nullptr) {
-      Usage("--android-root unspecified and ANDROID_ROOT not set");
-    }
-    android_root += android_root_env_var;
-  }
-
-  bool image = (!image_filename.empty());
-  if (!image && boot_image_filename.empty()) {
-    boot_image_filename += android_root;
-    boot_image_filename += "/framework/boot.art";
-  }
-  std::string boot_image_option;
-  if (!boot_image_filename.empty()) {
-    boot_image_option += "-Ximage:";
-    boot_image_option += boot_image_filename;
-  }
-
-  if (image_classes_filename != nullptr && !image) {
-    Usage("--image-classes should only be used with --image");
-  }
-
-  if (image_classes_filename != nullptr && !boot_image_option.empty()) {
-    Usage("--image-classes should not be used with --boot-image");
-  }
-
-  if (image_classes_zip_filename != nullptr && image_classes_filename == nullptr) {
-    Usage("--image-classes-zip should be used with --image-classes");
-  }
-
-  if (dex_filenames.empty() && zip_fd == -1) {
-    Usage("Input must be supplied with either --dex-file or --zip-fd");
-  }
-
-  if (!dex_filenames.empty() && zip_fd != -1) {
-    Usage("--dex-file should not be used with --zip-fd");
-  }
-
-  if (!dex_filenames.empty() && !zip_location.empty()) {
-    Usage("--dex-file should not be used with --zip-location");
-  }
-
-  if (dex_locations.empty()) {
-    for (size_t i = 0; i < dex_filenames.size(); i++) {
-      dex_locations.push_back(dex_filenames[i]);
-    }
-  } else if (dex_locations.size() != dex_filenames.size()) {
-    Usage("--dex-location arguments do not match --dex-file arguments");
-  }
-
-  if (zip_fd != -1 && zip_location.empty()) {
-    Usage("--zip-location should be supplied with --zip-fd");
-  }
-
-  if (boot_image_option.empty()) {
-    if (image_base == 0) {
-      Usage("Non-zero --base not specified");
-    }
-  }
-
-  std::string oat_stripped(oat_filename);
-  std::string oat_unstripped;
-  if (!oat_symbols.empty()) {
-    oat_unstripped += oat_symbols;
-  } else {
-    oat_unstripped += oat_filename;
-  }
-
-  if (compiler_filter_string == nullptr) {
-    if (instruction_set == kMips64) {
-      // TODO: fix compiler for Mips64.
-      compiler_filter_string = "interpret-only";
-    } else if (image) {
-      compiler_filter_string = "speed";
-    } else {
-#if ART_SMALL_MODE
-      compiler_filter_string = "interpret-only";
-#else
-      compiler_filter_string = "speed";
-#endif
-    }
-  }
-  CHECK(compiler_filter_string != nullptr);
-  CompilerOptions::CompilerFilter compiler_filter = CompilerOptions::kDefaultCompilerFilter;
-  if (strcmp(compiler_filter_string, "verify-none") == 0) {
-    compiler_filter = CompilerOptions::kVerifyNone;
-  } else if (strcmp(compiler_filter_string, "interpret-only") == 0) {
-    compiler_filter = CompilerOptions::kInterpretOnly;
-  } else if (strcmp(compiler_filter_string, "space") == 0) {
-    compiler_filter = CompilerOptions::kSpace;
-  } else if (strcmp(compiler_filter_string, "balanced") == 0) {
-    compiler_filter = CompilerOptions::kBalanced;
-  } else if (strcmp(compiler_filter_string, "speed") == 0) {
-    compiler_filter = CompilerOptions::kSpeed;
-  } else if (strcmp(compiler_filter_string, "everything") == 0) {
-    compiler_filter = CompilerOptions::kEverything;
-  } else {
-    Usage("Unknown --compiler-filter value %s", compiler_filter_string);
-  }
-
-  // Set the compilation target's implicit checks options.
-  switch (instruction_set) {
-    case kArm:
-    case kThumb2:
-    case kArm64:
-    case kX86:
-    case kX86_64:
-      implicit_null_checks = true;
-      implicit_so_checks = true;
-      break;
-
-    default:
-      // Defaults are correct.
-      break;
-  }
-
-  std::unique_ptr<CompilerOptions> compiler_options(new CompilerOptions(compiler_filter,
-                                                                        huge_method_threshold,
-                                                                        large_method_threshold,
-                                                                        small_method_threshold,
-                                                                        tiny_method_threshold,
-                                                                        num_dex_methods_threshold,
-                                                                        generate_gdb_information,
-                                                                        include_patch_information,
-                                                                        top_k_profile_threshold,
-                                                                        include_debug_symbols,
-                                                                        implicit_null_checks,
-                                                                        implicit_so_checks,
-                                                                        implicit_suspend_checks,
-                                                                        compile_pic
-#ifdef ART_SEA_IR_MODE
-                                                                        , compiler_options.sea_ir_ =
-                                                                              true;
-#endif
-  ));  // NOLINT(whitespace/parens)
-
-  // Done with usage checks, enable watchdog if requested
-  WatchDog watch_dog(watch_dog_enabled);
-
-  // Check early that the result of compilation can be written
-  std::unique_ptr<File> oat_file;
-  bool create_file = !oat_unstripped.empty();  // as opposed to using open file descriptor
-  if (create_file) {
-    oat_file.reset(OS::CreateEmptyFile(oat_unstripped.c_str()));
-    if (oat_location.empty()) {
-      oat_location = oat_filename;
-    }
-  } else {
-    oat_file.reset(new File(oat_fd, oat_location));
-    oat_file->DisableAutoClose();
-    oat_file->SetLength(0);
-  }
-  if (oat_file.get() == nullptr) {
-    PLOG(ERROR) << "Failed to create oat file: " << oat_location;
-    return EXIT_FAILURE;
-  }
-  if (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
-    PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location;
-    return EXIT_FAILURE;
-  }
-
-  timings.StartTiming("dex2oat Setup");
-  LOG(INFO) << CommandLine();
-
-  RuntimeOptions runtime_options;
-  std::vector<const DexFile*> boot_class_path;
-  art::MemMap::Init();  // For ZipEntry::ExtractToMemMap.
-  if (boot_image_option.empty()) {
-    size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
-    if (failure_count > 0) {
-      LOG(ERROR) << "Failed to open some dex files: " << failure_count;
-      return EXIT_FAILURE;
-    }
-    runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
-  } else {
-    runtime_options.push_back(std::make_pair(boot_image_option.c_str(), nullptr));
-  }
-  for (size_t i = 0; i < runtime_args.size(); i++) {
-    runtime_options.push_back(std::make_pair(runtime_args[i], nullptr));
-  }
-
-  std::unique_ptr<VerificationResults> verification_results(new VerificationResults(
-                                                            compiler_options.get()));
-  DexFileToMethodInlinerMap method_inliner_map;
-  QuickCompilerCallbacks callbacks(verification_results.get(), &method_inliner_map);
-  runtime_options.push_back(std::make_pair("compilercallbacks", &callbacks));
-  runtime_options.push_back(
-      std::make_pair("imageinstructionset",
-                     reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
-
-  Dex2Oat* p_dex2oat;
-  if (!Dex2Oat::Create(&p_dex2oat,
-                       runtime_options,
-                       *compiler_options,
-                       compiler_kind,
-                       instruction_set,
-                       instruction_set_features,
-                       verification_results.get(),
-                       &method_inliner_map,
-                       thread_count)) {
-    LOG(ERROR) << "Failed to create dex2oat";
-    timings.EndTiming();
-    return EXIT_FAILURE;
-  }
-  std::unique_ptr<Dex2Oat> dex2oat(p_dex2oat);
-
-  // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
-  // give it away now so that we don't starve GC.
-  Thread* self = Thread::Current();
-  self->TransitionFromRunnableToSuspended(kNative);
-  // If we're doing the image, override the compiler filter to force full compilation. Must be
-  // done ahead of WellKnownClasses::Init that causes verification.  Note: doesn't force
-  // compilation of class initializers.
-  // Whilst we're in native take the opportunity to initialize well known classes.
-  WellKnownClasses::Init(self->GetJniEnv());
-
-  // If --image-classes was specified, calculate the full list of classes to include in the image
-  std::unique_ptr<std::set<std::string>> image_classes(nullptr);
-  if (image_classes_filename != nullptr) {
-    std::string error_msg;
-    if (image_classes_zip_filename != nullptr) {
-      image_classes.reset(dex2oat->ReadImageClassesFromZip(image_classes_zip_filename,
-                                                           image_classes_filename,
-                                                           &error_msg));
-    } else {
-      image_classes.reset(dex2oat->ReadImageClassesFromFile(image_classes_filename));
-    }
-    if (image_classes.get() == nullptr) {
-      LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename <<
-          "': " << error_msg;
-      timings.EndTiming();
-      return EXIT_FAILURE;
-    }
-  } else if (image) {
-    image_classes.reset(new std::set<std::string>);
-  }
-
-  std::vector<const DexFile*> dex_files;
-  if (boot_image_option.empty()) {
-    dex_files = Runtime::Current()->GetClassLinker()->GetBootClassPath();
-  } else {
-    if (dex_filenames.empty()) {
-      ATRACE_BEGIN("Opening zip archive from file descriptor");
-      std::string error_msg;
-      std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd, zip_location.c_str(),
-                                                               &error_msg));
-      if (zip_archive.get() == nullptr) {
-        LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location << "': "
-            << error_msg;
-        timings.EndTiming();
-        return EXIT_FAILURE;
+      if (option.starts_with("--dex-file=")) {
+        dex_filenames_.push_back(option.substr(strlen("--dex-file=")).data());
+      } else if (option.starts_with("--dex-location=")) {
+        dex_locations_.push_back(option.substr(strlen("--dex-location=")).data());
+      } else if (option.starts_with("--zip-fd=")) {
+        const char* zip_fd_str = option.substr(strlen("--zip-fd=")).data();
+        if (!ParseInt(zip_fd_str, &zip_fd_)) {
+          Usage("Failed to parse --zip-fd argument '%s' as an integer", zip_fd_str);
+        }
+        if (zip_fd_ < 0) {
+          Usage("--zip-fd passed a negative value %d", zip_fd_);
+        }
+      } else if (option.starts_with("--zip-location=")) {
+        zip_location_ = option.substr(strlen("--zip-location=")).data();
+      } else if (option.starts_with("--oat-file=")) {
+        oat_filename_ = option.substr(strlen("--oat-file=")).data();
+      } else if (option.starts_with("--oat-symbols=")) {
+        oat_symbols = option.substr(strlen("--oat-symbols=")).data();
+      } else if (option.starts_with("--oat-fd=")) {
+        const char* oat_fd_str = option.substr(strlen("--oat-fd=")).data();
+        if (!ParseInt(oat_fd_str, &oat_fd_)) {
+          Usage("Failed to parse --oat-fd argument '%s' as an integer", oat_fd_str);
+        }
+        if (oat_fd_ < 0) {
+          Usage("--oat-fd passed a negative value %d", oat_fd_);
+        }
+      } else if (option == "--watch-dog") {
+        watch_dog_enabled = true;
+      } else if (option == "--no-watch-dog") {
+        watch_dog_enabled = false;
+      } else if (option == "--gen-gdb-info") {
+        generate_gdb_information = true;
+        // Debug symbols are needed for gdb information.
+        include_debug_symbols = true;
+      } else if (option == "--no-gen-gdb-info") {
+        generate_gdb_information = false;
+      } else if (option.starts_with("-j")) {
+        const char* thread_count_str = option.substr(strlen("-j")).data();
+        if (!ParseUint(thread_count_str, &thread_count_)) {
+          Usage("Failed to parse -j argument '%s' as an integer", thread_count_str);
+        }
+      } else if (option.starts_with("--oat-location=")) {
+        oat_location_ = option.substr(strlen("--oat-location=")).data();
+      } else if (option.starts_with("--bitcode=")) {
+        bitcode_filename_ = option.substr(strlen("--bitcode=")).data();
+      } else if (option.starts_with("--image=")) {
+        image_filename_ = option.substr(strlen("--image=")).data();
+      } else if (option.starts_with("--image-classes=")) {
+        image_classes_filename_ = option.substr(strlen("--image-classes=")).data();
+      } else if (option.starts_with("--image-classes-zip=")) {
+        image_classes_zip_filename_ = option.substr(strlen("--image-classes-zip=")).data();
+      } else if (option.starts_with("--base=")) {
+        const char* image_base_str = option.substr(strlen("--base=")).data();
+        char* end;
+        image_base_ = strtoul(image_base_str, &end, 16);
+        if (end == image_base_str || *end != '\0') {
+          Usage("Failed to parse hexadecimal value for option %s", option.data());
+        }
+      } else if (option.starts_with("--boot-image=")) {
+        boot_image_filename = option.substr(strlen("--boot-image=")).data();
+      } else if (option.starts_with("--android-root=")) {
+        android_root_ = option.substr(strlen("--android-root=")).data();
+      } else if (option.starts_with("--instruction-set=")) {
+        StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
+        // StringPiece is not necessarily zero-terminated, so need to make a copy and ensure it.
+        std::unique_ptr<char> buf(new char[instruction_set_str.length() + 1]);
+        strncpy(buf.get(), instruction_set_str.data(), instruction_set_str.length());
+        buf.get()[instruction_set_str.length()] = 0;
+        instruction_set_ = GetInstructionSetFromString(buf.get());
+        // arm actually means thumb2.
+        if (instruction_set_ == InstructionSet::kArm) {
+          instruction_set_ = InstructionSet::kThumb2;
+        }
+      } else if (option.starts_with("--instruction-set-variant=")) {
+        StringPiece str = option.substr(strlen("--instruction-set-variant=")).data();
+        instruction_set_features_.reset(
+            InstructionSetFeatures::FromVariant(instruction_set_, str.as_string(), &error_msg));
+        if (instruction_set_features_.get() == nullptr) {
+          Usage("%s", error_msg.c_str());
+        }
+      } else if (option.starts_with("--instruction-set-features=")) {
+        StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
+        instruction_set_features_.reset(
+            InstructionSetFeatures::FromFeatureString(instruction_set_, str.as_string(),
+                                                      &error_msg));
+        if (instruction_set_features_.get() == nullptr) {
+          Usage("%s", error_msg.c_str());
+        }
+      } else if (option.starts_with("--compiler-backend=")) {
+        StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data();
+        if (backend_str == "Quick") {
+          compiler_kind_ = Compiler::kQuick;
+        } else if (backend_str == "Optimizing") {
+          compiler_kind_ = Compiler::kOptimizing;
+        } else if (backend_str == "Portable") {
+          compiler_kind_ = Compiler::kPortable;
+        } else {
+          Usage("Unknown compiler backend: %s", backend_str.data());
+        }
+      } else if (option.starts_with("--compiler-filter=")) {
+        compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+      } else if (option == "--compile-pic") {
+        compile_pic = true;
+      } else if (option.starts_with("--huge-method-max=")) {
+        const char* threshold = option.substr(strlen("--huge-method-max=")).data();
+        if (!ParseInt(threshold, &huge_method_threshold)) {
+          Usage("Failed to parse --huge-method-max '%s' as an integer", threshold);
+        }
+        if (huge_method_threshold < 0) {
+          Usage("--huge-method-max passed a negative value %s", huge_method_threshold);
+        }
+      } else if (option.starts_with("--large-method-max=")) {
+        const char* threshold = option.substr(strlen("--large-method-max=")).data();
+        if (!ParseInt(threshold, &large_method_threshold)) {
+          Usage("Failed to parse --large-method-max '%s' as an integer", threshold);
+        }
+        if (large_method_threshold < 0) {
+          Usage("--large-method-max passed a negative value %s", large_method_threshold);
+        }
+      } else if (option.starts_with("--small-method-max=")) {
+        const char* threshold = option.substr(strlen("--small-method-max=")).data();
+        if (!ParseInt(threshold, &small_method_threshold)) {
+          Usage("Failed to parse --small-method-max '%s' as an integer", threshold);
+        }
+        if (small_method_threshold < 0) {
+          Usage("--small-method-max passed a negative value %s", small_method_threshold);
+        }
+      } else if (option.starts_with("--tiny-method-max=")) {
+        const char* threshold = option.substr(strlen("--tiny-method-max=")).data();
+        if (!ParseInt(threshold, &tiny_method_threshold)) {
+          Usage("Failed to parse --tiny-method-max '%s' as an integer", threshold);
+        }
+        if (tiny_method_threshold < 0) {
+          Usage("--tiny-method-max passed a negative value %s", tiny_method_threshold);
+        }
+      } else if (option.starts_with("--num-dex-methods=")) {
+        const char* threshold = option.substr(strlen("--num-dex-methods=")).data();
+        if (!ParseInt(threshold, &num_dex_methods_threshold)) {
+          Usage("Failed to parse --num-dex-methods '%s' as an integer", threshold);
+        }
+        if (num_dex_methods_threshold < 0) {
+          Usage("--num-dex-methods passed a negative value %s", num_dex_methods_threshold);
+        }
+      } else if (option == "--host") {
+        is_host_ = true;
+      } else if (option == "--runtime-arg") {
+        if (++i >= argc) {
+          Usage("Missing required argument for --runtime-arg");
+        }
+        if (log_options) {
+          LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
+        }
+        runtime_args_.push_back(argv[i]);
+      } else if (option == "--dump-timing") {
+        dump_timing_ = true;
+      } else if (option == "--dump-passes") {
+        dump_passes_ = true;
+      } else if (option == "--dump-stats") {
+        dump_stats_ = true;
+      } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
+        include_debug_symbols = true;
+      } else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") {
+        include_debug_symbols = false;
+        generate_gdb_information = false;  // Depends on debug symbols, see above.
+      } else if (option.starts_with("--profile-file=")) {
+        profile_file_ = option.substr(strlen("--profile-file=")).data();
+        VLOG(compiler) << "dex2oat: profile file is " << profile_file_;
+      } else if (option == "--no-profile-file") {
+        // No profile
+      } else if (option.starts_with("--top-k-profile-threshold=")) {
+        ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold);
+      } else if (option == "--print-pass-names") {
+        PassDriverMEOpts::PrintPassNames();
+      } else if (option.starts_with("--disable-passes=")) {
+        std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
+        PassDriverMEOpts::CreateDefaultPassList(disable_passes);
+      } else if (option.starts_with("--print-passes=")) {
+        std::string print_passes = option.substr(strlen("--print-passes=")).data();
+        PassDriverMEOpts::SetPrintPassList(print_passes);
+      } else if (option == "--print-all-passes") {
+        PassDriverMEOpts::SetPrintAllPasses();
+      } else if (option.starts_with("--dump-cfg-passes=")) {
+        std::string dump_passes_string = option.substr(strlen("--dump-cfg-passes=")).data();
+        PassDriverMEOpts::SetDumpPassList(dump_passes_string);
+      } 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") {
+        include_patch_information = false;
+      } else if (option.starts_with("--verbose-methods=")) {
+        // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages conditional
+        //       on having verbost methods.
+        gLogVerbosity.compiler = false;
+        Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
+      } else {
+        Usage("Unknown argument %s", option.data());
       }
-      if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location, &error_msg, &dex_files)) {
-        LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location
-            << "': " << error_msg;
-        timings.EndTiming();
-        return EXIT_FAILURE;
+    }
+
+    if (oat_filename_.empty() && oat_fd_ == -1) {
+      Usage("Output must be supplied with either --oat-file or --oat-fd");
+    }
+
+    if (!oat_filename_.empty() && oat_fd_ != -1) {
+      Usage("--oat-file should not be used with --oat-fd");
+    }
+
+    if (!oat_symbols.empty() && oat_fd_ != -1) {
+      Usage("--oat-symbols should not be used with --oat-fd");
+    }
+
+    if (!oat_symbols.empty() && is_host_) {
+      Usage("--oat-symbols should not be used with --host");
+    }
+
+    if (oat_fd_ != -1 && !image_filename_.empty()) {
+      Usage("--oat-fd should not be used with --image");
+    }
+
+    if (android_root_.empty()) {
+      const char* android_root_env_var = getenv("ANDROID_ROOT");
+      if (android_root_env_var == nullptr) {
+        Usage("--android-root unspecified and ANDROID_ROOT not set");
       }
-      ATRACE_END();
+      android_root_ += android_root_env_var;
+    }
+
+    image_ = (!image_filename_.empty());
+    if (!image_ && boot_image_filename.empty()) {
+      boot_image_filename += android_root_;
+      boot_image_filename += "/framework/boot.art";
+    }
+    if (!boot_image_filename.empty()) {
+      boot_image_option_ += "-Ximage:";
+      boot_image_option_ += boot_image_filename;
+    }
+
+    if (image_classes_filename_ != nullptr && !image_) {
+      Usage("--image-classes should only be used with --image");
+    }
+
+    if (image_classes_filename_ != nullptr && !boot_image_option_.empty()) {
+      Usage("--image-classes should not be used with --boot-image");
+    }
+
+    if (image_classes_zip_filename_ != nullptr && image_classes_filename_ == nullptr) {
+      Usage("--image-classes-zip should be used with --image-classes");
+    }
+
+    if (dex_filenames_.empty() && zip_fd_ == -1) {
+      Usage("Input must be supplied with either --dex-file or --zip-fd");
+    }
+
+    if (!dex_filenames_.empty() && zip_fd_ != -1) {
+      Usage("--dex-file should not be used with --zip-fd");
+    }
+
+    if (!dex_filenames_.empty() && !zip_location_.empty()) {
+      Usage("--dex-file should not be used with --zip-location");
+    }
+
+    if (dex_locations_.empty()) {
+      for (const char* dex_file_name : dex_filenames_) {
+        dex_locations_.push_back(dex_file_name);
+      }
+    } else if (dex_locations_.size() != dex_filenames_.size()) {
+      Usage("--dex-location arguments do not match --dex-file arguments");
+    }
+
+    if (zip_fd_ != -1 && zip_location_.empty()) {
+      Usage("--zip-location should be supplied with --zip-fd");
+    }
+
+    if (boot_image_option_.empty()) {
+      if (image_base_ == 0) {
+        Usage("Non-zero --base not specified");
+      }
+    }
+
+    oat_stripped_ = oat_filename_;
+    if (!oat_symbols.empty()) {
+      oat_unstripped_ = oat_symbols;
     } else {
-      size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, dex_files);
+      oat_unstripped_ = oat_filename_;
+    }
+
+    // If no instruction set feature was given, use the default one for the target
+    // instruction set.
+    if (instruction_set_features_.get() == nullptr) {
+      instruction_set_features_.reset(
+          InstructionSetFeatures::FromFeatureString(instruction_set_, "default", &error_msg));
+    }
+
+    if (instruction_set_ == kRuntimeISA) {
+      std::unique_ptr<const InstructionSetFeatures> runtime_features(
+          InstructionSetFeatures::FromCppDefines());
+      if (!instruction_set_features_->Equals(runtime_features.get())) {
+        LOG(WARNING) << "Mismatch between dex2oat instruction set features ("
+            << *instruction_set_features_ << ") and those of dex2oat executable ("
+            << *runtime_features <<") for the command line:\n"
+            << CommandLine();
+      }
+    }
+
+    if (compiler_filter_string == nullptr) {
+      if (instruction_set_ == kMips64) {
+        // TODO: fix compiler for Mips64.
+        compiler_filter_string = "interpret-only";
+      } else if (image_) {
+        compiler_filter_string = "speed";
+      } else {
+        // TODO: Migrate SMALL mode to command line option.
+  #if ART_SMALL_MODE
+        compiler_filter_string = "interpret-only";
+  #else
+        compiler_filter_string = "speed";
+  #endif
+      }
+    }
+    CHECK(compiler_filter_string != nullptr);
+    CompilerOptions::CompilerFilter compiler_filter = CompilerOptions::kDefaultCompilerFilter;
+    if (strcmp(compiler_filter_string, "verify-none") == 0) {
+      compiler_filter = CompilerOptions::kVerifyNone;
+    } else if (strcmp(compiler_filter_string, "interpret-only") == 0) {
+      compiler_filter = CompilerOptions::kInterpretOnly;
+    } else if (strcmp(compiler_filter_string, "space") == 0) {
+      compiler_filter = CompilerOptions::kSpace;
+    } else if (strcmp(compiler_filter_string, "balanced") == 0) {
+      compiler_filter = CompilerOptions::kBalanced;
+    } else if (strcmp(compiler_filter_string, "speed") == 0) {
+      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);
+    }
+
+    // Checks are all explicit until we know the architecture.
+    bool implicit_null_checks = false;
+    bool implicit_so_checks = false;
+    bool implicit_suspend_checks = false;
+    // Set the compilation target's implicit checks options.
+    switch (instruction_set_) {
+      case kArm:
+      case kThumb2:
+      case kArm64:
+      case kX86:
+      case kX86_64:
+        implicit_null_checks = true;
+        implicit_so_checks = true;
+        break;
+
+      default:
+        // Defaults are correct.
+        break;
+    }
+
+    if (print_pass_options) {
+      PassDriverMEOpts::PrintPassOptions();
+    }
+
+    compiler_options_.reset(new CompilerOptions(compiler_filter,
+                                                huge_method_threshold,
+                                                large_method_threshold,
+                                                small_method_threshold,
+                                                tiny_method_threshold,
+                                                num_dex_methods_threshold,
+                                                generate_gdb_information,
+                                                include_patch_information,
+                                                top_k_profile_threshold,
+                                                include_debug_symbols,
+                                                implicit_null_checks,
+                                                implicit_so_checks,
+                                                implicit_suspend_checks,
+                                                compile_pic,
+  #ifdef ART_SEA_IR_MODE
+                                                true,
+  #endif
+                                                verbose_methods_.empty() ?
+                                                    nullptr :
+                                                    &verbose_methods_));
+
+    // Done with usage checks, enable watchdog if requested
+    if (watch_dog_enabled) {
+      watchdog_.reset(new WatchDog(true));
+    }
+
+    // Fill some values into the key-value store for the oat header.
+    key_value_store_.reset(new SafeMap<std::string, std::string>());
+
+    // Insert some compiler things.
+    {
+      std::ostringstream oss;
+      for (int i = 0; i < argc; ++i) {
+        if (i > 0) {
+          oss << ' ';
+        }
+        oss << argv[i];
+      }
+      key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
+      oss.str("");  // Reset.
+      oss << kRuntimeISA;
+      key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str());
+      key_value_store_->Put(OatHeader::kPicKey, compile_pic ? "true" : "false");
+    }
+  }
+
+  // Check whether the oat output file is writable, and open it for later.
+  bool OpenFile() {
+    bool create_file = !oat_unstripped_.empty();  // as opposed to using open file descriptor
+    if (create_file) {
+      oat_file_.reset(OS::CreateEmptyFile(oat_unstripped_.c_str()));
+      if (oat_location_.empty()) {
+        oat_location_ = oat_filename_;
+      }
+    } else {
+      oat_file_.reset(new File(oat_fd_, oat_location_));
+      oat_file_->DisableAutoClose();
+      oat_file_->SetLength(0);
+    }
+    if (oat_file_.get() == nullptr) {
+      PLOG(ERROR) << "Failed to create oat file: " << oat_location_;
+      return false;
+    }
+    if (create_file && fchmod(oat_file_->Fd(), 0644) != 0) {
+      PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_;
+      return false;
+    }
+    return true;
+  }
+
+  // Set up the environment for compilation. Includes starting the runtime and loading/opening the
+  // boot class path.
+  bool Setup() {
+    TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
+    RuntimeOptions runtime_options;
+    std::vector<const DexFile*> boot_class_path;
+    art::MemMap::Init();  // For ZipEntry::ExtractToMemMap.
+    if (boot_image_option_.empty()) {
+      size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, boot_class_path);
       if (failure_count > 0) {
         LOG(ERROR) << "Failed to open some dex files: " << failure_count;
-        timings.EndTiming();
-        return EXIT_FAILURE;
+        return false;
       }
+      runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+    } else {
+      runtime_options.push_back(std::make_pair(boot_image_option_.c_str(), nullptr));
+    }
+    for (size_t i = 0; i < runtime_args_.size(); i++) {
+      runtime_options.push_back(std::make_pair(runtime_args_[i], nullptr));
     }
 
-    const bool kSaveDexInput = false;
-    if (kSaveDexInput) {
-      for (size_t i = 0; i < dex_files.size(); ++i) {
-        const DexFile* dex_file = dex_files[i];
-        std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
-        std::unique_ptr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
-        if (tmp_file.get() == nullptr) {
-            PLOG(ERROR) << "Failed to open file " << tmp_file_name
-                        << ". Try: adb shell chmod 777 /data/local/tmp";
-            continue;
+    verification_results_.reset(new VerificationResults(compiler_options_.get()));
+    callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), &method_inliner_map_));
+    runtime_options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+    runtime_options.push_back(
+        std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_)));
+
+    if (!CreateRuntime(runtime_options)) {
+      return false;
+    }
+
+    // Runtime::Create acquired the mutator_lock_ that is normally given away when we
+    // Runtime::Start, give it away now so that we don't starve GC.
+    Thread* self = Thread::Current();
+    self->TransitionFromRunnableToSuspended(kNative);
+    // If we're doing the image, override the compiler filter to force full compilation. Must be
+    // done ahead of WellKnownClasses::Init that causes verification.  Note: doesn't force
+    // compilation of class initializers.
+    // Whilst we're in native take the opportunity to initialize well known classes.
+    WellKnownClasses::Init(self->GetJniEnv());
+
+    // If --image-classes was specified, calculate the full list of classes to include in the image
+    if (image_classes_filename_ != nullptr) {
+      std::string error_msg;
+      if (image_classes_zip_filename_ != nullptr) {
+        image_classes_.reset(ReadImageClassesFromZip(image_classes_zip_filename_,
+                                                    image_classes_filename_,
+                                                    &error_msg));
+      } else {
+        image_classes_.reset(ReadImageClassesFromFile(image_classes_filename_));
+      }
+      if (image_classes_.get() == nullptr) {
+        LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename_ <<
+            "': " << error_msg;
+        return false;
+      }
+    } else if (image_) {
+      image_classes_.reset(new std::set<std::string>);
+    }
+
+    if (boot_image_option_.empty()) {
+      dex_files_ = Runtime::Current()->GetClassLinker()->GetBootClassPath();
+    } else {
+      if (dex_filenames_.empty()) {
+        ATRACE_BEGIN("Opening zip archive from file descriptor");
+        std::string error_msg;
+        std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd_,
+                                                                       zip_location_.c_str(),
+                                                                       &error_msg));
+        if (zip_archive.get() == nullptr) {
+          LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location_ << "': "
+              << error_msg;
+          return false;
         }
-        tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
-        LOG(INFO) << "Wrote input to " << tmp_file_name;
+        if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location_, &error_msg, &dex_files_)) {
+          LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location_
+              << "': " << error_msg;
+          return false;
+        }
+        ATRACE_END();
+      } else {
+        size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, dex_files_);
+        if (failure_count > 0) {
+          LOG(ERROR) << "Failed to open some dex files: " << failure_count;
+          return false;
+        }
+      }
+
+      constexpr bool kSaveDexInput = false;
+      if (kSaveDexInput) {
+        for (size_t i = 0; i < dex_files_.size(); ++i) {
+          const DexFile* dex_file = dex_files_[i];
+          std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
+          std::unique_ptr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
+          if (tmp_file.get() == nullptr) {
+            PLOG(ERROR) << "Failed to open file " << tmp_file_name
+                << ". Try: adb shell chmod 777 /data/local/tmp";
+            continue;
+          }
+          tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
+          LOG(INFO) << "Wrote input to " << tmp_file_name;
+        }
       }
     }
-  }
-  // Ensure opened dex files are writable for dex-to-dex transformations.
-  for (const auto& dex_file : dex_files) {
-    if (!dex_file->EnableWrite()) {
-      PLOG(ERROR) << "Failed to make .dex file writeable '" << dex_file->GetLocation() << "'\n";
-    }
-  }
-
-  /*
-   * 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()) {
-    size_t num_methods = 0;
-    for (size_t i = 0; i != dex_files.size(); ++i) {
-      const DexFile* dex_file = dex_files[i];
-      CHECK(dex_file != nullptr);
-      num_methods += dex_file->NumMethodIds();
-    }
-    if (num_methods <= compiler_options->GetNumDexMethodsThreshold()) {
-      compiler_options->SetCompilerFilter(CompilerOptions::kSpeed);
-      VLOG(compiler) << "Below method threshold, compiling anyways";
-    }
-  }
-
-  // Fill some values into the key-value store for the oat header.
-  std::unique_ptr<SafeMap<std::string, std::string> > key_value_store(
-      new SafeMap<std::string, std::string>());
-
-  // Insert some compiler things.
-  {
-    std::ostringstream oss;
-    for (int i = 0; i < argc; ++i) {
-      if (i > 0) {
-        oss << ' ';
+    // Ensure opened dex files are writable for dex-to-dex transformations.
+    for (const auto& dex_file : dex_files_) {
+      if (!dex_file->EnableWrite()) {
+        PLOG(ERROR) << "Failed to make .dex file writeable '" << dex_file->GetLocation() << "'\n";
       }
-      oss << argv[i];
     }
-    key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
-    oss.str("");  // Reset.
-    oss << kRuntimeISA;
-    key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
-    key_value_store->Put(OatHeader::kPicKey, compile_pic ? "true" : "false");
+
+    /*
+     * 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() && 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];
+        CHECK(dex_file != nullptr);
+        num_methods += dex_file->NumMethodIds();
+      }
+      if (num_methods <= compiler_options_->GetNumDexMethodsThreshold()) {
+        compiler_options_->SetCompilerFilter(CompilerOptions::kSpeed);
+        VLOG(compiler) << "Below method threshold, compiling anyways";
+      }
+    }
+
+    return true;
   }
 
-  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) {
-    LOG(ERROR) << "Failed to create oat file: " << oat_location;
-    timings.EndTiming();
-    return EXIT_FAILURE;
-  }
+  // Create and invoke the compiler driver. This will compile all the dex files.
+  void Compile() {
+    TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
+    compiler_phases_timings_.reset(new CumulativeLogger("compilation times"));
 
-  VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location;
+    // Handle and ClassLoader creation needs to come after Runtime::Create
+    jobject class_loader = nullptr;
+    Thread* self = Thread::Current();
+    if (!boot_image_option_.empty()) {
+      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+      std::vector<const DexFile*> class_path_files(dex_files_);
+      OpenClassPathFiles(runtime_->GetClassPathString(), class_path_files);
+      ScopedObjectAccess soa(self);
+      for (size_t i = 0; i < class_path_files.size(); i++) {
+        class_linker->RegisterDexFile(*class_path_files[i]);
+      }
+      soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader);
+      ScopedLocalRef<jobject> class_loader_local(soa.Env(),
+          soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
+      class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
+      Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
+    }
+
+    driver_.reset(new CompilerDriver(compiler_options_.get(),
+                                     verification_results_.get(),
+                                     &method_inliner_map_,
+                                     compiler_kind_,
+                                     instruction_set_,
+                                     instruction_set_features_.get(),
+                                     image_,
+                                     image_classes_.release(),
+                                     thread_count_,
+                                     dump_stats_,
+                                     dump_passes_,
+                                     compiler_phases_timings_.get(),
+                                     profile_file_));
+
+    driver_->GetCompiler()->SetBitcodeFileName(*driver_, bitcode_filename_);
+
+    driver_->CompileAll(class_loader, dex_files_, timings_);
+  }
 
   // Notes on the interleaving of creating the image and oat file to
   // ensure the references between the two are correct.
@@ -1470,110 +1128,485 @@
   //
   // 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.
   //
-  if (image) {
-    TimingLogger::ScopedTiming t("dex2oat ImageWriter", &timings);
-    bool image_creation_success = dex2oat->CreateImageFile(image_filename,
-                                                           image_base,
-                                                           oat_unstripped,
-                                                           oat_location,
-                                                           *compiler.get());
-    if (!image_creation_success) {
-      timings.EndTiming();
-      return EXIT_FAILURE;
+  // Steps 1.-3. are done by the CreateOatFile() above, steps 4.-5.
+  // are done by the CreateImageFile() below.
+
+
+  // Write out the generated code part. Calls the OatWriter and ElfBuilder. Also prepares the
+  // ImageWriter, if necessary.
+  bool CreateOatFile() {
+    CHECK(key_value_store_.get() != nullptr);
+
+    TimingLogger::ScopedTiming t("dex2oat Oat", timings_);
+
+    std::unique_ptr<OatWriter> oat_writer;
+    {
+      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 (image_) {
+        PrepareImageWriter(image_base_);
+      } else {
+        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 =
+            reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin());
+        image_file_location = image_space->GetImageFilename();
+        image_patch_delta = image_space->GetImageHeader().GetPatchDelta();
+      }
+
+      if (!image_file_location.empty()) {
+        key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location);
+      }
+
+      oat_writer.reset(new OatWriter(dex_files_, image_file_location_oat_checksum,
+                                     image_file_location_oat_data_begin,
+                                     image_patch_delta,
+                                     driver_.get(),
+                                     image_writer_.get(),
+                                     timings_,
+                                     key_value_store_.get()));
     }
-    VLOG(compiler) << "Image written successfully: " << image_filename;
+
+    if (image_) {
+      // 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.
+      TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
+      if (!image_writer_->PrepareImageAddressSpace()) {
+        LOG(ERROR) << "Failed to prepare image address space.";
+        return false;
+      }
+    }
+
+    {
+      TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_);
+      if (!driver_->WriteElf(android_root_, is_host_, dex_files_, oat_writer.get(),
+                             oat_file_.get())) {
+        LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath();
+        return false;
+      }
+    }
+
+    // Flush result to disk.
+    {
+      TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
+      if (oat_file_->Flush() != 0) {
+        LOG(ERROR) << "Failed to flush ELF file " << oat_file_->GetPath();
+        return false;
+      }
+    }
+
+    VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location_;
+    return true;
   }
 
-  if (is_host) {
-    timings.EndTiming();
-    if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) {
-      LOG(INFO) << Dumpable<TimingLogger>(timings);
+  // If we are compiling an image, invoke the image creation routine. Else just skip.
+  bool HandleImage() {
+    if (image_) {
+      TimingLogger::ScopedTiming t("dex2oat ImageWriter", timings_);
+      if (!CreateImageFile()) {
+        return false;
+      }
+      VLOG(compiler) << "Image written successfully: " << image_filename_;
     }
-    if (dump_passes) {
-      LOG(INFO) << Dumpable<CumulativeLogger>(*compiler.get()->GetTimingsLogger());
+    return true;
+  }
+
+  // Strip the oat file, if requested. This first creates a copy from unstripped to stripped, and
+  // then runs the ElfStripper. Currently only relevant for the portable compiler.
+  bool Strip() {
+    // If we don't want to strip in place, copy from unstripped location to stripped location.
+    // We need to strip after image creation because FixupElf needs to use .strtab.
+    if (oat_unstripped_ != oat_stripped_) {
+      TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_);
+      oat_file_.reset();
+      std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped_.c_str()));
+      std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped_.c_str()));
+      size_t buffer_size = 8192;
+      std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_size]);
+      while (true) {
+        int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size));
+        if (bytes_read <= 0) {
+          break;
+        }
+        bool write_ok = out->WriteFully(buffer.get(), bytes_read);
+        CHECK(write_ok);
+      }
+      oat_file_.reset(out.release());
+      VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped_;
     }
+
+    if (kUsePortableCompiler) {
+      // Portable includes debug symbols unconditionally. If we are not supposed to create them,
+      // strip them now. Quick generates debug symbols only when the flag(s) are set.
+      if (!compiler_options_->GetIncludeDebugSymbols()) {
+        TimingLogger::ScopedTiming t("dex2oat ElfStripper", timings_);
+        // Strip unneeded sections for target
+        off_t seek_actual = lseek(oat_file_->Fd(), 0, SEEK_SET);
+        CHECK_EQ(0, seek_actual);
+        std::string error_msg;
+        if (!ElfFile::Strip(oat_file_.get(), &error_msg)) {
+          LOG(ERROR) << "Failed to strip elf file: " << error_msg;
+          return false;
+        }
+
+        // We wrote the oat file successfully, and want to keep it.
+        VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location_;
+      } else {
+        VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location_;
+      }
+    }
+
+    return true;
+  }
+
+  void DumpTiming() {
+    if (dump_timing_ || (dump_slow_timing_ && timings_->GetTotalNs() > MsToNs(1000))) {
+      LOG(INFO) << Dumpable<TimingLogger>(*timings_);
+    }
+    if (dump_passes_) {
+      LOG(INFO) << Dumpable<CumulativeLogger>(*driver_->GetTimingsLogger());
+    }
+  }
+
+  CompilerOptions* GetCompilerOptions() const {
+    return compiler_options_.get();
+  }
+
+  bool IsHost() const {
+    return is_host_;
+  }
+
+ private:
+  static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
+                             const std::vector<const char*>& dex_locations,
+                             std::vector<const DexFile*>& dex_files) {
+    size_t failure_count = 0;
+    for (size_t i = 0; i < dex_filenames.size(); i++) {
+      const char* dex_filename = dex_filenames[i];
+      const char* dex_location = dex_locations[i];
+      ATRACE_BEGIN(StringPrintf("Opening dex file '%s'", dex_filenames[i]).c_str());
+      std::string error_msg;
+      if (!OS::FileExists(dex_filename)) {
+        LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
+        continue;
+      }
+      if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) {
+        LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
+        ++failure_count;
+      }
+      ATRACE_END();
+    }
+    return failure_count;
+  }
+
+  // Returns true if dex_files has a dex with the named location.
+  static bool DexFilesContains(const std::vector<const DexFile*>& dex_files,
+                               const std::string& location) {
+    for (size_t i = 0; i < dex_files.size(); ++i) {
+      if (dex_files[i]->GetLocation() == location) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // Appends to dex_files any elements of class_path that it doesn't already
+  // contain. This will open those dex files as necessary.
+  static void OpenClassPathFiles(const std::string& class_path,
+                                 std::vector<const DexFile*>& dex_files) {
+    std::vector<std::string> parsed;
+    Split(class_path, ':', &parsed);
+    // Take Locks::mutator_lock_ so that lock ordering on the ClassLinker::dex_lock_ is maintained.
+    ScopedObjectAccess soa(Thread::Current());
+    for (size_t i = 0; i < parsed.size(); ++i) {
+      if (DexFilesContains(dex_files, parsed[i])) {
+        continue;
+      }
+      std::string error_msg;
+      if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, &dex_files)) {
+        LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg;
+      }
+    }
+  }
+
+  // Create a runtime necessary for compilation.
+  bool CreateRuntime(const RuntimeOptions& runtime_options)
+      SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
+    if (!Runtime::Create(runtime_options, false)) {
+      LOG(ERROR) << "Failed to create runtime";
+      return false;
+    }
+    Runtime* runtime = Runtime::Current();
+    runtime->SetInstructionSet(instruction_set_);
+    for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
+      Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
+      if (!runtime->HasCalleeSaveMethod(type)) {
+        runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(), type);
+      }
+    }
+    runtime->GetClassLinker()->FixupDexCaches(runtime->GetResolutionMethod());
+    runtime->GetClassLinker()->RunRootClinits();
+    runtime_ = runtime;
+    return true;
+  }
+
+  void PrepareImageWriter(uintptr_t image_base) {
+    image_writer_.reset(new ImageWriter(*driver_, image_base, compiler_options_->GetCompilePic()));
+  }
+
+  // Let the ImageWriter write the image file. If we do not compile PIC, also fix up the oat file.
+  bool CreateImageFile()
+      LOCKS_EXCLUDED(Locks::mutator_lock_) {
+    CHECK(image_writer_ != nullptr);
+    if (!image_writer_->Write(image_filename_, oat_unstripped_, 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_unstripped_.c_str()));
+    if (oat_file.get() == nullptr) {
+      PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_;
+      return false;
+    }
+
+    // Do not fix up the ELF file if we are --compile-pic
+    if (!compiler_options_->GetCompilePic()) {
+      if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) {
+        LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
+  static std::set<std::string>* ReadImageClassesFromFile(const char* image_classes_filename) {
+    std::unique_ptr<std::ifstream> image_classes_file(new std::ifstream(image_classes_filename,
+                                                                        std::ifstream::in));
+    if (image_classes_file.get() == nullptr) {
+      LOG(ERROR) << "Failed to open image classes file " << image_classes_filename;
+      return nullptr;
+    }
+    std::unique_ptr<std::set<std::string>> result(ReadImageClasses(*image_classes_file));
+    image_classes_file->close();
+    return result.release();
+  }
+
+  static std::set<std::string>* ReadImageClasses(std::istream& image_classes_stream) {
+    std::unique_ptr<std::set<std::string>> image_classes(new std::set<std::string>);
+    while (image_classes_stream.good()) {
+      std::string dot;
+      std::getline(image_classes_stream, dot);
+      if (StartsWith(dot, "#") || dot.empty()) {
+        continue;
+      }
+      std::string descriptor(DotToDescriptor(dot.c_str()));
+      image_classes->insert(descriptor);
+    }
+    return image_classes.release();
+  }
+
+  // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
+  static std::set<std::string>* ReadImageClassesFromZip(const char* zip_filename,
+                                                        const char* image_classes_filename,
+                                                        std::string* error_msg) {
+    std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename, error_msg));
+    if (zip_archive.get() == nullptr) {
+      return nullptr;
+    }
+    std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename, error_msg));
+    if (zip_entry.get() == nullptr) {
+      *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename,
+                                zip_filename, error_msg->c_str());
+      return nullptr;
+    }
+    std::unique_ptr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(zip_filename,
+                                                                          image_classes_filename,
+                                                                          error_msg));
+    if (image_classes_file.get() == nullptr) {
+      *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", image_classes_filename,
+                                zip_filename, error_msg->c_str());
+      return nullptr;
+    }
+    const std::string image_classes_string(reinterpret_cast<char*>(image_classes_file->Begin()),
+                                           image_classes_file->Size());
+    std::istringstream image_classes_stream(image_classes_string);
+    return ReadImageClasses(image_classes_stream);
+  }
+
+  void LogCompletionTime() const {
+    LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_)
+              << " (threads: " << thread_count_ << ")";
+  }
+
+  std::unique_ptr<CompilerOptions> compiler_options_;
+  Compiler::Kind compiler_kind_;
+
+  InstructionSet instruction_set_;
+  std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
+
+  std::unique_ptr<SafeMap<std::string, std::string> > key_value_store_;
+
+  std::unique_ptr<VerificationResults> verification_results_;
+  DexFileToMethodInlinerMap method_inliner_map_;
+  std::unique_ptr<QuickCompilerCallbacks> callbacks_;
+
+  // Not a unique_ptr as we want to just exit on non-debug builds, not bringing the runtime down
+  // in an orderly fashion. The destructor takes care of deleting this.
+  Runtime* runtime_;
+
+  size_t thread_count_;
+  uint64_t start_ns_;
+  std::unique_ptr<WatchDog> watchdog_;
+  std::unique_ptr<File> oat_file_;
+  std::string oat_stripped_;
+  std::string oat_unstripped_;
+  std::string oat_location_;
+  std::string oat_filename_;
+  int oat_fd_;
+  std::string bitcode_filename_;
+  std::vector<const char*> dex_filenames_;
+  std::vector<const char*> dex_locations_;
+  int zip_fd_;
+  std::string zip_location_;
+  std::string boot_image_option_;
+  std::vector<const char*> runtime_args_;
+  std::string image_filename_;
+  uintptr_t image_base_;
+  const char* image_classes_zip_filename_;
+  const char* image_classes_filename_;
+  std::unique_ptr<std::set<std::string>> image_classes_;
+  bool image_;
+  std::unique_ptr<ImageWriter> image_writer_;
+  bool is_host_;
+  std::string android_root_;
+  std::vector<const DexFile*> dex_files_;
+  std::unique_ptr<CompilerDriver> driver_;
+  std::vector<std::string> verbose_methods_;
+  bool dump_stats_;
+  bool dump_passes_;
+  bool dump_timing_;
+  bool dump_slow_timing_;
+  std::string profile_file_;  // Profile file to use
+  TimingLogger* timings_;
+  std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
+};
+
+const unsigned int WatchDog::kWatchDogWarningSeconds;
+const unsigned int WatchDog::kWatchDogTimeoutSeconds;
+
+static void b13564922() {
+#if defined(__linux__) && defined(__arm__)
+  int major, minor;
+  struct utsname uts;
+  if (uname(&uts) != -1 &&
+      sscanf(uts.release, "%d.%d", &major, &minor) == 2 &&
+      ((major < 3) || ((major == 3) && (minor < 4)))) {
+    // Kernels before 3.4 don't handle the ASLR well and we can run out of address
+    // space (http://b/13564922). Work around the issue by inhibiting further mmap() randomization.
+    int old_personality = personality(0xffffffff);
+    if ((old_personality & ADDR_NO_RANDOMIZE) == 0) {
+      int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+      if (new_personality == -1) {
+        LOG(WARNING) << "personality(. | ADDR_NO_RANDOMIZE) failed.";
+      }
+    }
+  }
+#endif
+}
+
+static int dex2oat(int argc, char** argv) {
+  b13564922();
+
+  TimingLogger timings("compiler", false, false);
+
+  Dex2Oat dex2oat(&timings);
+
+  // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
+  dex2oat.ParseArgs(argc, argv);
+
+  // Check early that the result of compilation can be written
+  if (!dex2oat.OpenFile()) {
+    return EXIT_FAILURE;
+  }
+
+  LOG(INFO) << CommandLine();
+
+  if (!dex2oat.Setup()) {
+    return EXIT_FAILURE;
+  }
+
+  dex2oat.Compile();
+
+  if (!dex2oat.CreateOatFile()) {
+    return EXIT_FAILURE;
+  }
+
+  if (!dex2oat.HandleImage()) {
+    return EXIT_FAILURE;
+  }
+
+  if (dex2oat.IsHost()) {
+    dex2oat.DumpTiming();
     return EXIT_SUCCESS;
   }
 
-  // If we don't want to strip in place, copy from unstripped location to stripped location.
-  // We need to strip after image creation because FixupElf needs to use .strtab.
-  if (oat_unstripped != oat_stripped) {
-    TimingLogger::ScopedTiming t("dex2oat OatFile copy", &timings);
-    oat_file.reset();
-     std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped.c_str()));
-    std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped.c_str()));
-    size_t buffer_size = 8192;
-    std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_size]);
-    while (true) {
-      int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size));
-      if (bytes_read <= 0) {
-        break;
-      }
-      bool write_ok = out->WriteFully(buffer.get(), bytes_read);
-      CHECK(write_ok);
-    }
-    oat_file.reset(out.release());
-    VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped;
+  if (!dex2oat.Strip()) {
+    return EXIT_FAILURE;
   }
 
-#if ART_USE_PORTABLE_COMPILER  // We currently only generate symbols on Portable
-  if (!compiler_options.GetIncludeDebugSymbols()) {
-    timings.NewSplit("dex2oat ElfStripper");
-    // Strip unneeded sections for target
-    off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
-    CHECK_EQ(0, seek_actual);
-    std::string error_msg;
-    CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
-
-
-    // We wrote the oat file successfully, and want to keep it.
-    VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location;
-  } else {
-    VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location;
-  }
-#endif  // ART_USE_PORTABLE_COMPILER
-
-  timings.EndTiming();
-
-  if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) {
-    LOG(INFO) << Dumpable<TimingLogger>(timings);
-  }
-  if (dump_passes) {
-    LOG(INFO) << Dumpable<CumulativeLogger>(compiler_phases_timings);
-  }
-
-  // Everything was successfully written, do an explicit exit here to avoid running Runtime
-  // destructors that take time (bug 10645725) unless we're a debug build or running on valgrind.
-  if (!kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
-    dex2oat->LogCompletionTime();
-    exit(EXIT_SUCCESS);
-  }
-
+  dex2oat.DumpTiming();
   return EXIT_SUCCESS;
-}  // NOLINT(readability/fn_size)
+}
 }  // namespace art
 
 int main(int argc, char** argv) {
-  return art::dex2oat(argc, argv);
+  int result = art::dex2oat(argc, argv);
+  // Everything was done, do an explicit exit here to avoid running Runtime destructors that take
+  // time (bug 10645725) unless we're a debug build or running on valgrind. Note: The Dex2Oat class
+  // should not destruct the runtime in this case.
+  if (!art::kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
+    exit(result);
+  }
+  return result;
 }
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index a0abc9e..f2dd1ee 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -63,6 +63,7 @@
   	$(call set-target-local-cflags-vars,$(2))
   else # host
     LOCAL_CLANG := $(ART_HOST_CLANG)
+    LOCAL_LDLIBS := $(ART_HOST_LDLIBS)
     LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
     ifeq ($$(art_ndebug_or_debug),debug)
       LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
@@ -83,11 +84,11 @@
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
   include external/libcxx/libcxx.mk
+  # For disassembler_arm64.
+  LOCAL_SHARED_LIBRARIES += libvixl
   ifeq ($$(art_target_or_host),target)
-    LOCAL_SHARED_LIBRARIES += libcutils libvixl
     include $(BUILD_SHARED_LIBRARY)
   else # host
-    LOCAL_STATIC_LIBRARIES += libcutils libvixl
     include $(BUILD_HOST_SHARED_LIBRARY)
   endif
 endef
@@ -99,9 +100,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.cc b/disassembler/disassembler.cc
index c97bf64..bf68204 100644
--- a/disassembler/disassembler.cc
+++ b/disassembler/disassembler.cc
@@ -16,7 +16,7 @@
 
 #include "disassembler.h"
 
-#include <iostream>
+#include <ostream>
 
 #include "base/logging.h"
 #include "base/stringprintf.h"
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 54e7761..9243b1a 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -18,7 +18,8 @@
 
 #include <inttypes.h>
 
-#include <iostream>
+#include <ostream>
+#include <sstream>
 
 #include "base/logging.h"
 #include "base/stringprintf.h"
@@ -82,14 +83,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;
   }
 }
 
@@ -124,8 +125,10 @@
 };
 
 struct ArmRegister {
-  explicit ArmRegister(uint32_t r) : r(r) { CHECK_LE(r, 15U); }
-  ArmRegister(uint32_t instruction, uint32_t at_bit) : r((instruction >> at_bit) & 0xf) { CHECK_LE(r, 15U); }
+  explicit ArmRegister(uint32_t r_in) : r(r_in) { CHECK_LE(r_in, 15U); }
+  ArmRegister(uint32_t instruction, uint32_t at_bit) : r((instruction >> at_bit) & 0xf) {
+    CHECK_LE(r, 15U);
+  }
   uint32_t r;
 };
 std::ostream& operator<<(std::ostream& os, const ArmRegister& r) {
@@ -269,7 +272,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) {
@@ -389,7 +392,7 @@
   return (bit_a << 31) | ((1 << 30) - (bit_b << 25)) | (slice << 19);
 }
 
-uint64_t VFPExpand64(uint32_t imm8) {
+static uint64_t VFPExpand64(uint32_t imm8) {
   CHECK_EQ(imm8 & 0xffu, imm8);
   uint64_t bit_a = (imm8 >> 7) & 1;
   uint64_t bit_b = (imm8 >> 6) & 1;
@@ -397,45 +400,6 @@
   return (bit_a << 31) | ((UINT64_C(1) << 62) - (bit_b << 54)) | (slice << 48);
 }
 
-uint64_t AdvSIMDExpand(uint32_t op, uint32_t cmode, uint32_t imm8) {
-  CHECK_EQ(op & 1, op);
-  CHECK_EQ(cmode & 0xf, cmode);
-  CHECK_EQ(imm8 & 0xff, imm8);
-  int32_t cmode321 = cmode >> 1;
-  if (imm8 == 0 && cmode321 != 0 && cmode321 != 4 && cmode321 != 7) {
-    return INT64_C(0x00000000deadbeef);  // UNPREDICTABLE
-  }
-  uint64_t imm = imm8;
-  switch (cmode321) {
-    case 3: imm <<= 8;  // Fall through.
-    case 2: imm <<= 8;  // Fall through.
-    case 1: imm <<= 8;  // Fall through.
-    case 0: return static_cast<int64_t>((imm << 32) | imm);
-    case 5: imm <<= 8;  // Fall through.
-    case 4: return static_cast<int64_t>((imm << 48) | (imm << 32) | (imm << 16) | imm);
-    case 6:
-      imm = ((imm + 1u) << ((cmode & 1) != 0 ? 16 : 8)) - 1u;  // Add 8 or 16 ones.
-      return static_cast<int64_t>((imm << 32) | imm);
-    default:
-      CHECK_EQ(cmode321, 7);
-      if ((cmode & 1) == 0 && op == 0) {
-        imm = (imm << 8) | imm;
-        return static_cast<int64_t>((imm << 48) | (imm << 32) | (imm << 16) | imm);
-      } else if ((cmode & 1) == 0 && op != 0) {
-        for (int i = 1; i != 8; ++i) {
-          imm |= ((imm >> i) & UINT64_C(1)) << (i * 8);
-        }
-        imm = imm & ~UINT64_C(0xfe);
-        return static_cast<int64_t>((imm << 8) - imm);
-      } else if ((cmode & 1) != 0 && op == 0) {
-        imm = static_cast<uint32_t>(VFPExpand32(imm8));
-        return static_cast<int64_t>((imm << 32) | imm);
-      } else {
-        return INT64_C(0xdeadbeef00000000);  // UNDEFINED
-      }
-  }
-}
-
 size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
   uint32_t instr = (ReadU16(instr_ptr) << 16) | ReadU16(instr_ptr + 2);
   // |111|1 1|1000000|0000|1111110000000000|
@@ -1196,7 +1160,7 @@
               }
               break;
             }
-            // Else deliberate fall-through to B.
+            FALLTHROUGH_INTENDED;  // Else deliberate fall-through to B.
           case 1: case 3: {
             // B
             // |111|11|1|0000|000000|11|1 |1|1 |10000000000|
@@ -1358,8 +1322,6 @@
                   }
                 } else {
                   // STR Rt, [Rn, Rm, LSL #imm2] - 111 11 000 010 0 nnnn tttt 000000iimmmm
-                  ArmRegister Rn(instr, 16);
-                  ArmRegister Rt(instr, 12);
                   ArmRegister Rm(instr, 0);
                   uint32_t imm2 = (instr >> 4) & 3;
                   opcode << "str.w";
@@ -1597,6 +1559,7 @@
           }
         }
       }
+      break;
     default:
       break;
   }
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index 5d0c218..229ac97 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -18,7 +18,7 @@
 
 #include <inttypes.h>
 
-#include <iostream>
+#include <ostream>
 
 #include "base/logging.h"
 #include "base/stringprintf.h"
@@ -27,15 +27,11 @@
 namespace art {
 namespace arm64 {
 
-static uint32_t ReadU32(const uint8_t* ptr) {
-  return *((const uint32_t*)ptr);
-}
-
 size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) {
-  uint32_t instruction = ReadU32(begin);
-  decoder.Decode(reinterpret_cast<vixl::Instruction*>(&instruction));
+  const vixl::Instruction* instr = reinterpret_cast<const vixl::Instruction*>(begin);
+  decoder.Decode(instr);
   os << FormatInstructionPointer(begin)
-     << StringPrintf(": %08x\t%s\n", instruction, disasm.GetOutput());
+     << StringPrintf(": %08x\t%s\n", instr->InstructionBits(), disasm.GetOutput());
   return vixl::kInstructionSize;
 }
 
diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h
index ad20c70..e56fe4f 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -19,8 +19,11 @@
 
 #include "disassembler.h"
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
 #include "a64/decoder-a64.h"
 #include "a64/disasm-a64.h"
+#pragma GCC diagnostic pop
 
 namespace art {
 namespace arm64 {
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index bd5fac7..97c06f1 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -16,7 +16,8 @@
 
 #include "disassembler_mips.h"
 
-#include <iostream>
+#include <ostream>
+#include <sstream>
 
 #include "base/logging.h"
 #include "base/stringprintf.h"
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 4708498..b3af8a6 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -16,12 +16,14 @@
 
 #include "disassembler_x86.h"
 
-#include <iostream>
+#include <inttypes.h>
+
+#include <ostream>
+#include <sstream>
 
 #include "base/logging.h"
 #include "base/stringprintf.h"
 #include "thread.h"
-#include <inttypes.h>
 
 namespace art {
 namespace x86 {
@@ -58,10 +60,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) {
@@ -157,7 +159,6 @@
   const uint8_t* begin_instr = instr;
   bool have_prefixes = true;
   uint8_t prefix[4] = {0, 0, 0, 0};
-  const char** modrm_opcodes = NULL;
   do {
     switch (*instr) {
         // Group 1 - lock and repeat prefixes:
@@ -195,6 +196,7 @@
   if (rex != 0) {
     instr++;
   }
+  const char** modrm_opcodes = nullptr;
   bool has_modrm = false;
   bool reg_is_opcode = false;
   size_t immediate_bytes = 0;
@@ -203,7 +205,9 @@
   bool store = false;  // stores to memory (ie rm is on the left)
   bool load = false;  // loads from memory (ie rm is on the right)
   bool byte_operand = false;  // true when the opcode is dealing with byte operands
-  bool byte_second_operand = false;  // true when the source operand is a byte register but the target register isn't (ie movsxb/movzxb).
+  // true when the source operand is a byte register but the target register isn't
+  // (ie movsxb/movzxb).
+  bool byte_second_operand = false;
   bool target_specific = false;  // register name depends on target (64 vs 32 bits).
   bool ax = false;  // implicit use of ax
   bool cx = false;  // implicit use of cx
@@ -412,7 +416,7 @@
         break;
       case 0x2E:
         opcode << "u";
-        // FALLTHROUGH
+        FALLTHROUGH_INTENDED;
       case 0x2F:
         if (prefix[2] == 0x66) {
           opcode << "comisd";
@@ -506,7 +510,7 @@
           case 0x5D: opcode << "min"; break;
           case 0x5E: opcode << "div"; break;
           case 0x5F: opcode << "max"; break;
-          default: LOG(FATAL) << "Unreachable";
+          default: LOG(FATAL) << "Unreachable"; UNREACHABLE();
         }
         if (prefix[2] == 0x66) {
           opcode << "pd";
@@ -558,14 +562,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;
@@ -622,7 +631,9 @@
         } else {
           dst_reg_file = MMX;
         }
-        static const char* x71_opcodes[] = {"unknown-71", "unknown-71", "psrlw", "unknown-71", "psraw", "unknown-71", "psllw", "unknown-71"};
+        static const char* x71_opcodes[] = {
+            "unknown-71", "unknown-71", "psrlw", "unknown-71",
+            "psraw",      "unknown-71", "psllw", "unknown-71"};
         modrm_opcodes = x71_opcodes;
         reg_is_opcode = true;
         has_modrm = true;
@@ -636,7 +647,9 @@
         } else {
           dst_reg_file = MMX;
         }
-        static const char* x72_opcodes[] = {"unknown-72", "unknown-72", "psrld", "unknown-72", "psrad", "unknown-72", "pslld", "unknown-72"};
+        static const char* x72_opcodes[] = {
+            "unknown-72", "unknown-72", "psrld", "unknown-72",
+            "psrad",      "unknown-72", "pslld", "unknown-72"};
         modrm_opcodes = x72_opcodes;
         reg_is_opcode = true;
         has_modrm = true;
@@ -650,7 +663,9 @@
         } 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;
@@ -691,7 +706,7 @@
       case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
       case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F:
         opcode << "set" << condition_codes[*instr & 0xF];
-        modrm_opcodes = NULL;
+        modrm_opcodes = nullptr;
         reg_is_opcode = true;
         has_modrm = true;
         store = true;
@@ -702,16 +717,30 @@
         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
-          static const char* xAE_opcodes[] = {"rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase", "unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE"};
+          static const char* xAE_opcodes[] = {
+              "rdfsbase",   "rdgsbase",   "wrfsbase",   "wrgsbase",
+              "unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE"};
           modrm_opcodes = xAE_opcodes;
           reg_is_opcode = true;
           has_modrm = true;
@@ -738,7 +767,9 @@
               break;
           }
         } else {
-          static const char* xAE_opcodes[] = {"unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE", "lfence", "mfence", "sfence"};
+          static const char* xAE_opcodes[] = {
+              "unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE",
+              "unknown-AE", "lfence",     "mfence",     "sfence"};
           modrm_opcodes = xAE_opcodes;
           reg_is_opcode = true;
           has_modrm = true;
@@ -746,12 +777,44 @@
           no_ops = true;
         }
         break;
-      case 0xAF: opcode << "imul"; has_modrm = true; load = true; break;
-      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 0xBF: opcode << "movsxw"; has_modrm = true; load = true; break;
+      case 0xAF:
+        opcode << "imul";
+        has_modrm = true;
+        load = true;
+        break;
+      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 : 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";
@@ -777,7 +840,9 @@
         immediate_bytes = 1;
         break;
       case 0xC7:
-        static const char* x0FxC7_opcodes[] = { "unknown-0f-c7", "cmpxchg8b", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7" };
+        static const char* x0FxC7_opcodes[] = {
+            "unknown-0f-c7", "cmpxchg8b",     "unknown-0f-c7", "unknown-0f-c7",
+            "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7"};
         modrm_opcodes = x0FxC7_opcodes;
         has_modrm = true;
         reg_is_opcode = true;
@@ -787,6 +852,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 +911,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 +926,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;
@@ -983,7 +1018,9 @@
     break;
   case 0xC3: opcode << "ret"; break;
   case 0xC6:
-    static const char* c6_opcodes[] = {"mov", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6"};
+    static const char* c6_opcodes[] = {"mov",        "unknown-c6", "unknown-c6",
+                                       "unknown-c6", "unknown-c6", "unknown-c6",
+                                       "unknown-c6", "unknown-c6"};
     modrm_opcodes = c6_opcodes;
     store = true;
     immediate_bytes = 1;
@@ -992,7 +1029,9 @@
     byte_operand = true;
     break;
   case 0xC7:
-    static const char* c7_opcodes[] = {"mov", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7"};
+    static const char* c7_opcodes[] = {"mov",        "unknown-c7", "unknown-c7",
+                                       "unknown-c7", "unknown-c7", "unknown-c7",
+                                       "unknown-c7", "unknown-c7"};
     modrm_opcodes = c7_opcodes;
     store = true;
     immediate_bytes = 4;
@@ -1022,21 +1061,27 @@
     }
     break;
   case 0xDB:
-    static const char* db_opcodes[] = {"fildl", "unknown-db", "unknown-db", "unknown-db", "unknown-db", "unknown-db", "unknown-db", "unknown-db"};
+    static const char* db_opcodes[] = {"fildl",      "unknown-db", "unknown-db",
+                                       "unknown-db", "unknown-db", "unknown-db",
+                                       "unknown-db", "unknown-db"};
     modrm_opcodes = db_opcodes;
     load = true;
     has_modrm = true;
     reg_is_opcode = true;
     break;
   case 0xDD:
-    static const char* dd_opcodes[] = {"fldl", "fisttp", "fstl", "fstpl", "frstor", "unknown-dd", "fnsave", "fnstsw"};
+    static const char* dd_opcodes[] = {"fldl",   "fisttp", "fstl",
+                                       "fstpl",  "frstor", "unknown-dd",
+                                       "fnsave", "fnstsw"};
     modrm_opcodes = dd_opcodes;
     store = true;
     has_modrm = true;
     reg_is_opcode = true;
     break;
   case 0xDF:
-    static const char* df_opcodes[] = {"fild", "unknown-df", "unknown-df", "unknown-df", "unknown-df", "fildll", "unknown-df", "unknown-df"};
+    static const char* df_opcodes[] = {"fild",       "unknown-df", "unknown-df",
+                                       "unknown-df", "unknown-df", "fildll",
+                                       "unknown-df", "unknown-df"};
     modrm_opcodes = df_opcodes;
     load = true;
     has_modrm = true;
@@ -1048,7 +1093,10 @@
   case 0xEB: opcode << "jmp"; branch_bytes = 1; break;
   case 0xF5: opcode << "cmc"; break;
   case 0xF6: case 0xF7:
-    static const char* f7_opcodes[] = {"test", "unknown-f7", "not", "neg", "mul edx:eax, eax *", "imul edx:eax, eax *", "div edx:eax, edx:eax /", "idiv edx:eax, edx:eax /"};
+    static const char* f7_opcodes[] = {
+        "test", "unknown-f7", "not", "neg", "mul edx:eax, eax *",
+        "imul edx:eax, eax *", "div edx:eax, edx:eax /",
+        "idiv edx:eax, edx:eax /"};
     modrm_opcodes = f7_opcodes;
     has_modrm = true;
     reg_is_opcode = true;
@@ -1057,7 +1105,9 @@
     break;
   case 0xFF:
     {
-      static const char* ff_opcodes[] = {"inc", "dec", "call", "call", "jmp", "jmp", "push", "unknown-ff"};
+      static const char* ff_opcodes[] = {
+          "inc", "dec", "call", "call",
+          "jmp", "jmp", "push", "unknown-ff"};
       modrm_opcodes = ff_opcodes;
       has_modrm = true;
       reg_is_opcode = true;
@@ -1158,7 +1208,7 @@
       }
     }
 
-    if (reg_is_opcode && modrm_opcodes != NULL) {
+    if (reg_is_opcode && modrm_opcodes != nullptr) {
       opcode << modrm_opcodes[reg_or_opcode];
     }
 
@@ -1249,7 +1299,7 @@
     case 0xF2: prefixed_opcode << "repne "; break;
     case 0xF3: prefixed_opcode << "repe "; break;
     case 0: break;
-    default: LOG(FATAL) << "Unreachable";
+    default: LOG(FATAL) << "Unreachable"; UNREACHABLE();
   }
   prefixed_opcode << opcode.str();
   os << FormatInstructionPointer(begin_instr)
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index c35ff85..b8a3b49 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 libartd-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 libartd-compiler,art/disassembler art/compiler,host,debug))
 endif
 
 ########################################################################
@@ -53,14 +53,14 @@
 
 .PHONY: dump-oat-core-host
 ifeq ($(ART_BUILD_HOST),true)
-dump-oat-core-host: $(HOST_CORE_IMG_OUT) $(OATDUMP)
+dump-oat-core-host: $(HOST_CORE_IMG_OUTS) $(OATDUMP)
 	$(OATDUMP) --image=$(HOST_CORE_IMG_LOCATION) --output=$(ART_DUMP_OAT_PATH)/core.host.oatdump.txt
 	@echo Output in $(ART_DUMP_OAT_PATH)/core.host.oatdump.txt
 endif
 
 .PHONY: dump-oat-core-target
 ifeq ($(ART_BUILD_TARGET),true)
-dump-oat-core-target: $(TARGET_CORE_IMG_OUT) $(OATDUMP)
+dump-oat-core-target: $(TARGET_CORE_IMAGE_default_no-pic_32) $(OATDUMP)
 	$(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \
 	  --output=$(ART_DUMP_OAT_PATH)/core.target.oatdump.txt --instruction-set=$(TARGET_ARCH)
 	@echo Output in $(ART_DUMP_OAT_PATH)/core.target.oatdump.txt
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 8f93a66..cdf48c3 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_[] = {
@@ -117,24 +121,255 @@
   "kClassRoots",
 };
 
+class OatSymbolizer FINAL : public CodeOutput {
+ public:
+  explicit OatSymbolizer(const OatFile* oat_file, const std::string& output_name) :
+      oat_file_(oat_file), builder_(nullptr), elf_output_(nullptr),
+      output_name_(output_name.empty() ? "symbolized.oat" : 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;
+
+    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 uint8_t* 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 ATTRIBUTE_UNUSED,
+                        uint32_t class_method_index ATTRIBUTE_UNUSED,
+                        const OatFile::OatMethod& oat_method,
+                        const DexFile& dex_file ATTRIBUTE_UNUSED,
+                        uint32_t dex_method_idx ATTRIBUTE_UNUSED,
+                        const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED,
+                        uint32_t method_access_flags ATTRIBUTE_UNUSED) {
+    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 ATTRIBUTE_UNUSED,
+                 uint32_t class_method_index ATTRIBUTE_UNUSED,
+                 const OatFile::OatMethod& oat_method,
+                 const DexFile& dex_file,
+                 uint32_t dex_method_idx,
+                 const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED,
+                 uint32_t method_access_flags ATTRIBUTE_UNUSED) {
+    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_->GetSymtabBuilder();
+
+      symtab->AddSymbol(pretty_name, &builder_->GetTextBuilder(),
+          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 ATTRIBUTE_UNUSED) {
+    // 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_;
+  const 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 {
@@ -146,6 +381,7 @@
       disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet(),
                                          new DisassemblerOptions(options_->absolute_addresses_,
                                                                  oat_file.Begin()))) {
+    CHECK(options_->class_loader_ != nullptr);
     AddAllOffsets();
   }
 
@@ -167,8 +403,13 @@
     os << "INSTRUCTION SET:\n";
     os << oat_header.GetInstructionSet() << "\n\n";
 
-    os << "INSTRUCTION SET FEATURES:\n";
-    os << oat_header.GetInstructionSetFeatures().GetFeatureString() << "\n\n";
+    {
+      std::unique_ptr<const InstructionSetFeatures> features(
+          InstructionSetFeatures::FromBitmap(oat_header.GetInstructionSet(),
+                                             oat_header.GetInstructionSetFeaturesBitmap()));
+      os << "INSTRUCTION SET FEATURES:\n";
+      os << features->GetFeatureString() << "\n\n";
+    }
 
     os << "DEX FILE COUNT:\n";
     os << oat_header.GetDexFileCount() << "\n\n";
@@ -253,8 +494,8 @@
   }
 
   size_t ComputeSize(const void* oat_data) {
-    if (reinterpret_cast<const byte*>(oat_data) < oat_file_.Begin() ||
-        reinterpret_cast<const byte*>(oat_data) > oat_file_.End()) {
+    if (reinterpret_cast<const uint8_t*>(oat_data) < oat_file_.Begin() ||
+        reinterpret_cast<const uint8_t*>(oat_data) > oat_file_.End()) {
       return 0;  // Address not in oat file
     }
     uintptr_t begin_offset = reinterpret_cast<uintptr_t>(oat_data) -
@@ -315,7 +556,7 @@
            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);
-        const byte* class_data = dex_file->GetClassData(class_def);
+        const uint8_t* class_data = dex_file->GetClassData(class_def);
         if (class_data != nullptr) {
           ClassDataItemIterator it(*dex_file, class_data);
           SkipAllFields(it);
@@ -403,7 +644,7 @@
   bool DumpOatClass(std::ostream& os, const OatFile::OatClass& oat_class, const DexFile& dex_file,
                     const DexFile::ClassDef& class_def) {
     bool success = true;
-    const byte* class_data = dex_file.GetClassData(class_def);
+    const uint8_t* class_data = dex_file.GetClassData(class_def);
     if (class_data == nullptr) {  // empty class such as a marker interface?
       os << std::flush;
       return success;
@@ -576,6 +817,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()) {
@@ -666,6 +913,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);
@@ -692,6 +945,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();
@@ -900,13 +1192,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;
@@ -1002,26 +1297,26 @@
           std::ostream indent2_os(&indent2_filter);
           mirror::ObjectArray<mirror::Object>* image_root_object_array
               = image_root_object->AsObjectArray<mirror::Object>();
-          for (int i = 0; i < image_root_object_array->GetLength(); i++) {
-            mirror::Object* value = image_root_object_array->Get(i);
+          for (int j = 0; j < image_root_object_array->GetLength(); j++) {
+            mirror::Object* value = image_root_object_array->Get(j);
             size_t run = 0;
-            for (int32_t j = i + 1; j < image_root_object_array->GetLength(); j++) {
-              if (value == image_root_object_array->Get(j)) {
+            for (int32_t k = j + 1; k < image_root_object_array->GetLength(); k++) {
+              if (value == image_root_object_array->Get(k)) {
                 run++;
               } else {
                 break;
               }
             }
             if (run == 0) {
-              indent2_os << StringPrintf("%d: ", i);
+              indent2_os << StringPrintf("%d: ", j);
             } else {
-              indent2_os << StringPrintf("%d to %zd: ", i, i + run);
-              i = i + run;
+              indent2_os << StringPrintf("%d to %zd: ", j, j + run);
+              j = j + run;
             }
             if (value != nullptr) {
               PrettyObjectValue(indent2_os, value->GetClass(), value);
             } else {
-              indent2_os << i << ": null\n";
+              indent2_os << j << ": null\n";
             }
           }
         }
@@ -1150,15 +1445,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
@@ -1202,7 +1508,7 @@
   const void* GetQuickOatCodeBegin(mirror::ArtMethod* m)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     const void* quick_code = m->GetEntryPointFromQuickCompiledCode();
-    if (quick_code == Runtime::Current()->GetClassLinker()->GetQuickResolutionTrampoline()) {
+    if (Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(quick_code)) {
       quick_code = oat_dumper_->GetQuickOatCode(m);
     }
     if (oat_dumper_->GetInstructionSet() == kThumb2) {
@@ -1442,20 +1748,20 @@
           dex_instruction_bytes(0) {}
 
     struct SizeAndCount {
-      SizeAndCount(size_t bytes, size_t count) : bytes(bytes), count(count) {}
+      SizeAndCount(size_t bytes_in, size_t count_in) : bytes(bytes_in), count(count_in) {}
       size_t bytes;
       size_t count;
     };
     typedef SafeMap<std::string, SizeAndCount> SizeAndCountTable;
     SizeAndCountTable sizes_and_counts;
 
-    void Update(const char* descriptor, size_t object_bytes) {
+    void Update(const char* descriptor, size_t object_bytes_in) {
       SizeAndCountTable::iterator it = sizes_and_counts.find(descriptor);
       if (it != sizes_and_counts.end()) {
-        it->second.bytes += object_bytes;
+        it->second.bytes += object_bytes_in;
         it->second.count += 1;
       } else {
-        sizes_and_counts.Put(descriptor, SizeAndCount(object_bytes, 1));
+        sizes_and_counts.Put(descriptor, SizeAndCount(object_bytes_in, 1));
       }
     }
 
@@ -1677,104 +1983,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));
-  MemMap::Init();
-  if (oat_filename != nullptr) {
-    std::string error_msg;
-    OatFile* oat_file =
-        OatFile::Open(oat_filename, oat_filename, nullptr, 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;
@@ -1782,7 +1994,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) {
@@ -1801,14 +2012,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();
@@ -1816,11 +2037,229 @@
     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, 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, 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_));
+  } else {
+    MemMap::Init();
+  }
+
+  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 b9637d0..75160ca 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -24,11 +24,14 @@
 #include <string>
 #include <vector>
 
+#include "base/dumpable.h"
 #include "base/scoped_flock.h"
 #include "base/stringpiece.h"
 #include "base/stringprintf.h"
+#include "base/unix_file/fd_file.h"
 #include "elf_utils.h"
 #include "elf_file.h"
+#include "elf_file_impl.h"
 #include "gc/space/image_space.h"
 #include "image.h"
 #include "instruction_set.h"
@@ -463,14 +466,15 @@
 }
 
 void PatchOat::PatchVisitor::operator() (mirror::Object* obj, MemberOffset off,
-                                         bool is_static_unused) const {
+                                         bool is_static_unused ATTRIBUTE_UNUSED) const {
   mirror::Object* referent = obj->GetFieldObject<mirror::Object, kVerifyNone>(off);
   DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap.";
   mirror::Object* moved_object = patcher_->RelocatedAddressOf(referent);
   copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(off, moved_object);
 }
 
-void PatchOat::PatchVisitor::operator() (mirror::Class* cls, mirror::Reference* ref) const {
+void PatchOat::PatchVisitor::operator() (mirror::Class* cls ATTRIBUTE_UNUSED,
+                                         mirror::Reference* ref) const {
   MemberOffset off = mirror::Reference::ReferentOffset();
   mirror::Object* referent = ref->GetReferent();
   DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap.";
@@ -494,11 +498,20 @@
   if (obj == nullptr) {
     return nullptr;
   } else {
-    return reinterpret_cast<mirror::Object*>(reinterpret_cast<byte*>(obj) + delta_);
+    return reinterpret_cast<mirror::Object*>(reinterpret_cast<uint8_t*>(obj) + delta_);
   }
 }
 
 const OatHeader* PatchOat::GetOatHeader(const ElfFile* elf_file) {
+  if (elf_file->Is64Bit()) {
+    return GetOatHeader<ElfFileImpl64>(elf_file->GetImpl64());
+  } else {
+    return GetOatHeader<ElfFileImpl32>(elf_file->GetImpl32());
+  }
+}
+
+template <typename ElfFileImpl>
+const OatHeader* PatchOat::GetOatHeader(const ElfFileImpl* elf_file) {
   auto rodata_sec = elf_file->FindSectionByName(".rodata");
   if (rodata_sec == nullptr) {
     return nullptr;
@@ -531,13 +544,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) {
@@ -605,18 +616,16 @@
   return true;
 }
 
-bool PatchOat::CheckOatFile() {
-  Elf32_Shdr* patches_sec = oat_file_->FindSectionByName(".oat_patches");
-  if (patches_sec == nullptr) {
-    return false;
-  }
+template <typename ElfFileImpl, typename ptr_t>
+bool PatchOat::CheckOatFile(ElfFileImpl* oat_file) {
+  auto patches_sec = oat_file->FindSectionByName(".oat_patches");
   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));
-  Elf32_Shdr* oat_data_sec = oat_file_->FindSectionByName(".rodata");
-  Elf32_Shdr* oat_text_sec = oat_file_->FindSectionByName(".text");
+  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));
+  auto oat_data_sec = oat_file->FindSectionByName(".rodata");
+  auto oat_text_sec = oat_file->FindSectionByName(".text");
   if (oat_data_sec == nullptr) {
     return false;
   }
@@ -636,14 +645,15 @@
   return true;
 }
 
-bool PatchOat::PatchOatHeader() {
-  Elf32_Shdr *rodata_sec = oat_file_->FindSectionByName(".rodata");
+template <typename ElfFileImpl>
+bool PatchOat::PatchOatHeader(ElfFileImpl* oat_file) {
+  auto rodata_sec = oat_file->FindSectionByName(".rodata");
   if (rodata_sec == nullptr) {
     return false;
   }
-  OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file_->Begin() + rodata_sec->sh_offset);
+  OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file->Begin() + rodata_sec->sh_offset);
   if (!oat_header->IsValid()) {
-    LOG(ERROR) << "Elf file " << oat_file_->GetFile().GetPath() << " has an invalid oat header";
+    LOG(ERROR) << "Elf file " << oat_file->GetFile().GetPath() << " has an invalid oat header";
     return false;
   }
   oat_header->RelocateOat(delta_);
@@ -651,28 +661,30 @@
 }
 
 bool PatchOat::PatchElf() {
+  if (oat_file_->Is64Bit())
+    return PatchElf<ElfFileImpl64>(oat_file_->GetImpl64());
+  else
+    return PatchElf<ElfFileImpl32>(oat_file_->GetImpl32());
+}
+
+template <typename ElfFileImpl>
+bool PatchOat::PatchElf(ElfFileImpl* oat_file) {
   TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_);
-  if (!PatchTextSection()) {
+  if (!PatchTextSection<ElfFileImpl>(oat_file)) {
     return false;
   }
 
-  if (!PatchOatHeader()) {
+  if (!PatchOatHeader<ElfFileImpl>(oat_file)) {
     return false;
   }
 
   bool need_fixup = false;
-  t.NewTiming("Fixup Elf Headers");
-  // Fixup Phdr's
-  for (unsigned int i = 0; i < oat_file_->GetProgramHeaderNum(); i++) {
-    Elf32_Phdr* hdr = oat_file_->GetProgramHeader(i);
-    CHECK(hdr != nullptr);
-    if (hdr->p_vaddr != 0 && hdr->p_vaddr != hdr->p_offset) {
+  for (unsigned int i = 0; i < oat_file->GetProgramHeaderNum(); ++i) {
+    auto hdr = oat_file->GetProgramHeader(i);
+    if ((hdr->p_vaddr != 0 && hdr->p_vaddr != hdr->p_offset) ||
+        (hdr->p_paddr != 0 && hdr->p_paddr != hdr->p_offset)) {
       need_fixup = true;
-      hdr->p_vaddr += delta_;
-    }
-    if (hdr->p_paddr != 0 && hdr->p_paddr != hdr->p_offset) {
-      need_fixup = true;
-      hdr->p_paddr += delta_;
+      break;
     }
   }
   if (!need_fixup) {
@@ -680,73 +692,70 @@
     // their addr. Therefore we do not need to update these parts.
     return true;
   }
+
+  t.NewTiming("Fixup Elf Headers");
+  // Fixup Phdr's
+  oat_file->FixupProgramHeaders(delta_);
+
   t.NewTiming("Fixup Section Headers");
-  for (unsigned int i = 0; i < oat_file_->GetSectionHeaderNum(); i++) {
-    Elf32_Shdr* hdr = oat_file_->GetSectionHeader(i);
-    CHECK(hdr != nullptr);
-    if (hdr->sh_addr != 0) {
-      hdr->sh_addr += delta_;
-    }
-  }
+  // Fixup Shdr's
+  oat_file->FixupSectionHeaders(delta_);
 
   t.NewTiming("Fixup Dynamics");
-  for (Elf32_Word i = 0; i < oat_file_->GetDynamicNum(); i++) {
-    Elf32_Dyn& dyn = oat_file_->GetDynamic(i);
-    if (IsDynamicSectionPointer(dyn.d_tag, oat_file_->GetHeader().e_machine)) {
-      dyn.d_un.d_ptr += delta_;
-    }
-  }
+  oat_file->FixupDynamic(delta_);
 
   t.NewTiming("Fixup Elf Symbols");
   // Fixup dynsym
-  Elf32_Shdr* dynsym_sec = oat_file_->FindSectionByName(".dynsym");
-  CHECK(dynsym_sec != nullptr);
-  if (!PatchSymbols(dynsym_sec)) {
+  if (!oat_file->FixupSymbols(delta_, true)) {
+    return false;
+  }
+  // Fixup symtab
+  if (!oat_file->FixupSymbols(delta_, false)) {
     return false;
   }
 
-  // Fixup symtab
-  Elf32_Shdr* symtab_sec = oat_file_->FindSectionByName(".symtab");
-  if (symtab_sec != nullptr) {
-    if (!PatchSymbols(symtab_sec)) {
-      return false;
-    }
+  t.NewTiming("Fixup Debug Sections");
+  if (!oat_file->FixupDebugSections(delta_)) {
+    return false;
   }
 
   return true;
 }
 
-bool PatchOat::PatchSymbols(Elf32_Shdr* section) {
-  Elf32_Sym* syms = reinterpret_cast<Elf32_Sym*>(oat_file_->Begin() + section->sh_offset);
-  const Elf32_Sym* last_sym =
-      reinterpret_cast<Elf32_Sym*>(oat_file_->Begin() + section->sh_offset + section->sh_size);
-  CHECK_EQ(section->sh_size % sizeof(Elf32_Sym), 0u)
-      << "Symtab section size is not multiple of symbol size";
-  for (; syms < last_sym; syms++) {
-    uint8_t sttype = ELF32_ST_TYPE(syms->st_info);
-    Elf32_Word shndx = syms->st_shndx;
-    if (shndx != SHN_ABS && shndx != SHN_COMMON && shndx != SHN_UNDEF &&
-        (sttype == STT_FUNC || sttype == STT_OBJECT)) {
-      CHECK_NE(syms->st_value, 0u);
-      syms->st_value += delta_;
-    }
-  }
-  return true;
-}
-
-bool PatchOat::PatchTextSection() {
-  Elf32_Shdr* patches_sec = oat_file_->FindSectionByName(".oat_patches");
+template <typename ElfFileImpl>
+bool PatchOat::PatchTextSection(ElfFileImpl* oat_file) {
+  auto patches_sec = oat_file->FindSectionByName(".oat_patches");
   if (patches_sec == nullptr) {
     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));
-  Elf32_Shdr* oat_text_sec = oat_file_->FindSectionByName(".text");
+  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<ElfFileImpl, uint32_t>(oat_file);
+    case sizeof(uint64_t):
+      return PatchTextSection<ElfFileImpl, uint64_t>(oat_file);
+    default:
+      LOG(ERROR) << ".oat_patches Entsize of " << patches_sec->sh_entsize << "bits "
+                 << "is not valid";
+      return false;
+  }
+}
+
+template <typename ElfFileImpl, typename patch_loc_t>
+bool PatchOat::PatchTextSection(ElfFileImpl* oat_file) {
+  bool oat_file_valid = CheckOatFile<ElfFileImpl, patch_loc_t>(oat_file);
+  CHECK(oat_file_valid) << "Oat file invalid";
+  auto patches_sec = oat_file->FindSectionByName(".oat_patches");
+  patch_loc_t* patches = reinterpret_cast<patch_loc_t*>(oat_file->Begin() + patches_sec->sh_offset);
+  patch_loc_t* patches_end = patches + (patches_sec->sh_size / sizeof(patch_loc_t));
+  auto oat_text_sec = oat_file->FindSectionByName(".text");
   CHECK(oat_text_sec != nullptr);
-  byte* to_patch = oat_file_->Begin() + oat_text_sec->sh_offset;
+  uint8_t* to_patch = oat_file->Begin() + oat_text_sec->sh_offset;
   uintptr_t to_patch_end = reinterpret_cast<uintptr_t>(to_patch) + oat_text_sec->sh_size;
 
   for (; patches < patches_end; patches++) {
@@ -755,7 +764,6 @@
     CHECK_LT(reinterpret_cast<uintptr_t>(patch_loc), to_patch_end);
     *patch_loc += delta_;
   }
-
   return true;
 }
 
@@ -783,7 +791,7 @@
   va_end(ap);
 }
 
-static void Usage(const char *fmt, ...) {
+[[noreturn]] static void Usage(const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   UsageErrorV(fmt, ap);
@@ -887,7 +895,7 @@
     if (f.get() != nullptr) {
       if (fchmod(f->Fd(), 0644) != 0) {
         PLOG(ERROR) << "Unable to make " << name << " world readable";
-        unlink(name);
+        TEMP_FAILURE_RETRY(unlink(name));
         return nullptr;
       }
     }
@@ -940,7 +948,7 @@
   bool dump_timings = kIsDebugBuild;
   bool lock_output = true;
 
-  for (int i = 0; i < argc; i++) {
+  for (int i = 0; i < argc; ++i) {
     const StringPiece option(argv[i]);
     const bool log_options = false;
     if (log_options) {
@@ -1222,11 +1230,11 @@
     if (!success) {
       if (new_oat_out) {
         CHECK(!output_oat_filename.empty());
-        unlink(output_oat_filename.c_str());
+        TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str()));
       }
       if (new_image_out) {
         CHECK(!output_image_filename.empty());
-        unlink(output_image_filename.c_str());
+        TEMP_FAILURE_RETRY(unlink(output_image_filename.c_str()));
       }
     }
     if (dump_timings) {
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 21041fb..0ceef64 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -37,7 +37,7 @@
 class Reference;
 class Class;
 class ArtMethod;
-};  // namespace mirror
+}  // namespace mirror
 
 class PatchOat {
  public:
@@ -60,7 +60,8 @@
  private:
   // Takes ownership only of the ElfFile. All other pointers are only borrowed.
   PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings)
-      : oat_file_(oat_file), delta_(delta), timings_(timings) {}
+      : oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta),
+        timings_(timings) {}
   PatchOat(MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
            MemMap* heap, off_t delta, TimingLogger* timings)
       : image_(image), bitmap_(bitmap), heap_(heap),
@@ -102,13 +103,17 @@
       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();
-  bool PatchOatHeader();
-  bool PatchSymbols(Elf32_Shdr* section);
+  template <typename ElfFileImpl>
+  bool PatchElf(ElfFileImpl* oat_file);
+  template <typename ElfFileImpl>
+  bool PatchTextSection(ElfFileImpl* oat_file);
+  // Templatized version to actually do the patching with the right sized offsets.
+  template <typename ElfFileImpl, typename patch_loc_t> bool PatchTextSection(ElfFileImpl* oat_file);
+  template <typename ElfFileImpl, typename patch_loc_t> bool CheckOatFile(ElfFileImpl* oat_filec);
+  template <typename ElfFileImpl>
+  bool PatchOatHeader(ElfFileImpl* oat_file);
 
   bool PatchImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -121,6 +126,10 @@
   // Look up the oat header from any elf file.
   static const OatHeader* GetOatHeader(const ElfFile* elf_file);
 
+  // Templatized version to actually look up the oat header
+  template <typename ElfFileImpl>
+  static const OatHeader* GetOatHeader(const ElfFileImpl* elf_file);
+
   // Walks through the old image and patches the mmap'd copy of it to the new offset. It does not
   // change the heap.
   class PatchVisitor {
@@ -133,21 +142,22 @@
     void operator() (mirror::Class* cls, mirror::Reference* ref) const
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
   private:
-    PatchOat* patcher_;
-    mirror::Object* copy_;
+    PatchOat* const patcher_;
+    mirror::Object* const copy_;
   };
 
   // The elf file we are patching.
   std::unique_ptr<ElfFile> oat_file_;
   // A mmap of the image we are patching. This is modified.
-  const MemMap* image_;
+  const MemMap* const image_;
+  // The bitmap over the image within the heap we are patching. This is not modified.
+  gc::accounting::ContinuousSpaceBitmap* const bitmap_;
   // The heap we are patching. This is not modified.
-  gc::accounting::ContinuousSpaceBitmap* bitmap_;
-  // The heap we are patching. This is not modified.
-  const MemMap* heap_;
+  const MemMap* const heap_;
   // The amount we are changing the offset by.
-  off_t delta_;
-  TimingLogger* timings_;
+  const off_t delta_;
+  // Timing splits.
+  TimingLogger* const timings_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(PatchOat);
 };
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 17ee8ab..4505b8e 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -31,7 +31,6 @@
   base/stringprintf.cc \
   base/timing_logger.cc \
   base/unix_file/fd_file.cc \
-  base/unix_file/mapped_file.cc \
   base/unix_file/null_file.cc \
   base/unix_file/random_access_file_utils.cc \
   base/unix_file/string_file.cc \
@@ -80,6 +79,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 +87,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 \
@@ -207,7 +208,6 @@
 
 LIBART_TARGET_SRC_FILES := \
   $(LIBART_COMMON_SRC_FILES) \
-  base/logging_android.cc \
   jdwp/jdwp_adb.cc \
   monitor_android.cc \
   runtime_android.cc \
@@ -216,11 +216,12 @@
 LIBART_TARGET_SRC_FILES_arm := \
   arch/arm/context_arm.cc.arm \
   arch/arm/entrypoints_init_arm.cc \
+  arch/arm/instruction_set_features_arm.S \
   arch/arm/jni_entrypoints_arm.S \
   arch/arm/memcmp16_arm.S \
   arch/arm/portable_entrypoints_arm.S \
   arch/arm/quick_entrypoints_arm.S \
-  arch/arm/arm_sdiv.S \
+  arch/arm/quick_entrypoints_cc_arm.cc \
   arch/arm/thread_arm.cc \
   arch/arm/fault_handler_arm.cc
 
@@ -280,7 +281,6 @@
 
 LIBART_HOST_SRC_FILES := \
   $(LIBART_COMMON_SRC_FILES) \
-  base/logging_linux.cc \
   monitor_linux.cc \
   runtime_linux.cc \
   thread_linux.cc
@@ -295,12 +295,16 @@
   arch/x86_64/registers_x86_64.h \
   base/allocator.h \
   base/mutex.h \
+  debugger.h \
   dex_file.h \
   dex_instruction.h \
+  gc/allocator/rosalloc.h \
   gc/collector/gc_type.h \
+  gc/allocator_type.h \
   gc/collector_type.h \
   gc/space/space.h \
   gc/heap.h \
+  instrumentation.h \
   indirect_reference_table.h \
   instruction_set.h \
   invoke_type.h \
@@ -310,12 +314,15 @@
   mirror/class.h \
   oat.h \
   object_callbacks.h \
+  profiler_options.h \
   quick/inline_method_analyser.h \
+  runtime.h \
+  stack.h \
   thread.h \
   thread_state.h \
   verifier/method_verifier.h
 
-LIBART_CFLAGS :=
+LIBART_CFLAGS := -DBUILDING_LIBART=1
 ifeq ($(ART_USE_PORTABLE_COMPILER),true)
   LIBART_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
 endif
@@ -326,8 +333,27 @@
   LIBART_CFLAGS += -DUSE_JEMALLOC
 endif
 
-ifeq ($(ART_USE_HSPACE_COMPACT),true)
-  LIBART_CFLAGS += -DART_USE_HSPACE_COMPACT
+# Default dex2oat instruction set features.
+LIBART_HOST_DEFAULT_INSTRUCTION_SET_FEATURES := default
+LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := default
+2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := default
+ifeq ($(DEX2OAT_TARGET_ARCH),arm)
+  ifneq (,$(filter $(DEX2OAT_TARGET_CPU_VARIANT),cortex-a15 krait denver))
+    LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := lpae,div
+  else
+    ifneq (,$(filter $(DEX2OAT_TARGET_CPU_VARIANT),cortex-a7))
+      LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := div
+    endif
+  endif
+endif
+ifeq ($(2ND_DEX2OAT_TARGET_ARCH),arm)
+  ifneq (,$(filter $(DEX2OAT_TARGET_CPU_VARIANT),cortex-a15 krait denver))
+    2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := lpae,div
+  else
+    ifneq (,$(filter $(DEX2OAT_TARGET_CPU_VARIANT),cortex-a7))
+      2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := div
+    endif
+  endif
 endif
 
 # $(1): target or host
@@ -348,6 +374,13 @@
   art_ndebug_or_debug := $(2)
 
   include $$(CLEAR_VARS)
+  # Clang assembler has problem with macros in asm_support_x86.S, http://b/17443165,
+  # on linux. Yet sdk on mac needs integrated assembler.
+  ifeq ($$(HOST_OS),darwin)
+    LOCAL_CLANG_ASFLAGS += -integrated-as
+  else
+    LOCAL_CLANG_ASFLAGS += -no-integrated-as
+  endif
   LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION)
   ifeq ($$(art_ndebug_or_debug),ndebug)
     LOCAL_MODULE := libart
@@ -395,6 +428,9 @@
   ifeq ($$(art_target_or_host),target)
     $$(eval $$(call set-target-local-clang-vars))
     $$(eval $$(call set-target-local-cflags-vars,$(2)))
+    LOCAL_CFLAGS_$(DEX2OAT_TARGET_ARCH) += -DART_DEFAULT_INSTRUCTION_SET_FEATURES="$(LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES)"
+    LOCAL_CFLAGS_$(2ND_DEX2OAT_TARGET_ARCH) += -DART_DEFAULT_INSTRUCTION_SET_FEATURES="$(2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES)"
+
     # TODO: Loop with ifeq, ART_TARGET_CLANG
     ifneq ($$(ART_TARGET_CLANG_$$(TARGET_ARCH)),true)
       LOCAL_SRC_FILES_$$(TARGET_ARCH) += $$(LIBART_GCC_ONLY_SRC_FILES)
@@ -403,37 +439,45 @@
       LOCAL_SRC_FILES_$$(TARGET_2ND_ARCH) += $$(LIBART_GCC_ONLY_SRC_FILES)
     endif
   else # host
-    LOCAL_CLANG := $$(ART_HOST_CLANG)
-    ifeq ($$(ART_HOST_CLANG),false)
+    ifneq ($$(ART_HOST_CLANG),true)
+      # Add files only built with GCC on the host.
       LOCAL_SRC_FILES += $$(LIBART_GCC_ONLY_SRC_FILES)
     endif
+    LOCAL_CLANG := $$(ART_HOST_CLANG)
+    LOCAL_LDLIBS := $$(ART_HOST_LDLIBS)
+    LOCAL_LDLIBS += -ldl -lpthread
+    ifeq ($$(HOST_OS),linux)
+      LOCAL_LDLIBS += -lrt
+    endif
     LOCAL_CFLAGS += $$(ART_HOST_CFLAGS)
+    LOCAL_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES="$(LIBART_HOST_DEFAULT_INSTRUCTION_SET_FEATURES)"
+
     ifeq ($$(art_ndebug_or_debug),debug)
       LOCAL_CFLAGS += $$(ART_HOST_DEBUG_CFLAGS)
-      LOCAL_LDLIBS += $$(ART_HOST_DEBUG_LDLIBS)
-      LOCAL_STATIC_LIBRARIES := libgtest_host
     else
       LOCAL_CFLAGS += $$(ART_HOST_NON_DEBUG_CFLAGS)
     endif
+    LOCAL_MULTILIB := both
   endif
 
   LOCAL_C_INCLUDES += $$(ART_C_INCLUDES)
   LOCAL_C_INCLUDES += art/sigchainlib
 
-  LOCAL_SHARED_LIBRARIES += liblog libnativehelper libnativebridge
+  LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain
   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 += libdl
+    # ZipArchive support, the order matters here to get all symbols.
     LOCAL_STATIC_LIBRARIES := libziparchive libz
+    # For android::FileMap used by libziparchive.
+    LOCAL_SHARED_LIBRARIES += libutils
+    # For liblog, atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted.
+    LOCAL_SHARED_LIBRARIES += libcutils
   else # host
-    LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils
-    LOCAL_SHARED_LIBRARIES += libsigchain
-    LOCAL_LDLIBS += -ldl -lpthread
-    ifeq ($$(HOST_OS),linux)
-      LOCAL_LDLIBS += -lrt
-    endif
-    LOCAL_MULTILIB := both
+    LOCAL_SHARED_LIBRARIES += libziparchive-host
+    # For ashmem_create_region.
+    LOCAL_STATIC_LIBRARIES += libcutils
   endif
   ifeq ($$(ART_USE_PORTABLE_COMPILER),true)
     include $$(LLVM_GEN_INTRINSICS_MK)
@@ -444,7 +488,7 @@
     endif
   endif
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
-#  LOCAL_ADDITIONAL_DEPENDENCIES += $$(LOCAL_PATH)/Android.mk
+  LOCAL_ADDITIONAL_DEPENDENCIES += $$(LOCAL_PATH)/Android.mk
 
   ifeq ($$(art_target_or_host),target)
     LOCAL_MODULE_TARGET_ARCH := $$(ART_TARGET_SUPPORTED_ARCH)
@@ -471,10 +515,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
 
@@ -490,6 +534,9 @@
 LOCAL_PATH :=
 LIBART_COMMON_SRC_FILES :=
 LIBART_GCC_ONLY_SRC_FILES :=
+LIBART_HOST_DEFAULT_INSTRUCTION_SET_FEATURES :=
+LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES :=
+2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES :=
 LIBART_TARGET_LDFLAGS :=
 LIBART_HOST_LDFLAGS :=
 LIBART_TARGET_SRC_FILES :=
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index 5220dc3..cac500c 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -32,7 +32,7 @@
     t->TransitionFromSuspendedToRunnable();  // So we can create callee-save methods.
 
     r->SetInstructionSet(isa);
-    mirror::ArtMethod* save_method = r->CreateCalleeSaveMethod(type);
+    mirror::ArtMethod* save_method = r->CreateCalleeSaveMethod();
     r->SetCalleeSaveMethod(save_method, type);
     QuickMethodFrameInfo frame_info = save_method->GetQuickFrameInfo();
     EXPECT_EQ(frame_info.FrameSizeInBytes(), save_size) << "Expected and real size differs for "
@@ -43,398 +43,98 @@
   }
 };
 
+// Common tests are declared next to the constants.
+#define ADD_TEST_EQ(x, y) EXPECT_EQ(x, y);
+#include "asm_support.h"
 
-TEST_F(ArchTest, ARM) {
+TEST_F(ArchTest, CheckCommonOffsetsAndSizes) {
+  CheckAsmSupportOffsetsAndSizes();
+}
+
+// Grab architecture specific constants.
+namespace arm {
 #include "arch/arm/asm_support_arm.h"
-#undef ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
-
-
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kArm, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for SaveAll";
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kArm, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsOnly";
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kArm, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsAndArgs";
-#endif
-
-
-#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef THREAD_SELF_OFFSET
-#undef THREAD_SELF_OFFSET
-#endif
-#ifdef THREAD_CARD_TABLE_OFFSET
-#undef THREAD_CARD_TABLE_OFFSET
-#endif
-#ifdef THREAD_EXCEPTION_OFFSET
-#undef THREAD_EXCEPTION_OFFSET
-#endif
-#ifdef THREAD_ID_OFFSET
-#undef THREAD_ID_OFFSET
-#endif
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE;
 #undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE;
 #undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE;
 #undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-#endif
-#ifdef HEAP_REFERENCE_SIZE
-#undef HEAP_REFERENCE_SIZE
-#endif
+}
+
+namespace arm64 {
+#include "arch/arm64/asm_support_arm64.h"
+static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE;
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+}
+
+namespace mips {
+#include "arch/mips/asm_support_mips.h"
+static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE;
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+}
+
+namespace x86 {
+#include "arch/x86/asm_support_x86.h"
+static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE;
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+}
+
+namespace x86_64 {
+#include "arch/x86_64/asm_support_x86_64.h"
+static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE;
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+}
+
+// Check architecture specific constants are sound.
+TEST_F(ArchTest, ARM) {
+  CheckFrameSize(InstructionSet::kArm, Runtime::kSaveAll, arm::kFrameSizeSaveAllCalleeSave);
+  CheckFrameSize(InstructionSet::kArm, Runtime::kRefsOnly, arm::kFrameSizeRefsOnlyCalleeSave);
+  CheckFrameSize(InstructionSet::kArm, Runtime::kRefsAndArgs, arm::kFrameSizeRefsAndArgsCalleeSave);
 }
 
 
 TEST_F(ArchTest, ARM64) {
-#include "arch/arm64/asm_support_arm64.h"
-#undef ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
-
-
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kArm64, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for SaveAll";
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsOnly";
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsAndArgs";
-#endif
-
-
-#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef THREAD_SELF_OFFSET
-#undef THREAD_SELF_OFFSET
-#endif
-#ifdef THREAD_CARD_TABLE_OFFSET
-#undef THREAD_CARD_TABLE_OFFSET
-#endif
-#ifdef THREAD_EXCEPTION_OFFSET
-#undef THREAD_EXCEPTION_OFFSET
-#endif
-#ifdef THREAD_ID_OFFSET
-#undef THREAD_ID_OFFSET
-#endif
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-#endif
-#ifdef HEAP_REFERENCE_SIZE
-#undef HEAP_REFERENCE_SIZE
-#endif
+  CheckFrameSize(InstructionSet::kArm64, Runtime::kSaveAll, arm64::kFrameSizeSaveAllCalleeSave);
+  CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsOnly, arm64::kFrameSizeRefsOnlyCalleeSave);
+  CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsAndArgs,
+                 arm64::kFrameSizeRefsAndArgsCalleeSave);
 }
 
-
 TEST_F(ArchTest, MIPS) {
-#include "arch/mips/asm_support_mips.h"
-#undef ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_H_
-
-
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kMips, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for SaveAll";
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kMips, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsOnly";
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kMips, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsAndArgs";
-#endif
-
-
-#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef THREAD_SELF_OFFSET
-#undef THREAD_SELF_OFFSET
-#endif
-#ifdef THREAD_CARD_TABLE_OFFSET
-#undef THREAD_CARD_TABLE_OFFSET
-#endif
-#ifdef THREAD_EXCEPTION_OFFSET
-#undef THREAD_EXCEPTION_OFFSET
-#endif
-#ifdef THREAD_ID_OFFSET
-#undef THREAD_ID_OFFSET
-#endif
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-#endif
-#ifdef HEAP_REFERENCE_SIZE
-#undef HEAP_REFERENCE_SIZE
-#endif
+  CheckFrameSize(InstructionSet::kMips, Runtime::kSaveAll, mips::kFrameSizeSaveAllCalleeSave);
+  CheckFrameSize(InstructionSet::kMips, Runtime::kRefsOnly, mips::kFrameSizeRefsOnlyCalleeSave);
+  CheckFrameSize(InstructionSet::kMips, Runtime::kRefsAndArgs,
+                 mips::kFrameSizeRefsAndArgsCalleeSave);
 }
 
-
 TEST_F(ArchTest, X86) {
-#include "arch/x86/asm_support_x86.h"
-#undef ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
-
-
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kX86, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for SaveAll";
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kX86, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsOnly";
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kX86, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsAndArgs";
-#endif
-
-
-#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef THREAD_SELF_OFFSET
-#undef THREAD_SELF_OFFSET
-#endif
-#ifdef THREAD_CARD_TABLE_OFFSET
-#undef THREAD_CARD_TABLE_OFFSET
-#endif
-#ifdef THREAD_EXCEPTION_OFFSET
-#undef THREAD_EXCEPTION_OFFSET
-#endif
-#ifdef THREAD_ID_OFFSET
-#undef THREAD_ID_OFFSET
-#endif
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-#endif
-#ifdef HEAP_REFERENCE_SIZE
-#undef HEAP_REFERENCE_SIZE
-#endif
+  CheckFrameSize(InstructionSet::kX86, Runtime::kSaveAll, x86::kFrameSizeSaveAllCalleeSave);
+  CheckFrameSize(InstructionSet::kX86, Runtime::kRefsOnly, x86::kFrameSizeRefsOnlyCalleeSave);
+  CheckFrameSize(InstructionSet::kX86, Runtime::kRefsAndArgs, x86::kFrameSizeRefsAndArgsCalleeSave);
 }
 
-
 TEST_F(ArchTest, X86_64) {
-#include "arch/x86_64/asm_support_x86_64.h"
-#undef ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
-
-
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kX86_64, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for SaveAll";
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsOnly";
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-  CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
-#else
-  LOG(WARNING) << "No frame size for RefsAndArgs";
-#endif
-
-
-#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
-#endif
-#ifdef THREAD_SELF_OFFSET
-#undef THREAD_SELF_OFFSET
-#endif
-#ifdef THREAD_CARD_TABLE_OFFSET
-#undef THREAD_CARD_TABLE_OFFSET
-#endif
-#ifdef THREAD_EXCEPTION_OFFSET
-#undef THREAD_EXCEPTION_OFFSET
-#endif
-#ifdef THREAD_ID_OFFSET
-#undef THREAD_ID_OFFSET
-#endif
-#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
-#endif
-#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
-#endif
-#ifdef HEAP_REFERENCE_SIZE
-#undef HEAP_REFERENCE_SIZE
-#endif
-}
-
-
-// The following tests are all for the running architecture. So we get away
-// with just including it and not undefining it every time.
-
-#if defined(__arm__)
-#include "arch/arm/asm_support_arm.h"
-#elif defined(__aarch64__)
-#include "arch/arm64/asm_support_arm64.h"
-#elif defined(__mips__)
-#include "arch/mips/asm_support_mips.h"
-#elif defined(__i386__)
-#include "arch/x86/asm_support_x86.h"
-#elif defined(__x86_64__)
-#include "arch/x86_64/asm_support_x86_64.h"
-#else
-  // This happens for the host test.
-#ifdef __LP64__
-#include "arch/x86_64/asm_support_x86_64.h"
-#else
-#include "arch/x86/asm_support_x86.h"
-#endif
-#endif
-
-
-TEST_F(ArchTest, ThreadOffsets) {
-  // Ugly hack, change when possible.
-#ifdef __LP64__
-#define POINTER_SIZE 8
-#else
-#define POINTER_SIZE 4
-#endif
-
-#if defined(THREAD_SELF_OFFSET)
-  ThreadOffset<POINTER_SIZE> self_offset = Thread::SelfOffset<POINTER_SIZE>();
-  EXPECT_EQ(self_offset.Int32Value(), THREAD_SELF_OFFSET);
-#else
-  LOG(INFO) << "No Thread Self Offset found.";
-#endif
-
-#if defined(THREAD_CARD_TABLE_OFFSET)
-  ThreadOffset<POINTER_SIZE> card_offset = Thread::CardTableOffset<POINTER_SIZE>();
-  EXPECT_EQ(card_offset.Int32Value(), THREAD_CARD_TABLE_OFFSET);
-#else
-  LOG(INFO) << "No Thread Card Table Offset found.";
-#endif
-
-#if defined(THREAD_EXCEPTION_OFFSET)
-  ThreadOffset<POINTER_SIZE> exc_offset = Thread::ExceptionOffset<POINTER_SIZE>();
-    EXPECT_EQ(exc_offset.Int32Value(), THREAD_EXCEPTION_OFFSET);
-#else
-  LOG(INFO) << "No Thread Exception Offset found.";
-#endif
-
-#if defined(THREAD_ID_OFFSET)
-  ThreadOffset<POINTER_SIZE> id_offset = Thread::ThinLockIdOffset<POINTER_SIZE>();
-  EXPECT_EQ(id_offset.Int32Value(), THREAD_ID_OFFSET);
-#else
-  LOG(INFO) << "No Thread ID Offset found.";
-#endif
-}
-
-
-TEST_F(ArchTest, CalleeSaveMethodOffsets) {
-#if defined(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET)
-  EXPECT_EQ(Runtime::GetCalleeSaveMethodOffset(Runtime::kSaveAll),
-            static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET));
-#else
-  LOG(INFO) << "No Runtime Save-all Offset found.";
-#endif
-
-#if defined(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET)
-  EXPECT_EQ(Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsOnly),
-            static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET));
-#else
-  LOG(INFO) << "No Runtime Refs-only Offset found.";
-#endif
-
-#if defined(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET)
-  EXPECT_EQ(Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsAndArgs),
-            static_cast<size_t>(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET));
-#else
-  LOG(INFO) << "No Runtime Refs-and-Args Offset found.";
-#endif
-}
-
-
-TEST_F(ArchTest, HeapReferenceSize) {
-#if defined(HEAP_REFERENCE_SIZE)
-  EXPECT_EQ(sizeof(mirror::HeapReference<mirror::Object>),
-            static_cast<size_t>(HEAP_REFERENCE_SIZE));
-#else
-  LOG(INFO) << "No expected HeapReference Size found.";
-#endif
-}
-
-TEST_F(ArchTest, StackReferenceSize) {
-#if defined(STACK_REFERENCE_SIZE)
-  EXPECT_EQ(sizeof(StackReference<mirror::Object>),
-            static_cast<size_t>(STACK_REFERENCE_SIZE));
-#else
-  LOG(INFO) << "No expected StackReference Size #define found.";
-#endif
+  CheckFrameSize(InstructionSet::kX86_64, Runtime::kSaveAll, x86_64::kFrameSizeSaveAllCalleeSave);
+  CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsOnly, x86_64::kFrameSizeRefsOnlyCalleeSave);
+  CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsAndArgs,
+                 x86_64::kFrameSizeRefsAndArgsCalleeSave);
 }
 
 }  // namespace art
diff --git a/runtime/arch/arm/arm_sdiv.S b/runtime/arch/arm/arm_sdiv.S
deleted file mode 100644
index babdbf5..0000000
--- a/runtime/arch/arm/arm_sdiv.S
+++ /dev/null
@@ -1,24 +0,0 @@
-// This function is used to check for the CPU's support for the sdiv
-// instruction at runtime.  It will either return the value 1 or
-// will cause an invalid instruction trap (SIGILL signal).  The
-// caller must arrange for the signal handler to set the r0
-// register to 0 and move the pc forward by 4 bytes (to skip
-// the invalid instruction).
-
-
-#include "asm_support_arm.S"
-
-.section .text
-ENTRY_NO_HIDE CheckForARMSDIVInstruction
-  mov r1,#1
-  // depending on the architecture, the assembler will not allow an
-  // sdiv instruction, so we will have to output the bytes directly.
-
-  // sdiv r0,r1,r1 is two words: 0xfb91 0xf1f0.  We need little endian.
-  .byte 0x91,0xfb,0xf1,0xf0
-
-  // if the divide worked, r0 will have the value #1 (result of sdiv).
-  // It will have 0 otherwise (set by the signal handler)
-  // the value is just returned from this function.
-  bx lr
-  END CheckForARMSDIVInstruction
diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S
index a3e3b21..2af636e 100644
--- a/runtime/arch/arm/asm_support_arm.S
+++ b/runtime/arch/arm/asm_support_arm.S
@@ -30,63 +30,92 @@
 .arch armv7-a
 .thumb
 
+// Macro to generate the value of Runtime::Current into rDest clobbering rTemp. As it uses labels
+// then the labels need to be unique. We bind these to the function name in the ENTRY macros.
+.macro RUNTIME_CURRENT name, num, rDest, rTemp
+    .if .Lruntime_current\num\()_used
+         .error
+    .endif
+    .set .Lruntime_current\num\()_used, 1
+    ldr \rDest, .Lgot_\name\()_\num               @ Load offset of the GOT.
+    ldr \rTemp, .Lruntime_instance_\name\()_\num  @ Load GOT offset of Runtime::instance_.
+.Lload_got_\name\()_\num\():
+    add \rDest, pc                                @ Fixup GOT address.
+    ldr \rDest, [\rDest, \rTemp]                  @ Load address of Runtime::instance_.
+    ldr \rDest, [\rDest]                          @ Load Runtime::instance_.
+.endm
+
+// Common ENTRY declaration code for ARM and thumb, an ENTRY should always be paired with an END.
+// Declares the RUNTIME_CURRENT[123] macros that can be used within an ENTRY and will have literals
+// generated at END.
+.macro DEF_ENTRY thumb_or_arm, name
+    \thumb_or_arm
+    .type \name, #function
+    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
+    .global \name
+    // Cache alignment for function entry.
+    .balign 16
+\name:
+    .cfi_startproc
+    .fnstart
+    // Track whether RUNTIME_CURRENT was used.
+    .set .Lruntime_current1_used, 0
+    .set .Lruntime_current2_used, 0
+    .set .Lruntime_current3_used, 0
+    // The RUNTIME_CURRENT macros that are bound to the \name argument of DEF_ENTRY to ensure
+    // that label names are unique.
+    .macro RUNTIME_CURRENT1 rDest, rTemp
+        RUNTIME_CURRENT \name, 1, \rDest, \rTemp
+    .endm
+    .macro RUNTIME_CURRENT2 rDest, rTemp
+        RUNTIME_CURRENT \name, 2, \rDest, \rTemp
+    .endm
+    .macro RUNTIME_CURRENT3 rDest, rTemp
+        RUNTIME_CURRENT \name, 3, \rDest, \rTemp
+    .endm
+.endm
+
+// A thumb2 style ENTRY.
 .macro ENTRY name
-    .thumb_func
-    .type \name, #function
-    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
-    .global \name
-    /* Cache alignment for function entry */
-    .balign 16
-\name:
-    .cfi_startproc
-    .fnstart
+    DEF_ENTRY .thumb_func, \name
 .endm
 
-.macro ENTRY_NO_HIDE name
-    .thumb_func
-    .type \name, #function
-    .global \name
-    /* Cache alignment for function entry */
-    .balign 16
-\name:
-    .cfi_startproc
-    .fnstart
-.endm
-
-
+// A ARM style ENTRY.
 .macro ARM_ENTRY name
-    .arm
-    .type \name, #function
-    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
-    .global \name
-    /* Cache alignment for function entry */
-    .balign 16
-\name:
-    .cfi_startproc
-     /* Ensure we get a sane starting CFA. */
-    .cfi_def_cfa sp,0
-    .fnstart
+    DEF_ENTRY .arm, \name
 .endm
 
-.macro ARM_ENTRY_NO_HIDE name
-    .arm
-    .type \name, #function
-    .global \name
-    /* Cache alignment for function entry */
-    .balign 16
-\name:
-    .cfi_startproc
-     /* Ensure we get a sane starting CFA. */
-    .cfi_def_cfa sp,0
-    .fnstart
-.endm
-
+// Terminate an ENTRY and generate GOT references.
 .macro END name
+     // Generate offsets of GOT and Runtime::instance_ used in RUNTIME_CURRENT.
+     .if .Lruntime_current1_used
+         .Lgot_\name\()_1:
+             .word   _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_1+4)
+         .Lruntime_instance_\name\()_1:
+             .word   _ZN3art7Runtime9instance_E(GOT)
+     .endif
+     .if .Lruntime_current2_used
+         .Lgot_\name\()_2:
+             .word   _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_2+4)
+         .Lruntime_instance_\name\()_2:
+             .word   _ZN3art7Runtime9instance_E(GOT)
+    .endif
+     .if .Lruntime_current3_used
+         .Lgot_\name\()_3:
+             .word   _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_3+4)
+         .Lruntime_instance_\name\()_3:
+             .word   _ZN3art7Runtime9instance_E(GOT)
+    .endif
+    // Remove the RUNTIME_CURRENTx macros so they get rebound in the next function entry.
+    .purgem RUNTIME_CURRENT1
+    .purgem RUNTIME_CURRENT2
+    .purgem RUNTIME_CURRENT3
     .fnend
     .cfi_endproc
     .size \name, .-\name
 .endm
 
+// Declare an unimplemented ENTRY that will halt a debugger.
 .macro UNIMPLEMENTED name
     ENTRY \name
     bkpt
diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h
index 330924e..1fa566b 100644
--- a/runtime/arch/arm/asm_support_arm.h
+++ b/runtime/arch/arm/asm_support_arm.h
@@ -19,22 +19,11 @@
 
 #include "asm_support.h"
 
-// Offset of field Thread::tls32_.state_and_flags verified in InitCpu
-#define THREAD_FLAGS_OFFSET 0
-// Offset of field Thread::tls32_.thin_lock_thread_id verified in InitCpu
-#define THREAD_ID_OFFSET 12
-// Offset of field Thread::tlsPtr_.card_table verified in InitCpu
-#define THREAD_CARD_TABLE_OFFSET 120
-// Offset of field Thread::tlsPtr_.exception verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 124
-
-#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 112
 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32
-#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 48
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 112
 
-// Expected size of a heap reference
-#define HEAP_REFERENCE_SIZE 4
 // Flag for enabling R4 optimization in arm runtime
-#define ARM_R4_SUSPEND_FLAG
+// #define ARM_R4_SUSPEND_FLAG
 
 #endif  // ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
diff --git a/runtime/arch/arm/context_arm.cc b/runtime/arch/arm/context_arm.cc
index 96ffc93..9e8d282 100644
--- a/runtime/arch/arm/context_arm.cc
+++ b/runtime/arch/arm/context_arm.cc
@@ -17,10 +17,8 @@
 #include "context_arm.h"
 
 #include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
 #include "quick/quick_method_frame_info.h"
-#include "stack.h"
-#include "thread.h"
+#include "utils.h"
 
 namespace art {
 namespace arm {
@@ -97,6 +95,23 @@
   gprs_[R1] = const_cast<uint32_t*>(&gZero);
   gprs_[R2] = nullptr;
   gprs_[R3] = nullptr;
+
+  fprs_[S0] = nullptr;
+  fprs_[S1] = nullptr;
+  fprs_[S2] = nullptr;
+  fprs_[S3] = nullptr;
+  fprs_[S4] = nullptr;
+  fprs_[S5] = nullptr;
+  fprs_[S6] = nullptr;
+  fprs_[S7] = nullptr;
+  fprs_[S8] = nullptr;
+  fprs_[S9] = nullptr;
+  fprs_[S10] = nullptr;
+  fprs_[S11] = nullptr;
+  fprs_[S12] = nullptr;
+  fprs_[S13] = nullptr;
+  fprs_[S14] = nullptr;
+  fprs_[S15] = nullptr;
 }
 
 extern "C" void art_quick_do_long_jump(uint32_t*, uint32_t*);
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 8c6afd6..85a0dd2 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -18,114 +18,35 @@
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/math_entrypoints.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "interpreter/interpreter.h"
 
 namespace art {
 
-// Interpreter entrypoints.
-extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh,
-                                                  const DexFile::CodeItem* code_item,
-                                                  ShadowFrame* shadow_frame, JValue* result);
-extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh,
-                                                   const DexFile::CodeItem* code_item,
-                                                   ShadowFrame* shadow_frame, JValue* result);
-
-// Portable entrypoints.
-extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
-
 // Cast entrypoints.
 extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
                                             const mirror::Class* ref_class);
-extern "C" void art_quick_check_cast(void*, void*);
 
-// DexCache entrypoints.
-extern "C" void* art_quick_initialize_static_storage(uint32_t, void*);
-extern "C" void* art_quick_initialize_type(uint32_t, void*);
-extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, void*);
-extern "C" void* art_quick_resolve_string(void*, uint32_t);
 
-// Field entrypoints.
-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" 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*);
-extern "C" int64_t art_quick_get64_static(uint32_t);
-extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
-extern "C" void* art_quick_get_obj_static(uint32_t);
-
-// Array entrypoints.
-extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
-extern "C" void art_quick_handle_fill_data(void*, void*);
-
-// Lock entrypoints.
-extern "C" void art_quick_lock_object(void*);
-extern "C" void art_quick_unlock_object(void*);
-
-// Math entrypoints.
-extern int32_t CmpgDouble(double a, double b);
-extern int32_t CmplDouble(double a, double b);
-extern int32_t CmpgFloat(float a, float b);
-extern int32_t CmplFloat(float a, float b);
-
-// Math conversions.
-extern "C" int32_t __aeabi_f2iz(float op1);        // FLOAT_TO_INT
-extern "C" int32_t __aeabi_d2iz(double op1);       // DOUBLE_TO_INT
-extern "C" float __aeabi_l2f(int64_t op1);         // LONG_TO_FLOAT
-extern "C" double __aeabi_l2d(int64_t op1);        // LONG_TO_DOUBLE
-
+// Used by soft float.
 // Single-precision FP arithmetics.
-extern "C" float fmodf(float a, float b);          // REM_FLOAT[_2ADDR]
-
+extern "C" float fmodf(float a, float b);              // REM_FLOAT[_2ADDR]
 // Double-precision FP arithmetics.
-extern "C" double fmod(double a, double b);         // REM_DOUBLE[_2ADDR]
+extern "C" double fmod(double a, double b);            // REM_DOUBLE[_2ADDR]
+
+// Used by hard float.
+extern "C" float art_quick_fmodf(float a, float b);    // REM_FLOAT[_2ADDR]
+extern "C" double art_quick_fmod(double a, double b);  // REM_DOUBLE[_2ADDR]
 
 // Integer arithmetics.
 extern "C" int __aeabi_idivmod(int32_t, int32_t);  // [DIV|REM]_INT[_2ADDR|_LIT8|_LIT16]
 
 // Long long arithmetics - REM_LONG[_2ADDR] and DIV_LONG[_2ADDR]
 extern "C" int64_t __aeabi_ldivmod(int64_t, int64_t);
-extern "C" int64_t art_quick_mul_long(int64_t, int64_t);
-extern "C" uint64_t art_quick_shl_long(uint64_t, uint32_t);
-extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t);
-extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t);
-
-// Intrinsic entrypoints.
-extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
-extern "C" int32_t art_quick_string_compareto(void*, void*);
-
-// Invoke entrypoints.
-extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
-extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
-
-// Thread entrypoints.
-extern "C" void art_quick_test_suspend();
-
-// Throw entrypoints.
-extern "C" void art_quick_deliver_exception(void*);
-extern "C" void art_quick_throw_array_bounds(int32_t index, int32_t limit);
-extern "C" void art_quick_throw_div_zero();
-extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
-extern "C" void art_quick_throw_null_pointer_exception();
-extern "C" void art_quick_throw_stack_overflow(void*);
-
-// Generic JNI downcall
-extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
                      PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
@@ -154,15 +75,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;
@@ -187,25 +120,24 @@
   qpoints->pUnlockObject = art_quick_unlock_object;
 
   // Math
-  qpoints->pCmpgDouble = CmpgDouble;
-  qpoints->pCmpgFloat = CmpgFloat;
-  qpoints->pCmplDouble = CmplDouble;
-  qpoints->pCmplFloat = CmplFloat;
-  qpoints->pFmod = fmod;
-  qpoints->pL2d = __aeabi_l2d;
-  qpoints->pFmodf = fmodf;
-  qpoints->pL2f = __aeabi_l2f;
-  qpoints->pD2iz = __aeabi_d2iz;
-  qpoints->pF2iz = __aeabi_f2iz;
   qpoints->pIdivmod = __aeabi_idivmod;
-  qpoints->pD2l = art_d2l;
-  qpoints->pF2l = art_f2l;
   qpoints->pLdiv = __aeabi_ldivmod;
   qpoints->pLmod = __aeabi_ldivmod;  // result returned in r2:r3
   qpoints->pLmul = art_quick_mul_long;
   qpoints->pShlLong = art_quick_shl_long;
   qpoints->pShrLong = art_quick_shr_long;
   qpoints->pUshrLong = art_quick_ushr_long;
+  if (kArm32QuickCodeUseSoftFloat) {
+    qpoints->pFmod = fmod;
+    qpoints->pFmodf = fmodf;
+    qpoints->pD2l = art_d2l;
+    qpoints->pF2l = art_f2l;
+  } else {
+    qpoints->pFmod = art_quick_fmod;
+    qpoints->pFmodf = art_quick_fmodf;
+    qpoints->pD2l = art_quick_d2l;
+    qpoints->pF2l = art_quick_f2l;
+  }
 
   // Intrinsics
   qpoints->pIndexOf = art_quick_indexof;
@@ -232,6 +164,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/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index 564fcba..480190a 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -47,7 +47,8 @@
   return instr_size;
 }
 
-void FaultManager::HandleNestedSignal(int sig, siginfo_t* info, void* context) {
+void FaultManager::HandleNestedSignal(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                      void* context) {
   // Note that in this handler we set up the registers and return to
   // longjmp directly rather than going through an assembly language stub.  The
   // reason for this is that longjmp is (currently) in ARM mode and that would
@@ -64,7 +65,7 @@
   VLOG(signals) << "longjmp address: " << reinterpret_cast<void*>(sc->arm_pc);
 }
 
-void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
+void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo ATTRIBUTE_UNUSED, void* context,
                                              mirror::ArtMethod** out_method,
                                              uintptr_t* out_return_pc, uintptr_t* out_sp) {
   struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
@@ -100,7 +101,8 @@
   *out_return_pc = (sc->arm_pc + instr_size) | 1;
 }
 
-bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
+bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                void* context) {
   // The code that looks for the catch location needs to know the value of the
   // ARM PC at the point of call.  For Null checks we insert a GC map that is immediately after
   // the load/store instruction that might cause the fault.  However the mapping table has
@@ -127,7 +129,8 @@
 // The offset from r9 is Thread::ThreadSuspendTriggerOffset().
 // To check for a suspend check, we examine the instructions that caused
 // the fault (at PC-4 and PC).
-bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) {
+bool SuspensionHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                               void* context) {
   // These are the instructions to check for.  The first one is the ldr r0,[r9,#xxx]
   // where xxx is the offset of the suspend trigger.
   uint32_t checkinst1 = 0xf8d90000 + Thread::ThreadSuspendTriggerOffset<4>().Int32Value();
@@ -196,7 +199,8 @@
 // If we determine this is a stack overflow we need to move the stack pointer
 // to the overflow region below the protected region.
 
-bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) {
+bool StackOverflowHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                  void* context) {
   struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
   struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
   VLOG(signals) << "stack overflow handler with sp at " << std::hex << &uc;
diff --git a/runtime/arch/arm/instruction_set_features_arm.S b/runtime/arch/arm/instruction_set_features_arm.S
new file mode 100644
index 0000000..c26f2cd
--- /dev/null
+++ b/runtime/arch/arm/instruction_set_features_arm.S
@@ -0,0 +1,38 @@
+/*
+ * 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 "asm_support_arm.S"
+
+.section .text
+// This function is used to check for the CPU's support for the sdiv
+// instruction at runtime.  It will either return the value 1 or
+// will cause an invalid instruction trap (SIGILL signal).  The
+// caller must arrange for the signal handler to set the r0
+// register to 0 and move the pc forward by 4 bytes (to skip
+// the invalid instruction).
+ENTRY artCheckForARMSDIVInstruction
+  mov r1,#1
+  // depending on the architecture, the assembler will not allow an
+  // sdiv instruction, so we will have to output the bytes directly.
+
+  // sdiv r0,r1,r1 is two words: 0xfb91 0xf1f0.  We need little endian.
+  .byte 0x91,0xfb,0xf1,0xf0
+
+  // if the divide worked, r0 will have the value #1 (result of sdiv).
+  // It will have 0 otherwise (set by the signal handler)
+  // the value is just returned from this function.
+  bx lr
+END artCheckForARMSDIVInstruction
diff --git a/runtime/arch/arm/memcmp16_arm.S b/runtime/arch/arm/memcmp16_arm.S
index 3762194..b623a2a 100644
--- a/runtime/arch/arm/memcmp16_arm.S
+++ b/runtime/arch/arm/memcmp16_arm.S
@@ -65,7 +65,7 @@
 
 
         /* save registers */
-0:      stmfd       sp!, {r4, lr}
+0:      push        {r4, lr}
         .cfi_def_cfa_offset 8
         .cfi_rel_offset r4, 0
         .cfi_rel_offset lr, 4
@@ -79,7 +79,7 @@
         sub         r2, r2, #1
         subs        r0, r0, ip
         /* restore registers and return */
-        ldmnefd     sp!, {r4, lr}
+        popne       {r4, lr}
         bxne        lr
 
 
@@ -110,25 +110,25 @@
         eors        r0, r0, ip
         ldreq       r0, [r3], #4
         ldreq       ip, [r1, #4]!
-        eoreqs      r0, r0, lr
+        eorseq      r0, r0, lr
         ldreq       r0, [r3], #4
         ldreq       lr, [r1, #4]!
-        eoreqs      r0, r0, ip
+        eorseq      r0, r0, ip
         ldreq       r0, [r3], #4
         ldreq       ip, [r1, #4]!
-        eoreqs      r0, r0, lr
+        eorseq      r0, r0, lr
         ldreq       r0, [r3], #4
         ldreq       lr, [r1, #4]!
-        eoreqs      r0, r0, ip
+        eorseq      r0, r0, ip
         ldreq       r0, [r3], #4
         ldreq       ip, [r1, #4]!
-        eoreqs      r0, r0, lr
+        eorseq      r0, r0, lr
         ldreq       r0, [r3], #4
         ldreq       lr, [r1, #4]!
-        eoreqs      r0, r0, ip
+        eorseq      r0, r0, ip
         ldreq       r0, [r3], #4
         ldreq       ip, [r1, #4]!
-        eoreqs      r0, r0, lr
+        eorseq      r0, r0, lr
         bne         2f
         subs        r2, r2, #16
         bhs         0b
@@ -150,18 +150,24 @@
         bne         8f
         /* restore registers and return */
         mov         r0, #0
-        ldmfd       sp!, {r4, lr}
+        pop         {r4, lr}
+        .cfi_restore r4
+        .cfi_restore lr
+        .cfi_adjust_cfa_offset -8
         bx          lr
 
 2:      /* the last 2 words are different, restart them */
         ldrh        r0, [r3, #-4]
         ldrh        ip, [r1, #-4]
         subs        r0, r0, ip
-        ldreqh      r0, [r3, #-2]
-        ldreqh      ip, [r1, #-2]
-        subeqs      r0, r0, ip
+        ldrheq      r0, [r3, #-2]
+        ldrheq      ip, [r1, #-2]
+        subseq      r0, r0, ip
         /* restore registers and return */
-        ldmfd       sp!, {r4, lr}
+        pop         {r4, lr}
+        .cfi_restore r4
+        .cfi_restore lr
+        .cfi_adjust_cfa_offset -8
         bx          lr
 
         /* process the last few words */
@@ -173,7 +179,10 @@
         bne         8b
 
 9:      /* restore registers and return */
-        ldmfd       sp!, {r4, lr}
+        pop         {r4, lr}
+        .cfi_restore r4
+        .cfi_restore lr
+        .cfi_adjust_cfa_offset -8
         bx          lr
 
 
@@ -196,17 +205,17 @@
         ldreq       lr, [r1], #4
         ldreq       r0, [r3], #4
         orreq       ip, ip, lr, lsl #16
-        eoreqs      r0, r0, ip
+        eorseq      r0, r0, ip
         moveq       ip, lr, lsr #16
         ldreq       lr, [r1], #4
         ldreq       r0, [r3], #4
         orreq       ip, ip, lr, lsl #16
-        eoreqs      r0, r0, ip
+        eorseq      r0, r0, ip
         moveq       ip, lr, lsr #16
         ldreq       lr, [r1], #4
         ldreq       r0, [r3], #4
         orreq       ip, ip, lr, lsl #16
-        eoreqs      r0, r0, ip
+        eorseq      r0, r0, ip
         bne         7f
         subs        r2, r2, #8
         bhs         6b
diff --git a/runtime/arch/arm/portable_entrypoints_arm.S b/runtime/arch/arm/portable_entrypoints_arm.S
index 3491c18..d37e760 100644
--- a/runtime/arch/arm/portable_entrypoints_arm.S
+++ b/runtime/arch/arm/portable_entrypoints_arm.S
@@ -53,7 +53,7 @@
     mov    ip, #0                          @ set ip to 0
     str    ip, [sp]                        @ store NULL for method* at bottom of frame
     add    sp, #16                         @ first 4 args are not passed on stack for portable
-    ldr    ip, [r0, #METHOD_PORTABLE_CODE_OFFSET]  @ get pointer to the code
+    ldr    ip, [r0, #MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET]  @ get pointer to the code
     blx    ip                              @ call the method
     mov    sp, r11                         @ restore the stack pointer
     ldr    ip, [sp, #24]                   @ load the result pointer
@@ -138,7 +138,7 @@
 END art_portable_resolution_trampoline
 
     .extern artPortableToInterpreterBridge
-ENTRY_NO_HIDE art_portable_to_interpreter_bridge
+ENTRY art_portable_to_interpreter_bridge
     @ Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
     @ TODO: just save the registers that are needed in artPortableToInterpreterBridge.
     push {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
@@ -165,3 +165,5 @@
     .cfi_adjust_cfa_offset -48
     bx      lr                     @ return
 END art_portable_to_interpreter_bridge
+
+UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 1b30c9c..632b414 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -27,8 +27,8 @@
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kSaveAll)
      */
-.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    push {r4-r11, lr} @ 9 words of callee saves
+.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME rTemp1, rTemp2
+    push {r4-r11, lr}                             @ 9 words (36 bytes) of callee saves.
     .save {r4-r11, lr}
     .cfi_adjust_cfa_offset 36
     .cfi_rel_offset r4, 0
@@ -40,15 +40,20 @@
     .cfi_rel_offset r10, 24
     .cfi_rel_offset r11, 28
     .cfi_rel_offset lr, 32
-    vpush {s0-s31}
-    .pad #128
-    .cfi_adjust_cfa_offset 128
-    sub sp, #12       @ 3 words of space, bottom word will hold Method*
+    vpush {s16-s31}                               @ 16 words (64 bytes) of floats.
+    .pad #64
+    .cfi_adjust_cfa_offset 64
+    sub sp, #12                                   @ 3 words of space, bottom word will hold Method*
     .pad #12
     .cfi_adjust_cfa_offset 12
+    RUNTIME_CURRENT1 \rTemp1, \rTemp2             @ Load Runtime::Current into rTemp1.
+    THIS_LOAD_REQUIRES_READ_BARRIER
+    ldr \rTemp1, [\rTemp1, #RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kSaveAll Method*.
+    str \rTemp1, [sp, #0]                         @ Place Method* at bottom of stack.
+    str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
 
      // Ugly compile-time check, but we only have the preprocessor.
-#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 36 + 128 + 12)
+#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 36 + 64 + 12)
 #error "SAVE_ALL_CALLEE_SAVE_FRAME(ARM) size not as expected."
 #endif
 .endm
@@ -57,8 +62,8 @@
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kRefsOnly).
      */
-.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-    push {r5-r8, r10-r11, lr} @ 7 words of callee saves
+.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME rTemp1, rTemp2
+    push {r5-r8, r10-r11, lr}                     @ 7 words of callee saves
     .save {r5-r8, r10-r11, lr}
     .cfi_adjust_cfa_offset 28
     .cfi_rel_offset r5, 0
@@ -68,9 +73,14 @@
     .cfi_rel_offset r10, 16
     .cfi_rel_offset r11, 20
     .cfi_rel_offset lr, 24
-    sub sp, #4                @ bottom word will hold Method*
+    sub sp, #4                                    @ bottom word will hold Method*
     .pad #4
     .cfi_adjust_cfa_offset 4
+    RUNTIME_CURRENT2 \rTemp1, \rTemp2             @ Load Runtime::Current into rTemp1.
+    THIS_LOAD_REQUIRES_READ_BARRIER
+    ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kRefsOnly Method*.
+    str \rTemp1, [sp, #0]                         @ Place Method* at bottom of stack.
+    str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
 
     // Ugly compile-time check, but we only have the preprocessor.
 #if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 28 + 4)
@@ -78,7 +88,7 @@
 #endif
 .endm
 
-.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     add sp, #4               @ bottom word holds Method*
     pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
     .cfi_restore r5
@@ -87,19 +97,11 @@
     .cfi_restore r8
     .cfi_restore r10
     .cfi_restore r11
-    .cfi_adjust_cfa_offset -32
+    .cfi_adjust_cfa_offset -FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
 .endm
 
-.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
-    add sp, #4               @ bottom word holds Method*
-    pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
-    .cfi_restore r5
-    .cfi_restore r6
-    .cfi_restore r7
-    .cfi_restore r8
-    .cfi_restore r10
-    .cfi_restore r11
-    .cfi_adjust_cfa_offset -32
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     bx  lr                   @ return
 .endm
 
@@ -107,9 +109,10 @@
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kRefsAndArgs).
      */
-.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    push {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY
+    push {r1-r3, r5-r8, r10-r11, lr}   @ 10 words of callee saves and args.
     .save {r1-r3, r5-r8, r10-r11, lr}
+    .cfi_adjust_cfa_offset 40
     .cfi_rel_offset r1, 0
     .cfi_rel_offset r2, 4
     .cfi_rel_offset r3, 8
@@ -120,19 +123,39 @@
     .cfi_rel_offset r10, 28
     .cfi_rel_offset r11, 32
     .cfi_rel_offset lr, 36
-    .cfi_adjust_cfa_offset 40
-    sub sp, #8                        @ 2 words of space, bottom word will hold Method*
+    vpush {s0-s15}                     @ 16 words of float args.
+    .pad #64
+    .cfi_adjust_cfa_offset 64
+    sub sp, #8                         @ 2 words of space, bottom word will hold Method*
     .pad #8
     .cfi_adjust_cfa_offset 8
-
     // Ugly compile-time check, but we only have the preprocessor.
-#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 40 + 8)
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 40 + 64 + 8)
 #error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(ARM) size not as expected."
 #endif
 .endm
 
-.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME rTemp1, rTemp2
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY
+    RUNTIME_CURRENT3 \rTemp1, \rTemp2  @ Load Runtime::Current into rTemp1.
+    THIS_LOAD_REQUIRES_READ_BARRIER
+     @ rTemp1 is kRefsAndArgs Method*.
+    ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET]
+    str \rTemp1, [sp, #0]                         @ Place Method* at bottom of stack.
+    str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY
+    str r0, [sp, #0]                   @ Store ArtMethod* to bottom of stack.
+    str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+.macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     add  sp, #8                      @ rewind sp
+    .cfi_adjust_cfa_offset -8
+    vpop {s0-s15}
+    .cfi_adjust_cfa_offset -64
     pop {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
     .cfi_restore r1
     .cfi_restore r2
@@ -143,9 +166,10 @@
     .cfi_restore r8
     .cfi_restore r10
     .cfi_restore r11
-    .cfi_adjust_cfa_offset -48
+    .cfi_adjust_cfa_offset -40
 .endm
 
+
 .macro RETURN_IF_RESULT_IS_ZERO
     cbnz   r0, 1f              @ result non-zero branch over
     bx     lr                  @ return
@@ -165,44 +189,96 @@
 .macro DELIVER_PENDING_EXCEPTION
     .fnend
     .fnstart
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME           @ save callee saves for throw
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1    @ save callee saves for throw
     mov    r0, r9                              @ pass Thread::Current
-    mov    r1, sp                              @ pass SP
-    b      artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*, SP)
+    b      artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*)
 .endm
 
 .macro NO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
     .extern \cxx_name
 ENTRY \c_name
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  r0, r1 // save all registers as basis for long jump context
     mov r0, r9                      @ pass Thread::Current
-    mov r1, sp                      @ pass SP
-    b   \cxx_name                   @ \cxx_name(Thread*, SP)
+    b   \cxx_name                   @ \cxx_name(Thread*)
 END \c_name
 .endm
 
 .macro ONE_ARG_RUNTIME_EXCEPTION c_name, cxx_name
     .extern \cxx_name
 ENTRY \c_name
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r1, r2  // save all registers as basis for long jump context
     mov r1, r9                      @ pass Thread::Current
-    mov r2, sp                      @ pass SP
-    b   \cxx_name                   @ \cxx_name(Thread*, SP)
-    bkpt
+    b   \cxx_name                   @ \cxx_name(Thread*)
 END \c_name
 .endm
 
 .macro TWO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
     .extern \cxx_name
 ENTRY \c_name
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  r2, r3  // save all registers as basis for long jump context
     mov r2, r9                      @ pass Thread::Current
-    mov r3, sp                      @ pass SP
-    b   \cxx_name                   @ \cxx_name(Thread*, SP)
-    bkpt
+    b   \cxx_name                   @ \cxx_name(Thread*)
 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_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2  @ save callee saves in case of GC
+    ldr    r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
+    mov    r2, r9                        @ pass Thread::Current
+    bl     \entrypoint                   @ (uint32_t field_idx, const Method* referrer, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    \return
+END \name
+.endm
+
+.macro  TWO_ARG_REF_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3  @ save callee saves in case of GC
+    ldr    r2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
+    mov    r3, r9                        @ pass Thread::Current
+    bl     \entrypoint                   @ (field_idx, Object*, referrer, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    \return
+END \name
+.endm
+
+.macro  THREE_ARG_REF_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12  @ save callee saves in case of GC
+    ldr    r3, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
+    str    r9, [sp, #-16]!               @ expand the frame and pass Thread::Current
+    .pad #16
+    .cfi_adjust_cfa_offset 16
+    bl     \entrypoint                   @ (field_idx, Object*, new_val, referrer, Thread*)
+    add    sp, #16                       @ release out args
+    .cfi_adjust_cfa_offset -16
+    RESTORE_REFS_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.
@@ -254,8 +330,8 @@
 .macro INVOKE_TRAMPOLINE c_name, cxx_name
     .extern \cxx_name
 ENTRY \c_name
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
-    ldr    r2, [sp, #48]                  @ pass caller Method*
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3  @ save callee saves in case allocation triggers GC
+    ldr    r2, [sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE]  @ pass caller Method*
     mov    r3, r9                         @ pass Thread::Current
     mov    r12, sp
     str    r12, [sp, #-16]!               @ expand the frame and pass SP
@@ -265,7 +341,7 @@
     add    sp, #16                        @ strip the extra frame
     .cfi_adjust_cfa_offset -16
     mov    r12, r1                        @ save Method*->code_
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     cbz    r0, 1f                         @ did we find the target? if not go to exception delivery
     bx     r12                            @ tail call to target
 1:
@@ -282,60 +358,91 @@
 INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
     /*
-     * Quick invocation stub.
+     * Quick invocation stub internal.
      * On entry:
      *   r0 = method pointer
      *   r1 = argument array or NULL for no argument methods
      *   r2 = size of argument array in bytes
      *   r3 = (managed) thread pointer
      *   [sp] = JValue* result
-     *   [sp + 4] = shorty
+     *   [sp + 4] = result_in_float
+     *   [sp + 8] = core register argument array
+     *   [sp + 12] = fp register argument array
+     *  +-------------------------+
+     *  | uint32_t* fp_reg_args   |
+     *  | uint32_t* core_reg_args |
+     *  |   result_in_float       | <- Caller frame
+     *  |   Jvalue* result        |
+     *  +-------------------------+
+     *  |          lr             |
+     *  |          r11            |
+     *  |          r9             |
+     *  |          r4             | <- r11
+     *  +-------------------------+
+     *  | uint32_t out[n-1]       |
+     *  |    :      :             |        Outs
+     *  | uint32_t out[0]         |
+     *  | StackRef<ArtMethod>     | <- SP  value=null
+     *  +-------------------------+
      */
-ENTRY art_quick_invoke_stub
-    push   {r0, r4, r5, r9, r11, lr}       @ spill regs
-    .save  {r0, r4, r5, r9, r11, lr}
-    .pad #24
-    .cfi_adjust_cfa_offset 24
-    .cfi_rel_offset r0, 0
-    .cfi_rel_offset r4, 4
-    .cfi_rel_offset r5, 8
-    .cfi_rel_offset r9, 12
-    .cfi_rel_offset r11, 16
-    .cfi_rel_offset lr, 20
+ENTRY art_quick_invoke_stub_internal
+    push   {r4, r9, r11, lr}               @ spill regs
+    .save  {r4, r9, r11, lr}
+    .pad #16
+    .cfi_adjust_cfa_offset 16
+    .cfi_rel_offset r4, 0
+    .cfi_rel_offset r9, 4
+    .cfi_rel_offset r11, 8
+    .cfi_rel_offset lr, 12
     mov    r11, sp                         @ save the stack pointer
     .cfi_def_cfa_register r11
+
     mov    r9, r3                          @ move managed thread pointer into r9
+
+    add    r4, r2, #4                      @ create space for method pointer in frame
+    sub    r4, sp, r4                      @ reserve & align *stack* to 16 bytes: native calling
+    and    r4, #0xFFFFFFF0                 @ convention only aligns to 8B, so we have to ensure ART
+    mov    sp, r4                          @ 16B alignment ourselves.
+
+    mov    r4, r0                          @ save method*
+    add    r0, sp, #4                      @ pass stack pointer + method ptr as dest for memcpy
+    bl     memcpy                          @ memcpy (dest, src, bytes)
+    mov    ip, #0                          @ set ip to 0
+    str    ip, [sp]                        @ store NULL for method* at bottom of frame
+
+    ldr    ip, [r11, #28]                  @ load fp register argument array pointer
+    vldm   ip, {s0-s15}                    @ copy s0 - s15
+
+    ldr    ip, [r11, #24]                  @ load core register argument array pointer
+    mov    r0, r4                          @ restore method*
+    add    ip, ip, #4                      @ skip r0
+    ldm    ip, {r1-r3}                     @ copy r1 - r3
+
 #ifdef ARM_R4_SUSPEND_FLAG
     mov    r4, #SUSPEND_CHECK_INTERVAL     @ reset r4 to suspend check interval
 #endif
-    add    r5, r2, #4                      @ create space for method pointer in frame
 
-    sub    r5, sp, r5                      @ reserve & align *stack* to 16 bytes: native calling
-    and    r5, #0xFFFFFFF0                 @ convention only aligns to 8B, so we have to ensure ART
-    mov    sp, r5                          @ 16B alignment ourselves.
-
-    add    r0, sp, #4                      @ pass stack pointer + method ptr as dest for memcpy
-    bl     memcpy                          @ memcpy (dest, src, bytes)
-    ldr    r0, [r11]                       @ restore method*
-    ldr    r1, [sp, #4]                    @ copy arg value for r1
-    ldr    r2, [sp, #8]                    @ copy arg value for r2
-    ldr    r3, [sp, #12]                   @ copy arg value for r3
-    mov    ip, #0                          @ set ip to 0
-    str    ip, [sp]                        @ store NULL for method* at bottom of frame
-    ldr    ip, [r0, #METHOD_QUICK_CODE_OFFSET]  @ get pointer to the code
+    ldr    ip, [r0, #MIRROR_ART_METHOD_QUICK_CODE_OFFSET]  @ get pointer to the code
     blx    ip                              @ call the method
+
     mov    sp, r11                         @ restore the stack pointer
-    ldr    ip, [sp, #24]                   @ load the result pointer
-    strd   r0, [ip]                        @ store r0/r1 into result pointer
-    pop    {r0, r4, r5, r9, r11, lr}       @ restore spill regs
-    .cfi_restore r0
+    .cfi_def_cfa_register sp
+
+    ldr    r4, [sp, #20]                   @ load result_is_float
+    ldr    r9, [sp, #16]                   @ load the result pointer
+    cmp    r4, #0
+    ite    eq
+    strdeq r0, [r9]                        @ store r0/r1 into result pointer
+    vstrne d0, [r9]                        @ store s0-s1/d0 into result pointer
+
+    pop    {r4, r9, r11, lr}               @ restore spill regs
     .cfi_restore r4
-    .cfi_restore r5
     .cfi_restore r9
+    .cfi_restore r11
     .cfi_restore lr
-    .cfi_adjust_cfa_offset -24
+    .cfi_adjust_cfa_offset -16
     bx     lr
-END art_quick_invoke_stub
+END art_quick_invoke_stub_internal
 
     /*
      * On entry r0 is uint32_t* gprs_ and r1 is uint32_t* fprs_
@@ -355,16 +462,7 @@
      * Entry from managed code that calls artHandleFillArrayDataFromCode and delivers exception on
      * failure.
      */
-    .extern artHandleFillArrayDataFromCode
-ENTRY art_quick_handle_fill_data
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case exception allocation triggers GC
-    mov    r2, r9                          @ pass Thread::Current
-    mov    r3, sp                          @ pass SP
-    bl     artHandleFillArrayDataFromCode  @ (Array*, const DexFile::Payload*, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    RETURN_IF_RESULT_IS_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_handle_fill_data
+TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
 
     /*
      * Entry from managed code that calls artLockObjectFromCode, may block for GC. r0 holds the
@@ -375,10 +473,10 @@
     cbz    r0, .Lslow_lock
 .Lretry_lock:
     ldr    r2, [r9, #THREAD_ID_OFFSET]
-    ldrex  r1, [r0, #LOCK_WORD_OFFSET]
+    ldrex  r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     cbnz   r1, .Lnot_unlocked         @ already thin locked
     @ unlocked case - r2 holds thread id with count of 0
-    strex  r3, r2, [r0, #LOCK_WORD_OFFSET]
+    strex  r3, r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     cbnz   r3, .Lstrex_fail           @ store failed, retry
     dmb    ish                        @ full (LoadLoad|LoadStore) memory barrier
     bx lr
@@ -394,14 +492,13 @@
     add    r2, r1, #65536             @ increment count in lock word placing in r2 for storing
     lsr    r1, r2, 30                 @ if either of the top two bits are set, we overflowed.
     cbnz   r1, .Lslow_lock            @ if we overflow the count go slow path
-    str    r2, [r0, #LOCK_WORD_OFFSET] @ no need for strex as we hold the lock
+    str    r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ no need for strex as we hold the lock
     bx lr
 .Lslow_lock:
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case we block
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2  @ save callee saves in case we block
     mov    r1, r9                     @ pass Thread::Current
-    mov    r2, sp                     @ pass SP
-    bl     artLockObjectFromCode      @ (Object* obj, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    bl     artLockObjectFromCode      @ (Object* obj, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_lock_object
@@ -413,7 +510,7 @@
     .extern artUnlockObjectFromCode
 ENTRY art_quick_unlock_object
     cbz    r0, .Lslow_unlock
-    ldr    r1, [r0, #LOCK_WORD_OFFSET]
+    ldr    r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     lsr    r2, r1, 30
     cbnz   r2, .Lslow_unlock          @ if either of the top two bits are set, go slow path
     ldr    r2, [r9, #THREAD_ID_OFFSET]
@@ -424,18 +521,18 @@
     bpl    .Lrecursive_thin_unlock
     @ transition to unlocked, r3 holds 0
     dmb    ish                        @ full (LoadStore|StoreStore) memory barrier
-    str    r3, [r0, #LOCK_WORD_OFFSET]
+    str    r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     bx     lr
 .Lrecursive_thin_unlock:
     sub    r1, r1, #65536
-    str    r1, [r0, #LOCK_WORD_OFFSET]
+    str    r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     bx     lr
 .Lslow_unlock:
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case exception allocation triggers GC
+    @ save callee saves in case exception allocation triggers GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2
     mov    r1, r9                     @ pass Thread::Current
-    mov    r2, sp                     @ pass SP
-    bl     artUnlockObjectFromCode    @ (Object* obj, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    bl     artUnlockObjectFromCode    @ (Object* obj, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_unlock_object
@@ -466,10 +563,9 @@
     pop {r0-r1, lr}
     .cfi_restore r0
     .cfi_restore r1
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2, r3  // save all registers as basis for long jump context
     mov r2, r9                      @ pass Thread::Current
-    mov r3, sp                      @ pass SP
-    b   artThrowClassCastException  @ (Class*, Class*, Thread*, SP)
+    b   artThrowClassCastException  @ (Class*, Class*, Thread*)
     bkpt
 END art_quick_check_cast
 
@@ -486,7 +582,7 @@
 
     .hidden art_quick_aput_obj_with_bound_check
 ENTRY art_quick_aput_obj_with_bound_check
-    ldr r3, [r0, #ARRAY_LENGTH_OFFSET]
+    ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET]
     cmp r3, r1
     bhi art_quick_aput_obj
     mov r0, r1
@@ -497,20 +593,20 @@
     .hidden art_quick_aput_obj
 ENTRY art_quick_aput_obj
     cbz r2, .Ldo_aput_null
-    ldr r3, [r0, #CLASS_OFFSET]
-    ldr ip, [r2, #CLASS_OFFSET]
-    ldr r3, [r3, #CLASS_COMPONENT_TYPE_OFFSET]
+    ldr r3, [r0, #MIRROR_OBJECT_CLASS_OFFSET]
+    ldr ip, [r2, #MIRROR_OBJECT_CLASS_OFFSET]
+    ldr r3, [r3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET]
     cmp r3, ip  @ value's type == array's component type - trivial assignability
     bne .Lcheck_assignability
 .Ldo_aput:
-    add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
+    add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
     str r2, [r3, r1, lsl #2]
     ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
     lsr r0, r0, #7
     strb r3, [r3, r0]
     blx lr
 .Ldo_aput_null:
-    add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
+    add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
     str r2, [r3, r1, lsl #2]
     blx lr
 .Lcheck_assignability:
@@ -531,7 +627,7 @@
     .cfi_restore r2
     .cfi_restore lr
     .cfi_adjust_cfa_offset -16
-    add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
+    add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
     str r2, [r3, r1, lsl #2]
     ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
     lsr r0, r0, #7
@@ -544,12 +640,11 @@
     .cfi_restore r2
     .cfi_restore lr
     .cfi_adjust_cfa_offset -16
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r3, ip
     mov r1, r2
-    mov r2, r9                   @ pass Thread::Current
-    mov r3, sp                   @ pass SP
-    b artThrowArrayStoreException  @ (Class*, Class*, Thread*, SP)
-    bkpt                         @ unreached
+    mov r2, r9                     @ pass Thread::Current
+    b artThrowArrayStoreException  @ (Class*, Class*, Thread*)
+    bkpt                           @ unreached
 END art_quick_aput_obj
 
     /*
@@ -559,12 +654,11 @@
      */
     .extern artInitializeStaticStorageFromCode
 ENTRY art_quick_initialize_static_storage
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME           @ save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3    @ save callee saves in case of GC
     mov    r2, r9                              @ pass Thread::Current
-    mov    r3, sp                              @ pass SP
-    @ artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*, SP)
+    @ artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*)
     bl     artInitializeStaticStorageFromCode
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_initialize_static_storage
@@ -574,12 +668,11 @@
      */
     .extern artInitializeTypeFromCode
 ENTRY art_quick_initialize_type
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME           @ save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3    @ save callee saves in case of GC
     mov    r2, r9                              @ pass Thread::Current
-    mov    r3, sp                              @ pass SP
-    @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, SP)
+    @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*)
     bl     artInitializeTypeFromCode
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_initialize_type
@@ -590,46 +683,35 @@
      */
     .extern artInitializeTypeAndVerifyAccessFromCode
 ENTRY art_quick_initialize_type_and_verify_access
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME           @ save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3    @ save callee saves in case of GC
     mov    r2, r9                              @ pass Thread::Current
-    mov    r3, sp                              @ pass SP
-    @ artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Method* referrer, Thread*, SP)
+    @ artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Method* referrer, Thread*)
     bl     artInitializeTypeAndVerifyAccessFromCode
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO
     DELIVER_PENDING_EXCEPTION
 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.
      */
     .extern artGet64StaticFromCode
 ENTRY art_quick_get64_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r1, [sp, #32]                 @ pass referrer
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3  @ save callee saves in case of GC
+    ldr    r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
     mov    r2, r9                        @ pass Thread::Current
-    mov    r3, sp                        @ pass SP
-    bl     artGet64StaticFromCode        @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
+    bl     artGet64StaticFromCode        @ (uint32_t field_idx, const Method* referrer, Thread*)
     ldr    r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     cbnz   r2, 1f                        @ success if no exception pending
     bx     lr                            @ return on success
 1:
@@ -637,60 +719,25 @@
 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.
      */
     .extern artGet64InstanceFromCode
 ENTRY art_quick_get64_instance
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r2, [sp, #32]                 @ pass referrer
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  r2, r3  @ save callee saves in case of GC
+    ldr    r2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ 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     artGet64InstanceFromCode      @ (field_idx, Object*, referrer, Thread*, SP)
-    add    sp, #16                       @ strip the extra frame
-    .cfi_adjust_cfa_offset -16
+    bl     artGet64InstanceFromCode      @ (field_idx, Object*, referrer, Thread*)
     ldr    r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     cbnz   r2, 1f                        @ success if no exception pending
     bx     lr                            @ return on success
 1:
@@ -698,164 +745,62 @@
 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
      */
     .extern artSet64StaticFromCode
 ENTRY art_quick_set64_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12   @ save callee saves in case of GC
     mov    r3, r2                        @ pass one half of wide argument
     mov    r2, r1                        @ pass other half of wide argument
-    ldr    r1, [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     artSet64StaticFromCode        @ (field_idx, referrer, new_val, Thread*, SP)
+    ldr    r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
+    str    r9, [sp, #-16]!               @ expand the frame and pass Thread::Current
+    .pad #16
+    .cfi_adjust_cfa_offset 16
+    bl     artSet64StaticFromCode        @ (field_idx, referrer, new_val, Thread*)
     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
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME  @ TODO: we can clearly save an add here
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 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
-    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     artSet64InstanceFromCode      @ (field_idx, Object*, new_val, Thread*, SP)
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12, lr  @ save callee saves in case of GC
+    ldr    r12, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
+    str    r9, [sp, #-12]!               @ expand the frame and pass Thread::Current
+    .pad #12
+    .cfi_adjust_cfa_offset 12
+    str    r12, [sp, #-4]!               @ expand the frame and pass the referrer
+    .pad #4
+    .cfi_adjust_cfa_offset 4
+    bl     artSet64InstanceFromCode      @ (field_idx, Object*, new_val, Method* referrer, Thread*)
     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
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME  @ TODO: we can clearly save an add here
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 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
@@ -863,12 +808,11 @@
      */
     .extern artResolveStringFromCode
 ENTRY art_quick_resolve_string
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3  @ save callee saves in case of GC
     mov    r2, r9                     @ pass Thread::Current
-    mov    r3, sp                     @ pass SP
-    @ artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*, SP)
+    @ artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*)
     bl     artResolveStringFromCode
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_resolve_string
@@ -877,11 +821,10 @@
 .macro TWO_ARG_DOWNCALL name, entrypoint, return
     .extern \entrypoint
 ENTRY \name
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  r2, r3  @ save callee saves in case of GC
     mov    r2, r9                     @ pass Thread::Current
-    mov    r3, sp                     @ pass SP
-    bl     \entrypoint     @ (uint32_t type_idx, Method* method, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    bl     \entrypoint     @ (uint32_t type_idx, Method* method, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     \return
     DELIVER_PENDING_EXCEPTION
 END \name
@@ -891,17 +834,11 @@
 .macro THREE_ARG_DOWNCALL name, entrypoint, return
     .extern \entrypoint
 ENTRY \name
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  r3, r12  @ save callee saves in case of GC
     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
-    @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP)
+    @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*)
     bl     \entrypoint
-    add    sp, #16                    @ strip the extra frame
-    .cfi_adjust_cfa_offset -16
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     \return
     DELIVER_PENDING_EXCEPTION
 END \name
@@ -916,25 +853,24 @@
     .extern artTestSuspendFromCode
 ENTRY art_quick_test_suspend
 #ifdef ARM_R4_SUSPEND_FLAG
-    ldrh    r0, [rSELF, #THREAD_FLAGS_OFFSET]
+    ldrh   r0, [rSELF, #THREAD_FLAGS_OFFSET]
     mov    rSUSPEND, #SUSPEND_CHECK_INTERVAL  @ reset rSUSPEND to SUSPEND_CHECK_INTERVAL
     cbnz   r0, 1f                             @ check Thread::Current()->suspend_count_ == 0
     bx     lr                                 @ return if suspend_count_ == 0
 1:
 #endif
     mov    r0, rSELF
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME          @ save callee saves for stack crawl
-    mov    r1, sp
-    bl     artTestSuspendFromCode             @ (Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2   @ save callee saves for GC stack crawl
+    @ TODO: save FPRs to enable access in the debugger?
+    bl     artTestSuspendFromCode             @ (Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
 END art_quick_test_suspend
 
 ENTRY art_quick_implicit_suspend
     mov    r0, rSELF
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME          @ save callee saves for stack crawl
-    mov    r1, sp
-    bl     artTestSuspendFromCode             @ (Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2   @ save callee saves for stack crawl
+    bl     artTestSuspendFromCode             @ (Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
 END art_quick_implicit_suspend
 
     /*
@@ -944,19 +880,19 @@
      */
      .extern artQuickProxyInvokeHandler
 ENTRY art_quick_proxy_invoke_handler
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    str     r0, [sp, #0]           @ place proxy method at bottom of frame
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0
     mov     r2, r9                 @ pass Thread::Current
     mov     r3, sp                 @ pass SP
     blx     artQuickProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
     ldr     r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
-    add     sp, #16                @ skip r1-r3, 4 bytes padding.
-    .cfi_adjust_cfa_offset -16
+    // Tear down the callee-save frame. Skip arg registers.
+    add     sp, #(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+    .cfi_adjust_cfa_offset -(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     cbnz    r2, 1f                 @ success if no exception is pending
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    vmov    d0, r0, r1             @ store into fpr, for when it's a fpr return...
     bx      lr                     @ return on success
 1:
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
 END art_quick_proxy_invoke_handler
 
@@ -966,34 +902,33 @@
      */
 ENTRY art_quick_imt_conflict_trampoline
     ldr    r0, [sp, #0]            @ load caller Method*
-    ldr    r0, [r0, #METHOD_DEX_CACHE_METHODS_OFFSET]  @ load dex_cache_resolved_methods
-    add    r0, #OBJECT_ARRAY_DATA_OFFSET  @ get starting address of data
+    ldr    r0, [r0, #MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET]  @ load dex_cache_resolved_methods
+    add    r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET  @ get starting address of data
     ldr    r0, [r0, r12, lsl 2]    @ load the target method
     b art_quick_invoke_interface_trampoline
 END art_quick_imt_conflict_trampoline
 
     .extern artQuickResolutionTrampoline
 ENTRY art_quick_resolution_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3
     mov     r2, r9                 @ pass Thread::Current
     mov     r3, sp                 @ pass SP
     blx     artQuickResolutionTrampoline  @ (Method* called, receiver, Thread*, SP)
     cbz     r0, 1f                 @ is code pointer null? goto exception
     mov     r12, r0
     ldr  r0, [sp, #0]              @ load resolved method in r0
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     bx      r12                    @ tail-call into actual code
 1:
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
 END art_quick_resolution_trampoline
 
     /*
      * Called to do a generic JNI down-call
      */
-ENTRY_NO_HIDE art_quick_generic_jni_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    str r0, [sp, #0]  // Store native ArtMethod* to bottom of stack.
+ENTRY art_quick_generic_jni_trampoline
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0
 
     // Save rSELF
     mov r11, rSELF
@@ -1059,20 +994,13 @@
     ldr r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     cbnz r2, .Lexception_in_native
 
-    // Tear down the callee-save frame.
-    add  sp, #12                      @ rewind sp
-    // Do not pop r0 and r1, they contain the return value.
-    pop {r2-r3, r5-r8, r10-r11, lr}  @ 9 words of callee saves
-    .cfi_restore r2
-    .cfi_restore r3
-    .cfi_restore r5
-    .cfi_restore r6
-    .cfi_restore r7
-    .cfi_restore r8
-    .cfi_restore r10
-    .cfi_restore r11
-    .cfi_adjust_cfa_offset -48
+    // Tear down the callee-save frame. Skip arg registers.
+    add     sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+    .cfi_adjust_cfa_offset -(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
 
+    // store into fpr, for when it's a fpr return...
+    vmov d0, r0, r1
     bx lr      // ret
 
 .Lentry_error:
@@ -1080,23 +1008,25 @@
     .cfi_def_cfa_register sp
     mov r9, r11
 .Lexception_in_native:
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
 
 END art_quick_generic_jni_trampoline
 
     .extern artQuickToInterpreterBridge
-ENTRY_NO_HIDE art_quick_to_interpreter_bridge
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+ENTRY art_quick_to_interpreter_bridge
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r1, r2
     mov     r1, r9                 @ pass Thread::Current
     mov     r2, sp                 @ pass SP
     blx     artQuickToInterpreterBridge    @ (Method* method, Thread*, SP)
     ldr     r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
-    add     sp, #16                @ skip r1-r3, 4 bytes padding.
-    .cfi_adjust_cfa_offset -16
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    // Tear down the callee-save frame. Skip arg registers.
+    add     sp, #(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+    .cfi_adjust_cfa_offset -(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     cbnz    r2, 1f                 @ success if no exception is pending
-    bx    lr                       @ return on success
+    vmov    d0, r0, r1             @ store into fpr, for when it's a fpr return...
+    bx      lr                     @ return on success
 1:
     DELIVER_PENDING_EXCEPTION
 END art_quick_to_interpreter_bridge
@@ -1107,30 +1037,23 @@
     .extern artInstrumentationMethodEntryFromCode
     .extern artInstrumentationMethodExitFromCode
 ENTRY art_quick_instrumentation_entry
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    str   r0, [sp, #4]     @ preserve r0
-    mov   r12, sp          @ remember sp
-    str   lr, [sp, #-16]!  @ expand the frame and pass LR
-    .pad #16
-    .cfi_adjust_cfa_offset 16
-    .cfi_rel_offset lr, 0
+    @ Make stack crawlable and clobber r2 and r3 (post saving)
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3
+    @ preserve r0 (not normally an arg) knowing there is a spare slot in kRefsAndArgs.
+    str   r0, [sp, #4]
     mov   r2, r9         @ pass Thread::Current
-    mov   r3, r12        @ pass SP
-    blx   artInstrumentationMethodEntryFromCode  @ (Method*, Object*, Thread*, SP, LR)
-    add   sp, #16        @ remove out argument and padding from stack
-    .cfi_adjust_cfa_offset -16
+    mov   r3, lr         @ pass LR
+    blx   artInstrumentationMethodEntryFromCode  @ (Method*, Object*, Thread*, LR)
     mov   r12, r0        @ r12 holds reference to code
     ldr   r0, [sp, #4]   @ restore r0
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     blx   r12            @ call method with lr set to art_quick_instrumentation_exit
-END art_quick_instrumentation_entry
+@ Deliberate fall-through into art_quick_instrumentation_exit.
     .type art_quick_instrumentation_exit, #function
     .global art_quick_instrumentation_exit
 art_quick_instrumentation_exit:
-    .cfi_startproc
-    .fnstart
     mov   lr, #0         @ link register is to here, so clobber with 0 for later checks
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3  @ set up frame knowing r2 and r3 must be dead on exit
     mov   r12, sp        @ remember bottom of caller's frame
     push  {r0-r1}        @ save return value
     .save {r0-r1}
@@ -1157,7 +1080,7 @@
     add sp, #32          @ remove callee save frame
     .cfi_adjust_cfa_offset -32
     bx    r2             @ return
-END art_quick_instrumentation_exit
+END art_quick_instrumentation_entry
 
     /*
      * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
@@ -1165,10 +1088,9 @@
      */
     .extern artDeoptimize
 ENTRY art_quick_deoptimize
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1
     mov    r0, r9         @ Set up args.
-    mov    r1, sp
-    blx    artDeoptimize  @ artDeoptimize(Thread*, SP)
+    blx    artDeoptimize  @ artDeoptimize(Thread*)
 END art_quick_deoptimize
 
     /*
@@ -1291,9 +1213,9 @@
     .cfi_rel_offset r10, 4
     .cfi_rel_offset r11, 8
     .cfi_rel_offset lr, 12
-    ldr   r3, [r0, #STRING_COUNT_OFFSET]
-    ldr   r12, [r0, #STRING_OFFSET_OFFSET]
-    ldr   r0, [r0, #STRING_VALUE_OFFSET]
+    ldr   r3, [r0, #MIRROR_STRING_COUNT_OFFSET]
+    ldr   r12, [r0, #MIRROR_STRING_OFFSET_OFFSET]
+    ldr   r0, [r0, #MIRROR_STRING_VALUE_OFFSET]
 
     /* Clamp start to [0..count] */
     cmp   r2, #0
@@ -1304,7 +1226,7 @@
     movgt r2, r3
 
     /* Build a pointer to the start of string data */
-    add   r0, #STRING_DATA_OFFSET
+    add   r0, #MIRROR_CHAR_ARRAY_DATA_OFFSET
     add   r0, r0, r12, lsl #1
 
     /* Save a copy in r12 to later compute result */
@@ -1413,12 +1335,12 @@
     .cfi_rel_offset r12, 24
     .cfi_rel_offset lr, 28
 
-    ldr    r4, [r2, #STRING_OFFSET_OFFSET]
-    ldr    r9, [r1, #STRING_OFFSET_OFFSET]
-    ldr    r7, [r2, #STRING_COUNT_OFFSET]
-    ldr    r10, [r1, #STRING_COUNT_OFFSET]
-    ldr    r2, [r2, #STRING_VALUE_OFFSET]
-    ldr    r1, [r1, #STRING_VALUE_OFFSET]
+    ldr    r4, [r2, #MIRROR_STRING_OFFSET_OFFSET]
+    ldr    r9, [r1, #MIRROR_STRING_OFFSET_OFFSET]
+    ldr    r7, [r2, #MIRROR_STRING_COUNT_OFFSET]
+    ldr    r10, [r1, #MIRROR_STRING_COUNT_OFFSET]
+    ldr    r2, [r2, #MIRROR_STRING_VALUE_OFFSET]
+    ldr    r1, [r1, #MIRROR_STRING_VALUE_OFFSET]
 
     /*
      * At this point, we have:
@@ -1440,8 +1362,8 @@
       * Note: data pointers point to previous element so we can use pre-index
       * mode with base writeback.
       */
-     add   r2, #STRING_DATA_OFFSET-2   @ offset to contents[-1]
-     add   r1, #STRING_DATA_OFFSET-2   @ offset to contents[-1]
+     add   r2, #MIRROR_CHAR_ARRAY_DATA_OFFSET-2   @ offset to contents[-1]
+     add   r1, #MIRROR_CHAR_ARRAY_DATA_OFFSET-2   @ offset to contents[-1]
 
      /*
       * At this point we have:
@@ -1525,3 +1447,54 @@
 .Ldone:
     pop   {r4, r7-r12, pc}
 END art_quick_string_compareto
+
+    /* Assembly routines used to handle ABI differences. */
+
+    /* double fmod(double a, double b) */
+    .extern fmod
+ENTRY art_quick_fmod
+    push  {lr}
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset lr, 0
+    sub   sp, #4
+    .cfi_adjust_cfa_offset 4
+    vmov  r0, r1, d0
+    vmov  r2, r3, d1
+    bl    fmod
+    vmov  d0, r0, r1
+    add   sp, #4
+    .cfi_adjust_cfa_offset -4
+    pop   {pc}
+    .cfi_adjust_cfa_offset -4
+END art_quick_fmod
+
+    /* float fmodf(float a, float b) */
+     .extern fmodf
+ENTRY art_quick_fmodf
+    push  {lr}
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset lr, 0
+    sub   sp, #4
+    .cfi_adjust_cfa_offset 4
+    vmov  r0, r1, d0
+    bl    fmodf
+    vmov  s0, r0
+    add   sp, #4
+    .cfi_adjust_cfa_offset -4
+    pop   {pc}
+    .cfi_adjust_cfa_offset -4
+END art_quick_fmod
+
+    /* int64_t art_d2l(double d) */
+    .extern art_d2l
+ENTRY art_quick_d2l
+    vmov  r0, r1, d0
+    b     art_d2l
+END art_quick_d2l
+
+    /* int64_t art_f2l(float f) */
+    .extern art_f2l
+ENTRY art_quick_f2l
+    vmov  r0, s0
+    b     art_f2l
+END art_quick_f2l
diff --git a/runtime/arch/arm/quick_entrypoints_cc_arm.cc b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
new file mode 100644
index 0000000..e21e6c1
--- /dev/null
+++ b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
@@ -0,0 +1,110 @@
+/*
+ * 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 "mirror/art_method.h"
+#include "utils.h"  // For RoundUp().
+
+namespace art {
+
+// Assembly stub that does the final part of the up-call into Java.
+extern "C" void art_quick_invoke_stub_internal(mirror::ArtMethod*, uint32_t*, uint32_t,
+                                               Thread* self, JValue* result, uint32_t, uint32_t*,
+                                               uint32_t*);
+
+template <bool kIsStatic>
+static void quick_invoke_reg_setup(mirror::ArtMethod* method, uint32_t* args, uint32_t args_size,
+                                   Thread* self, JValue* result, const char* shorty) {
+  // Note: We do not follow aapcs ABI in quick code for both softfp and hardfp.
+  uint32_t core_reg_args[4];  // r0 ~ r3
+  uint32_t fp_reg_args[16];  // s0 ~ s15 (d0 ~ d7)
+  uint32_t gpr_index = 1;  // Index into core registers. Reserve r0 for mirror::ArtMethod*.
+  uint32_t fpr_index = 0;  // Index into float registers.
+  uint32_t fpr_double_index = 0;  // Index into float registers for doubles.
+  uint32_t arg_index = 0;  // Index into argument array.
+  const uint32_t result_in_float = kArm32QuickCodeUseSoftFloat ? 0 :
+      (shorty[0] == 'F' || shorty[0] == 'D') ? 1 : 0;
+
+  if (!kIsStatic) {
+    // Copy receiver for non-static methods.
+    core_reg_args[gpr_index++] = args[arg_index++];
+  }
+
+  for (uint32_t shorty_index = 1; shorty[shorty_index] != '\0'; ++shorty_index, ++arg_index) {
+    char arg_type = shorty[shorty_index];
+    if (kArm32QuickCodeUseSoftFloat) {
+      arg_type = (arg_type == 'D') ? 'J' : arg_type;  // Regard double as long.
+      arg_type = (arg_type == 'F') ? 'I' : arg_type;  // Regard float as int.
+    }
+    switch (arg_type) {
+      case 'D': {
+        // Copy double argument into fp_reg_args if there are still floating point reg arguments.
+        // Double should not overlap with float.
+        fpr_double_index = std::max(fpr_double_index, RoundUp(fpr_index, 2));
+        if (fpr_double_index < arraysize(fp_reg_args)) {
+          fp_reg_args[fpr_double_index++] = args[arg_index];
+          fp_reg_args[fpr_double_index++] = args[arg_index + 1];
+        }
+        ++arg_index;
+        break;
+      }
+      case 'F':
+        // Copy float argument into fp_reg_args if there are still floating point reg arguments.
+        // If fpr_index is odd then its pointing at a hole next to an existing float argument. If we
+        // encounter a float argument then pick it up from that hole. In the case fpr_index is even,
+        // ensure that we don't pick up an argument that overlaps with with a double from
+        // fpr_double_index. In either case, take care not to go beyond the maximum number of
+        // floating point arguments.
+        if (fpr_index % 2 == 0) {
+          fpr_index = std::max(fpr_double_index, fpr_index);
+        }
+        if (fpr_index < arraysize(fp_reg_args)) {
+          fp_reg_args[fpr_index++] = args[arg_index];
+        }
+        break;
+      case 'J':
+        if (gpr_index < arraysize(core_reg_args)) {
+          core_reg_args[gpr_index++] = args[arg_index];
+        }
+        ++arg_index;
+        FALLTHROUGH_INTENDED;  // Fall-through to take of the high part.
+      default:
+        if (gpr_index < arraysize(core_reg_args)) {
+          core_reg_args[gpr_index++] = args[arg_index];
+        }
+        break;
+    }
+  }
+
+  art_quick_invoke_stub_internal(method, args, args_size, self, result, result_in_float,
+      core_reg_args, fp_reg_args);
+}
+
+// Called by art::mirror::ArtMethod::Invoke to do entry into a non-static method.
+// TODO: migrate into an assembly implementation as with ARM64.
+extern "C" void art_quick_invoke_stub(mirror::ArtMethod* method, uint32_t* args, uint32_t args_size,
+                                      Thread* self, JValue* result, const char* shorty) {
+  quick_invoke_reg_setup<false>(method, args, args_size, self, result, shorty);
+}
+
+// Called by art::mirror::ArtMethod::Invoke to do entry into a static method.
+// TODO: migrate into an assembly implementation as with ARM64.
+extern "C" void art_quick_invoke_static_stub(mirror::ArtMethod* method, uint32_t* args,
+                                             uint32_t args_size, Thread* self, JValue* result,
+                                             const char* shorty) {
+  quick_invoke_reg_setup<true>(method, args, args_size, self, result, shorty);
+}
+
+}  // namespace art
diff --git a/runtime/arch/arm/quick_method_frame_info_arm.h b/runtime/arch/arm/quick_method_frame_info_arm.h
index 7595e94..c1f3fc2 100644
--- a/runtime/arch/arm/quick_method_frame_info_arm.h
+++ b/runtime/arch/arm/quick_method_frame_info_arm.h
@@ -25,6 +25,8 @@
 namespace art {
 namespace arm {
 
+static constexpr uint32_t kArmCalleeSaveAlwaysSpills =
+    (1 << art::arm::LR);
 static constexpr uint32_t kArmCalleeSaveRefSpills =
     (1 << art::arm::R5) | (1 << art::arm::R6)  | (1 << art::arm::R7) | (1 << art::arm::R8) |
     (1 << art::arm::R10) | (1 << art::arm::R11);
@@ -32,23 +34,30 @@
     (1 << art::arm::R1) | (1 << art::arm::R2) | (1 << art::arm::R3);
 static constexpr uint32_t kArmCalleeSaveAllSpills =
     (1 << art::arm::R4) | (1 << art::arm::R9);
-static constexpr uint32_t kArmCalleeSaveFpAllSpills =
+
+static constexpr uint32_t kArmCalleeSaveFpAlwaysSpills = 0;
+static constexpr uint32_t kArmCalleeSaveFpRefSpills = 0;
+static constexpr uint32_t kArmCalleeSaveFpArgSpills =
     (1 << art::arm::S0)  | (1 << art::arm::S1)  | (1 << art::arm::S2)  | (1 << art::arm::S3)  |
     (1 << art::arm::S4)  | (1 << art::arm::S5)  | (1 << art::arm::S6)  | (1 << art::arm::S7)  |
     (1 << art::arm::S8)  | (1 << art::arm::S9)  | (1 << art::arm::S10) | (1 << art::arm::S11) |
-    (1 << art::arm::S12) | (1 << art::arm::S13) | (1 << art::arm::S14) | (1 << art::arm::S15) |
+    (1 << art::arm::S12) | (1 << art::arm::S13) | (1 << art::arm::S14) | (1 << art::arm::S15);
+static constexpr uint32_t kArmCalleeSaveFpAllSpills =
     (1 << art::arm::S16) | (1 << art::arm::S17) | (1 << art::arm::S18) | (1 << art::arm::S19) |
     (1 << art::arm::S20) | (1 << art::arm::S21) | (1 << art::arm::S22) | (1 << art::arm::S23) |
     (1 << art::arm::S24) | (1 << art::arm::S25) | (1 << art::arm::S26) | (1 << art::arm::S27) |
     (1 << art::arm::S28) | (1 << art::arm::S29) | (1 << art::arm::S30) | (1 << art::arm::S31);
 
 constexpr uint32_t ArmCalleeSaveCoreSpills(Runtime::CalleeSaveType type) {
-  return kArmCalleeSaveRefSpills | (type == Runtime::kRefsAndArgs ? kArmCalleeSaveArgSpills : 0) |
-      (type == Runtime::kSaveAll ? kArmCalleeSaveAllSpills : 0) | (1 << art::arm::LR);
+  return kArmCalleeSaveAlwaysSpills | kArmCalleeSaveRefSpills |
+      (type == Runtime::kRefsAndArgs ? kArmCalleeSaveArgSpills : 0) |
+      (type == Runtime::kSaveAll ? kArmCalleeSaveAllSpills : 0);
 }
 
 constexpr uint32_t ArmCalleeSaveFpSpills(Runtime::CalleeSaveType type) {
-  return type == Runtime::kSaveAll ? kArmCalleeSaveFpAllSpills : 0;
+  return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills |
+      (type == Runtime::kRefsAndArgs ? kArmCalleeSaveFpArgSpills: 0) |
+      (type == Runtime::kSaveAll ? kArmCalleeSaveFpAllSpills : 0);
 }
 
 constexpr uint32_t ArmCalleeSaveFrameSize(Runtime::CalleeSaveType type) {
diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S
index fb49460..b3e9242 100644
--- a/runtime/arch/arm64/asm_support_arm64.S
+++ b/runtime/arch/arm64/asm_support_arm64.S
@@ -52,15 +52,6 @@
     .cfi_startproc
 .endm
 
-.macro ENTRY_NO_HIDE name
-    .type \name, #function
-    .global \name
-    /* Cache alignment for function entry */
-    .balign 16
-\name:
-    .cfi_startproc
-.endm
-
 .macro END name
     .cfi_endproc
     .size \name, .-\name
@@ -72,10 +63,4 @@
     END \name
 .endm
 
-.macro UNIMPLEMENTED_NO_HIDE name
-    ENTRY_NO_HIDE \name
-    brk 0
-    END \name
-.endm
-
 #endif  // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_
diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h
index a926449..989ecc6 100644
--- a/runtime/arch/arm64/asm_support_arm64.h
+++ b/runtime/arch/arm64/asm_support_arm64.h
@@ -19,30 +19,8 @@
 
 #include "asm_support.h"
 
-// Note: these callee save methods loads require read barriers.
-// Offset of field Runtime::callee_save_methods_[kSaveAll] verified in InitCpu
-#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0
-// Offset of field Runtime::callee_save_methods_[kRefsOnly] verified in InitCpu
-#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET 8
-// Offset of field Runtime::callee_save_methods_[kRefsAndArgs] verified in InitCpu
-#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 16
-
-// Offset of field Thread::suspend_count_
-#define THREAD_FLAGS_OFFSET 0
-// Offset of field Thread::card_table_
-#define THREAD_CARD_TABLE_OFFSET 120
-// Offset of field Thread::exception_
-#define THREAD_EXCEPTION_OFFSET 128
-// Offset of field Thread::thin_lock_thread_id_
-#define THREAD_ID_OFFSET 12
-
 #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176
 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 96
 #define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 224
 
-// Expected size of a heap reference
-#define HEAP_REFERENCE_SIZE 4
-// Expected size of a stack reference
-#define STACK_REFERENCE_SIZE 4
-
 #endif  // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc
index 3eb92c8..0a31480 100644
--- a/runtime/arch/arm64/context_arm64.cc
+++ b/runtime/arch/arm64/context_arm64.cc
@@ -19,11 +19,8 @@
 #include "context_arm64.h"
 
 #include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
 #include "quick/quick_method_frame_info.h"
-#include "stack.h"
-#include "thread.h"
-
+#include "utils.h"
 
 namespace art {
 namespace arm64 {
@@ -31,7 +28,7 @@
 static constexpr uint64_t gZero = 0;
 
 void Arm64Context::Reset() {
-  for (size_t i = 0; i < kNumberOfCoreRegisters; i++) {
+  for (size_t i = 0; i < kNumberOfXRegisters; i++) {
     gprs_[i] = nullptr;
   }
   for (size_t i = 0; i < kNumberOfDRegisters; i++) {
@@ -52,7 +49,7 @@
   if (spill_count > 0) {
     // Lowest number spill is farthest away, walk registers and fill into context.
     int j = 1;
-    for (size_t i = 0; i < kNumberOfCoreRegisters; i++) {
+    for (size_t i = 0; i < kNumberOfXRegisters; i++) {
       if (((frame_info.CoreSpillMask() >> i) & 1) != 0) {
         gprs_[i] = fr.CalleeSaveAddress(spill_count  - j, frame_info.FrameSizeInBytes());
         j++;
@@ -74,7 +71,8 @@
 }
 
 bool Arm64Context::SetGPR(uint32_t reg, uintptr_t value) {
-  DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+  DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters));
+  DCHECK_NE(reg, static_cast<uint32_t>(XZR));
   DCHECK_NE(gprs_[reg], &gZero);  // Can't overwrite this static value since they are never reset.
   if (gprs_[reg] != nullptr) {
     *gprs_[reg] = value;
@@ -146,11 +144,13 @@
 extern "C" void art_quick_do_long_jump(uint64_t*, uint64_t*);
 
 void Arm64Context::DoLongJump() {
-  uint64_t gprs[32];
+  uint64_t gprs[kNumberOfXRegisters];
   uint64_t fprs[kNumberOfDRegisters];
 
-  // Do not use kNumberOfCoreRegisters, as this is with the distinction of SP and XZR
-  for (size_t i = 0; i < 32; ++i) {
+  // The long jump routine called below expects to find the value for SP at index 31.
+  DCHECK_EQ(SP, 31);
+
+  for (size_t i = 0; i < kNumberOfXRegisters; ++i) {
     gprs[i] = gprs_[i] != nullptr ? *gprs_[i] : Arm64Context::kBadGprBase + i;
   }
   for (size_t i = 0; i < kNumberOfDRegisters; ++i) {
diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h
index 1f69869..d9a433b 100644
--- a/runtime/arch/arm64/context_arm64.h
+++ b/runtime/arch/arm64/context_arm64.h
@@ -34,7 +34,7 @@
 
   void Reset() OVERRIDE;
 
-  void FillCalleeSaves(const StackVisitor& fr) OVERRIDE;
+  void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetSP(uintptr_t new_sp) OVERRIDE {
     bool success = SetGPR(SP, new_sp);
@@ -47,12 +47,12 @@
   }
 
   uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
-    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters));
     return gprs_[reg];
   }
 
   bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
-    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters));
     if (gprs_[reg] == nullptr) {
       return false;
     } else {
@@ -82,7 +82,7 @@
 
  private:
   // Pointers to register locations, initialized to NULL or the specific registers below.
-  uintptr_t* gprs_[kNumberOfCoreRegisters];
+  uintptr_t* gprs_[kNumberOfXRegisters];
   uint64_t * fprs_[kNumberOfDRegisters];
   // Hold values for sp and pc if they are not located within a stack frame.
   uintptr_t sp_, pc_;
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 0c33d9c..2d26c03 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -17,97 +17,26 @@
 #include "entrypoints/interpreter/interpreter_entrypoints.h"
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/portable/portable_entrypoints.h"
+#include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/math_entrypoints.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "interpreter/interpreter.h"
 
 namespace art {
 
-// Interpreter entrypoints.
-extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh,
-                                                 const DexFile::CodeItem* code_item,
-                                                 ShadowFrame* shadow_frame, JValue* result);
-extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh,
-                                           const DexFile::CodeItem* code_item,
-                                           ShadowFrame* shadow_frame, JValue* result);
-
-// Portable entrypoints.
-extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
-
 // Cast entrypoints.
 extern "C" uint32_t art_quick_assignable_from_code(const mirror::Class* klass,
                                             const mirror::Class* ref_class);
-extern "C" void art_quick_check_cast(void*, void*);
-
-// DexCache entrypoints.
-extern "C" void* art_quick_initialize_static_storage(uint32_t, void*);
-extern "C" void* art_quick_initialize_type(uint32_t, void*);
-extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, void*);
-extern "C" void* art_quick_resolve_string(void*, uint32_t);
-
-// Field entrypoints.
-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" 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*);
-extern "C" int64_t art_quick_get64_static(uint32_t);
-extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
-extern "C" void* art_quick_get_obj_static(uint32_t);
-
-// Array entrypoints.
-extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
-extern "C" void art_quick_handle_fill_data(void*, void*);
-
-// Lock entrypoints.
-extern "C" void art_quick_lock_object(void*);
-extern "C" void art_quick_unlock_object(void*);
 
 // Single-precision FP arithmetics.
 extern "C" float art_quick_fmodf(float a, float b);          // REM_FLOAT[_2ADDR]
 
 // Double-precision FP arithmetics.
-extern "C" double art_quick_fmod(double a, double b);         // REM_DOUBLE[_2ADDR]
+extern "C" double art_quick_fmod(double a, double b);        // REM_DOUBLE[_2ADDR]
 
-// Memcpy
-extern "C" void* art_quick_memcpy(void* __restrict, const void* __restrict, size_t);
-
-// Intrinsic entrypoints.
-extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
-extern "C" int32_t art_quick_string_compareto(void*, void*);
-
-// Invoke entrypoints.
-extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
-extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
-
-// Thread entrypoints.
-extern "C" void art_quick_test_suspend();
-
-// Throw entrypoints.
-extern "C" void art_quick_deliver_exception(void*);
-extern "C" void art_quick_throw_array_bounds(int32_t index, int32_t limit);
-extern "C" void art_quick_throw_div_zero();
-extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
-extern "C" void art_quick_throw_null_pointer_exception();
-extern "C" void art_quick_throw_stack_overflow(void*);
-
-extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
-
-// Generic JNI downcall
-extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
                      PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
@@ -136,15 +65,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/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index 687d232..c914d85 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -37,7 +37,8 @@
 
 namespace art {
 
-void FaultManager::HandleNestedSignal(int sig, siginfo_t* info, void* context) {
+void FaultManager::HandleNestedSignal(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                      void* context) {
   // To match the case used in ARM we return directly to the longjmp function
   // rather than through a trivial assembly language stub.
 
@@ -51,7 +52,7 @@
   sc->pc = reinterpret_cast<uintptr_t>(longjmp);
 }
 
-void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
+void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo ATTRIBUTE_UNUSED, void* context,
                                              mirror::ArtMethod** out_method,
                                              uintptr_t* out_return_pc, uintptr_t* out_sp) {
   struct ucontext *uc = reinterpret_cast<struct ucontext *>(context);
@@ -82,7 +83,8 @@
   *out_return_pc = sc->pc + 4;
 }
 
-bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
+bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                void* context) {
   // The code that looks for the catch location needs to know the value of the
   // PC at the point of call.  For Null checks we insert a GC map that is immediately after
   // the load/store instruction that might cause the fault.
@@ -105,7 +107,8 @@
 // The offset from r18 is Thread::ThreadSuspendTriggerOffset().
 // To check for a suspend check, we examine the instructions that caused
 // the fault (at PC-4 and PC).
-bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) {
+bool SuspensionHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                               void* context) {
   // These are the instructions to check for.  The first one is the ldr x0,[r18,#xxx]
   // where xxx is the offset of the suspend trigger.
   uint32_t checkinst1 = 0xf9400240 | (Thread::ThreadSuspendTriggerOffset<8>().Int32Value() << 7);
@@ -155,7 +158,8 @@
   return false;
 }
 
-bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) {
+bool StackOverflowHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                  void* context) {
   struct ucontext *uc = reinterpret_cast<struct ucontext *>(context);
   struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
   VLOG(signals) << "stack overflow handler with sp at " << std::hex << &uc;
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/runtime/arch/arm64/portable_entrypoints_arm64.S
index 41711b5..9e2c030 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/runtime/arch/arm64/portable_entrypoints_arm64.S
@@ -25,4 +25,6 @@
 
 UNIMPLEMENTED art_portable_resolution_trampoline
 
-UNIMPLEMENTED_NO_HIDE art_portable_to_interpreter_bridge
+UNIMPLEMENTED art_portable_to_interpreter_bridge
+
+UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 2a19e27..147d434 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -79,13 +79,16 @@
 
     // Loads appropriate callee-save-method
     str xIP0, [sp]    // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs]
+    // Place sp in Thread::Current()->top_quick_frame.
+    mov xIP0, sp
+    str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
 .endm
 
     /*
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kRefsOnly).
      */
-.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
     adrp xIP0, :got:_ZN3art7Runtime9instance_E
     ldr xIP0, [xIP0, #:got_lo12:_ZN3art7Runtime9instance_E]
 
@@ -133,11 +136,14 @@
     mov xETR, xSELF
 
     // Loads appropriate callee-save-method
-    str xIP0, [sp]    // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs]
+    str xIP0, [sp]    // Store ArtMethod* Runtime::callee_save_methods_[kRefsOnly]
+    // Place sp in Thread::Current()->top_quick_frame.
+    mov xIP0, sp
+    str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
 .endm
 
 // TODO: Probably no need to restore registers preserved by aapcs64.
-.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     // Restore xSELF.
     mov xSELF, xETR
 
@@ -170,7 +176,7 @@
     .cfi_adjust_cfa_offset -96
 .endm
 
-.macro POP_REF_ONLY_CALLEE_SAVE_FRAME
+.macro POP_REFS_ONLY_CALLEE_SAVE_FRAME
     // Restore xSELF as it might be scratched.
     mov xSELF, xETR
     // ETR
@@ -181,13 +187,13 @@
     .cfi_adjust_cfa_offset -96
 .endm
 
-.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     ret
 .endm
 
 
-.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
     sub sp, sp, #224
     .cfi_adjust_cfa_offset 224
 
@@ -251,7 +257,7 @@
      *
      * TODO This is probably too conservative - saving FP & LR.
      */
-.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     adrp xIP0, :got:_ZN3art7Runtime9instance_E
     ldr xIP0, [xIP0, #:got_lo12:_ZN3art7Runtime9instance_E]
 
@@ -260,15 +266,26 @@
 
     // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs]  .
     THIS_LOAD_REQUIRES_READ_BARRIER
-    ldr xIP0, [xIP0, RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ]
+    ldr xIP0, [xIP0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ]
 
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
 
     str xIP0, [sp]    // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs]
+    // Place sp in Thread::Current()->top_quick_frame.
+    mov xIP0, sp
+    str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
+.endm
+
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_X0
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
+    str x0, [sp, #0]  // Store ArtMethod* to bottom of stack.
+    // Place sp in Thread::Current()->top_quick_frame.
+    mov xIP0, sp
+    str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
 .endm
 
 // TODO: Probably no need to restore registers preserved by aapcs64.
-.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+.macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     // Restore xSELF.
     mov xSELF, xETR
 
@@ -340,10 +357,9 @@
 .macro DELIVER_PENDING_EXCEPTION
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     mov x0, xSELF
-    mov x1, sp
 
     // Point of no return.
-    b artDeliverPendingExceptionFromCode  // artDeliverPendingExceptionFromCode(Thread*, SP)
+    b artDeliverPendingExceptionFromCode  // artDeliverPendingExceptionFromCode(Thread*)
     brk 0  // Unreached
 .endm
 
@@ -376,8 +392,7 @@
 ENTRY \c_name
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
     mov x0, xSELF                     // pass Thread::Current
-    mov x1, sp                        // pass SP
-    b   \cxx_name                     // \cxx_name(Thread*, SP)
+    b   \cxx_name                     // \cxx_name(Thread*)
 END \c_name
 .endm
 
@@ -386,8 +401,7 @@
 ENTRY \c_name
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context.
     mov x1, xSELF                     // pass Thread::Current.
-    mov x2, sp                        // pass SP.
-    b   \cxx_name                     // \cxx_name(arg, Thread*, SP).
+    b   \cxx_name                     // \cxx_name(arg, Thread*).
     brk 0
 END \c_name
 .endm
@@ -397,8 +411,7 @@
 ENTRY \c_name
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
     mov x2, xSELF                     // pass Thread::Current
-    mov x3, sp                        // pass SP
-    b   \cxx_name                     // \cxx_name(arg1, arg2, Thread*, SP)
+    b   \cxx_name                     // \cxx_name(arg1, arg2, Thread*)
     brk 0
 END \c_name
 .endm
@@ -458,7 +471,7 @@
 .macro INVOKE_TRAMPOLINE c_name, cxx_name
     .extern \cxx_name
 ENTRY \c_name
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  // save callee saves in case allocation triggers GC
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME  // save callee saves in case allocation triggers GC
     // Helper signature is always
     // (method_idx, *this_object, *caller_method, *self, sp)
 
@@ -467,7 +480,7 @@
     mov    x4, sp
     bl     \cxx_name                      // (method_idx, this, caller, Thread*, SP)
     mov    xIP0, x1                       // save Method*->code_
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     cbz    x0, 1f                         // did we find the target? if not go to exception delivery
     br     xIP0                           // tail call to target
 1:
@@ -551,7 +564,7 @@
 .macro INVOKE_STUB_CALL_AND_RETURN
 
     // load method-> METHOD_QUICK_CODE_OFFSET
-    ldr x9, [x0 , #METHOD_QUICK_CODE_OFFSET]
+    ldr x9, [x0 , #MIRROR_ART_METHOD_QUICK_CODE_OFFSET]
     // Branch to method.
     blr x9
 
@@ -937,21 +950,6 @@
 END art_quick_do_long_jump
 
     /*
-     * Entry from managed code that calls artHandleFillArrayDataFromCode and delivers exception on
-     * failure.
-     */
-    .extern artHandleFillArrayDataFromCode
-ENTRY art_quick_handle_fill_data
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // Save callee saves in case exception allocation triggers GC.
-    mov    x2, xSELF                       // Pass Thread::Current.
-    mov    x3, sp                          // Pass SP.
-    bl     artHandleFillArrayDataFromCode  // (Array*, const DexFile::Payload*, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    RETURN_IF_RESULT_IS_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_handle_fill_data
-
-    /*
      * Entry from managed code that calls artLockObjectFromCode, may block for GC. x0 holds the
      * possibly null object to lock.
      *
@@ -960,7 +958,7 @@
     .extern artLockObjectFromCode
 ENTRY art_quick_lock_object
     cbz    w0, .Lslow_lock
-    add    x4, x0, #LOCK_WORD_OFFSET  // exclusive load/store had no immediate anymore
+    add    x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET  // exclusive load/store has no immediate anymore
 .Lretry_lock:
     ldr    w2, [xSELF, #THREAD_ID_OFFSET] // TODO: Can the thread ID really change during the loop?
     ldxr   w1, [x4]
@@ -981,14 +979,13 @@
     add    w2, w1, #65536             // increment count in lock word placing in w2 for storing
     lsr    w1, w2, 30                 // if either of the top two bits are set, we overflowed.
     cbnz   w1, .Lslow_lock            // if we overflow the count go slow path
-    str    w2, [x0, #LOCK_WORD_OFFSET]// no need for stxr as we hold the lock
+    str    w2, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]  // no need for stxr as we hold the lock
     ret
 .Lslow_lock:
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case we block
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case we block
     mov    x1, xSELF                  // pass Thread::Current
-    mov    x2, sp                     // pass SP
-    bl     artLockObjectFromCode      // (Object* obj, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    bl     artLockObjectFromCode      // (Object* obj, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_lock_object
 
@@ -1001,7 +998,7 @@
     .extern artUnlockObjectFromCode
 ENTRY art_quick_unlock_object
     cbz    x0, .Lslow_unlock
-    ldr    w1, [x0, #LOCK_WORD_OFFSET]
+    ldr    w1, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     lsr    w2, w1, 30
     cbnz   w2, .Lslow_unlock          // if either of the top two bits are set, go slow path
     ldr    w2, [xSELF, #THREAD_ID_OFFSET]
@@ -1012,18 +1009,17 @@
     bpl    .Lrecursive_thin_unlock
     // transition to unlocked, w3 holds 0
     dmb    ish                        // full (LoadStore|StoreStore) memory barrier
-    str    w3, [x0, #LOCK_WORD_OFFSET]
+    str    w3, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     ret
 .Lrecursive_thin_unlock:
     sub    w1, w1, #65536
-    str    w1, [x0, #LOCK_WORD_OFFSET]
+    str    w1, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     ret
 .Lslow_unlock:
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case exception allocation triggers GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case exception allocation triggers GC
     mov    x1, xSELF                  // pass Thread::Current
-    mov    x2, sp                     // pass SP
-    bl     artUnlockObjectFromCode    // (Object* obj, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    bl     artUnlockObjectFromCode    // (Object* obj, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_unlock_object
 
@@ -1073,8 +1069,7 @@
 
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
     mov x2, xSELF                     // pass Thread::Current
-    mov x3, sp                        // pass SP
-    b artThrowClassCastException      // (Class*, Class*, Thread*, SP)
+    b artThrowClassCastException      // (Class*, Class*, Thread*)
     brk 0                             // We should not return here...
 END art_quick_check_cast
 
@@ -1097,7 +1092,7 @@
 END art_quick_aput_obj_with_null_and_bound_check
 
 ENTRY art_quick_aput_obj_with_bound_check
-    ldr w3, [x0, #ARRAY_LENGTH_OFFSET]
+    ldr w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET]
     cmp w3, w1
     bhi art_quick_aput_obj
     mov x0, x1
@@ -1107,16 +1102,16 @@
 
 ENTRY art_quick_aput_obj
     cbz x2, .Ldo_aput_null
-    ldr w3, [x0, #CLASS_OFFSET]                          // Heap reference = 32b
+    ldr w3, [x0, #MIRROR_OBJECT_CLASS_OFFSET]            // Heap reference = 32b
                                                          // This also zero-extends to x3
-    ldr w4, [x2, #CLASS_OFFSET]                          // Heap reference = 32b
+    ldr w4, [x2, #MIRROR_OBJECT_CLASS_OFFSET]            // Heap reference = 32b
                                                          // This also zero-extends to x4
-    ldr w3, [x3, #CLASS_COMPONENT_TYPE_OFFSET]           // Heap reference = 32b
+    ldr w3, [x3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET]    // Heap reference = 32b
                                                          // This also zero-extends to x3
     cmp w3, w4  // value's type == array's component type - trivial assignability
     bne .Lcheck_assignability
 .Ldo_aput:
-    add x3, x0, #OBJECT_ARRAY_DATA_OFFSET
+    add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
                                                          // "Compress" = do nothing
     str w2, [x3, x1, lsl #2]                             // Heap reference = 32b
     ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET]
@@ -1124,7 +1119,7 @@
     strb w3, [x3, x0]
     ret
 .Ldo_aput_null:
-    add x3, x0, #OBJECT_ARRAY_DATA_OFFSET
+    add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
                                                          // "Compress" = do nothing
     str w2, [x3, x1, lsl #2]                             // Heap reference = 32b
     ret
@@ -1161,7 +1156,7 @@
     add sp, sp, #48
     .cfi_adjust_cfa_offset -48
 
-    add x3, x0, #OBJECT_ARRAY_DATA_OFFSET
+    add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
                                                           // "Compress" = do nothing
     str w2, [x3, x1, lsl #2]                              // Heap reference = 32b
     ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET]
@@ -1183,8 +1178,7 @@
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     mov x1, x2                    // Pass value.
     mov x2, xSELF                 // Pass Thread::Current.
-    mov x3, sp                    // Pass SP.
-    b artThrowArrayStoreException // (Object*, Object*, Thread*, SP).
+    b artThrowArrayStoreException // (Object*, Object*, Thread*).
     brk 0                         // Unreached.
 END art_quick_aput_obj
 
@@ -1192,11 +1186,10 @@
 .macro TWO_ARG_DOWNCALL name, entrypoint, return
     .extern \entrypoint
 ENTRY \name
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
     mov    x2, xSELF                  // pass Thread::Current
-    mov    x3, sp                     // pass SP
-    bl     \entrypoint                // (uint32_t type_idx, Method* method, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    bl     \entrypoint                // (uint32_t type_idx, Method* method, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     \return
     DELIVER_PENDING_EXCEPTION
 END \name
@@ -1206,11 +1199,10 @@
 .macro THREE_ARG_DOWNCALL name, entrypoint, return
     .extern \entrypoint
 ENTRY \name
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
     mov    x3, xSELF                  // pass Thread::Current
-    mov    x4, sp                     // pass SP
     bl     \entrypoint
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     \return
     DELIVER_PENDING_EXCEPTION
 END \name
@@ -1220,12 +1212,11 @@
 .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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
     ldr    w1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer
     mov    x2, xSELF                  // pass Thread::Current
-    mov    x3, sp                     // pass SP
     bl     \entrypoint                // (uint32_t type_idx, Method* method, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     \return
 END \name
 .endm
@@ -1233,12 +1224,11 @@
 .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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
     ldr    w2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer
     mov    x3, xSELF                  // pass Thread::Current
-    mov    x4, sp                     // pass SP
     bl     \entrypoint
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     \return
 END \name
 .endm
@@ -1246,17 +1236,22 @@
 .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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
     ldr    w3, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer
     mov    x4, xSELF                  // pass Thread::Current
-    mov    x5, sp                     // pass SP
     bl     \entrypoint
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     \return
 END \name
 .endm
 
     /*
+     * Entry from managed code that calls artHandleFillArrayDataFromCode and delivers exception on
+     * failure.
+     */
+TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
+
+    /*
      * Entry from managed code when uninitialized static storage, this stub will run the class
      * initializer and deliver the exception on error. On success the static storage base is
      * returned.
@@ -1266,32 +1261,43 @@
 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_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
 
 // This is separated out as the argument order is different.
     .extern artSet64StaticFromCode
 ENTRY art_quick_set64_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
     mov    x3, x1                     // Store value
     ldr    w1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer
     mov    x2, x3                     // Put value param
     mov    x3, xSELF                  // pass Thread::Current
-    mov    x4, sp                     // pass SP
     bl     artSet64StaticFromCode
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_set64_static
 
@@ -1317,18 +1323,16 @@
     ret                                       // return if flags == 0
 .Lneed_suspend:
     mov    x0, xSELF
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME          // save callee saves for stack crawl
-    mov    x1, sp
-    bl     artTestSuspendFromCode             // (Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME          // save callee saves for stack crawl
+    bl     artTestSuspendFromCode             // (Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
 END art_quick_test_suspend
 
 ENTRY art_quick_implicit_suspend
     mov    x0, xSELF
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME          // save callee saves for stack crawl
-    mov    x1, sp
-    bl     artTestSuspendFromCode             // (Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME          // save callee saves for stack crawl
+    bl     artTestSuspendFromCode             // (Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
 END art_quick_implicit_suspend
 
      /*
@@ -1338,19 +1342,18 @@
      */
      .extern artQuickProxyInvokeHandler
 ENTRY art_quick_proxy_invoke_handler
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    str     x0, [sp, #0]                // place proxy method at bottom of frame
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_X0
     mov     x2, xSELF                   // pass Thread::Current
     mov     x3, sp                      // pass SP
     bl      artQuickProxyInvokeHandler  // (Method* proxy method, receiver, Thread*, SP)
     // Use xETR as xSELF might be scratched by native function above.
     ldr     x2, [xETR, THREAD_EXCEPTION_OFFSET]
     cbnz    x2, .Lexception_in_proxy    // success if no exception is pending
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME // Restore frame
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // Restore frame
     fmov    d0, x0                      // Store result in d0 in case it was float or double
     ret                                 // return on success
 .Lexception_in_proxy:
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
 END art_quick_proxy_invoke_handler
 
@@ -1360,24 +1363,24 @@
      */
 ENTRY art_quick_imt_conflict_trampoline
     ldr    w0, [sp, #0]                                // load caller Method*
-    ldr    w0, [x0, #METHOD_DEX_CACHE_METHODS_OFFSET]  // load dex_cache_resolved_methods
-    add    x0, x0, #OBJECT_ARRAY_DATA_OFFSET           // get starting address of data
+    ldr    w0, [x0, #MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET]  // load dex_cache_resolved_methods
+    add    x0, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET    // get starting address of data
     ldr    w0, [x0, xIP1, lsl 2]                       // load the target method
     b art_quick_invoke_interface_trampoline
 END art_quick_imt_conflict_trampoline
 
 ENTRY art_quick_resolution_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     mov x2, xSELF
     mov x3, sp
     bl artQuickResolutionTrampoline  // (called, receiver, Thread*, SP)
     cbz x0, 1f
     mov xIP0, x0            // Remember returned code pointer in xIP0.
     ldr w0, [sp, #0]        // artQuickResolutionTrampoline puts called method in *SP.
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     br xIP0
 1:
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
 END art_quick_resolution_trampoline
 
@@ -1435,9 +1438,8 @@
     /*
      * Called to do a generic JNI down-call
      */
-ENTRY_NO_HIDE art_quick_generic_jni_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
-    str x0, [sp, #0]  // Store native ArtMethod* to bottom of stack.
+ENTRY art_quick_generic_jni_trampoline
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_X0
 
     // Save SP , so we can have static CFI info.
     mov x28, sp
@@ -1510,7 +1512,7 @@
     cbnz x1, .Lexception_in_native
 
     // Tear down the callee-save frame.
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
 
     // store into fpr, for when it's a fpr return...
     fmov d0, x0
@@ -1520,7 +1522,7 @@
     mov sp, x28
     .cfi_def_cfa_register sp
 .Lexception_in_native:
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
 
 END art_quick_generic_jni_trampoline
@@ -1531,8 +1533,8 @@
  * x0 = method being called/to bridge to.
  * x1..x7, d0..d7 = arguments to that method.
  */
-ENTRY_NO_HIDE art_quick_to_interpreter_bridge
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // Set up frame and save arguments.
+ENTRY art_quick_to_interpreter_bridge
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME   // Set up frame and save arguments.
 
     //  x0 will contain mirror::ArtMethod* method.
     mov x1, xSELF                          // How to get Thread::Current() ???
@@ -1542,7 +1544,7 @@
     //                                      mirror::ArtMethod** sp)
     bl   artQuickToInterpreterBridge
 
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME  // TODO: no need to restore arguments in this case.
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME  // TODO: no need to restore arguments in this case.
 
     fmov d0, x0
 
@@ -1555,19 +1557,18 @@
 //
     .extern artInstrumentationMethodEntryFromCode
 ENTRY art_quick_instrumentation_entry
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
 
     mov   x20, x0             // Preserve method reference in a callee-save.
 
     mov   x2, xSELF
-    mov   x3, sp
-    mov   x4, xLR
-    bl    artInstrumentationMethodEntryFromCode  // (Method*, Object*, Thread*, SP, LR)
+    mov   x3, xLR
+    bl    artInstrumentationMethodEntryFromCode  // (Method*, Object*, Thread*, LR)
 
     mov   xIP0, x0            // x0 = result of call.
     mov   x0, x20             // Reload method reference.
 
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME  // Note: will restore xSELF
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME  // Note: will restore xSELF
     adr   xLR, art_quick_instrumentation_exit
     br    xIP0                // Tail-call method with lr set to art_quick_instrumentation_exit.
 END art_quick_instrumentation_entry
@@ -1576,7 +1577,7 @@
 ENTRY art_quick_instrumentation_exit
     mov   xLR, #0             // Clobber LR for later checks.
 
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
 
     // We need to save x0 and d0. We could use a callee-save from SETUP_REF_ONLY, but then
     // we would need to fully restore it. As there are a lot of callee-save registers, it seems
@@ -1599,7 +1600,7 @@
     ldr   x0, [sp], 16        // Restore integer result, and drop stack area.
     .cfi_adjust_cfa_offset 16
 
-    POP_REF_ONLY_CALLEE_SAVE_FRAME
+    POP_REFS_ONLY_CALLEE_SAVE_FRAME
 
     br    xIP0                // Tail-call out.
 END art_quick_instrumentation_exit
@@ -1612,8 +1613,7 @@
 ENTRY art_quick_deoptimize
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     mov    x0, xSELF          // Pass thread.
-    mov    x1, sp             // Pass SP.
-    bl     artDeoptimize      // artDeoptimize(Thread*, SP)
+    bl     artDeoptimize      // artDeoptimize(Thread*)
     brk 0
 END art_quick_deoptimize
 
@@ -1628,9 +1628,9 @@
      *    w2:   Starting offset in string data
      */
 ENTRY art_quick_indexof
-    ldr   w3, [x0, #STRING_COUNT_OFFSET]
-    ldr   w4, [x0, #STRING_OFFSET_OFFSET]
-    ldr   w0, [x0, #STRING_VALUE_OFFSET] // x0 ?
+    ldr   w3, [x0, #MIRROR_STRING_COUNT_OFFSET]
+    ldr   w4, [x0, #MIRROR_STRING_OFFSET_OFFSET]
+    ldr   w0, [x0, #MIRROR_STRING_VALUE_OFFSET] // x0 ?
 
     /* Clamp start to [0..count] */
     cmp   w2, #0
@@ -1639,7 +1639,7 @@
     csel  w2, w3, w2, gt
 
     /* Build a pointer to the start of the string data */
-    add   x0, x0, #STRING_DATA_OFFSET
+    add   x0, x0, #MIRROR_CHAR_ARRAY_DATA_OFFSET
     add   x0, x0, x4, lsl #1
 
     /* Save a copy to compute result */
@@ -1733,12 +1733,12 @@
     ret
 1:                        // Different string objects.
 
-    ldr    w6, [x2, #STRING_OFFSET_OFFSET]
-    ldr    w5, [x1, #STRING_OFFSET_OFFSET]
-    ldr    w4, [x2, #STRING_COUNT_OFFSET]
-    ldr    w3, [x1, #STRING_COUNT_OFFSET]
-    ldr    w2, [x2, #STRING_VALUE_OFFSET]
-    ldr    w1, [x1, #STRING_VALUE_OFFSET]
+    ldr    w6, [x2, #MIRROR_STRING_OFFSET_OFFSET]
+    ldr    w5, [x1, #MIRROR_STRING_OFFSET_OFFSET]
+    ldr    w4, [x2, #MIRROR_STRING_COUNT_OFFSET]
+    ldr    w3, [x1, #MIRROR_STRING_COUNT_OFFSET]
+    ldr    w2, [x2, #MIRROR_STRING_VALUE_OFFSET]
+    ldr    w1, [x1, #MIRROR_STRING_VALUE_OFFSET]
 
     /*
      * Now:           CharArray*    Offset   Count
@@ -1758,8 +1758,8 @@
     add x1, x1, w5, sxtw #1
 
     // Add offset in CharArray to array.
-    add x2, x2, #STRING_DATA_OFFSET
-    add x1, x1, #STRING_DATA_OFFSET
+    add x2, x2, #MIRROR_CHAR_ARRAY_DATA_OFFSET
+    add x1, x1, #MIRROR_CHAR_ARRAY_DATA_OFFSET
 
     // TODO: Tune this value.
     // Check for long string, do memcmp16 for them.
diff --git a/runtime/arch/arm64/quick_method_frame_info_arm64.h b/runtime/arch/arm64/quick_method_frame_info_arm64.h
index 15c6c07..0e1e32b 100644
--- a/runtime/arch/arm64/quick_method_frame_info_arm64.h
+++ b/runtime/arch/arm64/quick_method_frame_info_arm64.h
@@ -54,7 +54,7 @@
     (1 << art::arm64::D0) | (1 << art::arm64::D1) | (1 << art::arm64::D2) |
     (1 << art::arm64::D3) | (1 << art::arm64::D4) | (1 << art::arm64::D5) |
     (1 << art::arm64::D6) | (1 << art::arm64::D7);
-static constexpr uint32_t kArm64FpAllSpills =
+static constexpr uint32_t kArm64CalleeSaveFpAllSpills =
     (1 << art::arm64::D8)  | (1 << art::arm64::D9)  | (1 << art::arm64::D10) |
     (1 << art::arm64::D11)  | (1 << art::arm64::D12)  | (1 << art::arm64::D13) |
     (1 << art::arm64::D14)  | (1 << art::arm64::D15);
@@ -68,7 +68,7 @@
 constexpr uint32_t Arm64CalleeSaveFpSpills(Runtime::CalleeSaveType type) {
   return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills |
       (type == Runtime::kRefsAndArgs ? kArm64CalleeSaveFpArgSpills: 0) |
-      (type == Runtime::kSaveAll ? kArm64FpAllSpills : 0);
+      (type == Runtime::kSaveAll ? kArm64CalleeSaveFpAllSpills : 0);
 }
 
 constexpr uint32_t Arm64CalleeSaveFrameSize(Runtime::CalleeSaveType type) {
diff --git a/runtime/arch/arm64/registers_arm64.cc b/runtime/arch/arm64/registers_arm64.cc
index 87901e3..ea4383a 100644
--- a/runtime/arch/arm64/registers_arm64.cc
+++ b/runtime/arch/arm64/registers_arm64.cc
@@ -32,11 +32,11 @@
   "w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "w9",
   "w10", "w11", "w12", "w13", "w14", "w15", "w16", "w17", "w18", "w19",
   "w20", "w21", "w22", "w23", "w24", "w25", "w26", "w27", "w28", "w29",
-  "w30", "wsp", "wxr"
+  "w30", "wsp", "wzr"
 };
 
-std::ostream& operator<<(std::ostream& os, const Register& rhs) {
-  if (rhs >= X0 && rhs <= XZR) {
+std::ostream& operator<<(std::ostream& os, const XRegister& rhs) {
+  if (rhs >= X0 && rhs < kNumberOfXRegisters) {
     os << kRegisterNames[rhs];
   } else {
     os << "XRegister[" << static_cast<int>(rhs) << "]";
@@ -45,7 +45,7 @@
 }
 
 std::ostream& operator<<(std::ostream& os, const WRegister& rhs) {
-  if (rhs >= W0 && rhs <= WZR) {
+  if (rhs >= W0 && rhs < kNumberOfWRegisters) {
     os << kWRegisterNames[rhs];
   } else {
     os << "WRegister[" << static_cast<int>(rhs) << "]";
diff --git a/runtime/arch/arm64/registers_arm64.h b/runtime/arch/arm64/registers_arm64.h
index 9ccab70..51ae184 100644
--- a/runtime/arch/arm64/registers_arm64.h
+++ b/runtime/arch/arm64/registers_arm64.h
@@ -23,7 +23,7 @@
 namespace arm64 {
 
 // Values for GP XRegisters - 64bit registers.
-enum Register {
+enum XRegister {
   X0  =  0,
   X1  =  1,
   X2  =  2,
@@ -55,20 +55,20 @@
   X28 = 28,
   X29 = 29,
   X30 = 30,
-  X31 = 31,
-  TR  = 18,     // ART Thread Register - Managed Runtime (Caller Saved Reg)
-  ETR = 21,     // ART Thread Register - External Calls  (Callee Saved Reg)
-  IP0 = 16,     // Used as scratch by VIXL.
-  IP1 = 17,     // Used as scratch by ART JNI Assembler.
-  FP  = 29,
-  LR  = 30,
-  SP  = 31,     // SP is X31 and overlaps with XRZ but we encode it as a
-                // special register, due to the different instruction semantics.
-  XZR = 32,
-  kNumberOfCoreRegisters = 33,
+  SP  = 31,      // SP and XZR are encoded in instructions using the register
+  XZR = 32,      // code `31`, the context deciding which is used. We use a
+                 // different enum value to distinguish between the two.
+  kNumberOfXRegisters = 33,
+  // Aliases.
+  TR  = X18,     // ART Thread Register - Managed Runtime (Caller Saved Reg)
+  ETR = X21,     // ART Thread Register - External Calls  (Callee Saved Reg)
+  IP0 = X16,     // Used as scratch by VIXL.
+  IP1 = X17,     // Used as scratch by ART JNI Assembler.
+  FP  = X29,
+  LR  = X30,
   kNoRegister = -1,
 };
-std::ostream& operator<<(std::ostream& os, const Register& rhs);
+std::ostream& operator<<(std::ostream& os, const XRegister& rhs);
 
 // Values for GP WRegisters - 32bit registers.
 enum WRegister {
@@ -103,9 +103,9 @@
   W28 = 28,
   W29 = 29,
   W30 = 30,
-  W31 = 31,
-  WZR = 31,
-  kNumberOfWRegisters = 32,
+  WSP = 31,
+  WZR = 32,
+  kNumberOfWRegisters = 33,
   kNoWRegister = -1,
 };
 std::ostream& operator<<(std::ostream& os, const WRegister& rhs);
diff --git a/runtime/arch/memcmp16.cc b/runtime/arch/memcmp16.cc
index 5a3e73e..813df2f 100644
--- a/runtime/arch/memcmp16.cc
+++ b/runtime/arch/memcmp16.cc
@@ -19,6 +19,7 @@
 // This linked against by assembly stubs, only.
 #pragma GCC diagnostic ignored "-Wunused-function"
 
+int32_t memcmp16_generic_static(const uint16_t* s0, const uint16_t* s1, size_t count);
 int32_t memcmp16_generic_static(const uint16_t* s0, const uint16_t* s1, size_t count) {
   for (size_t i = 0; i < count; i++) {
     if (s0[i] != s1[i]) {
diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S
index d8ec9cd..0d18f1a 100644
--- a/runtime/arch/mips/asm_support_mips.S
+++ b/runtime/arch/mips/asm_support_mips.S
@@ -26,15 +26,31 @@
 // Register holding Thread::Current().
 #define rSELF $s1
 
-
-    /* Cache alignment for function entry */
+     // Declare a function called name, sets up $gp.
 .macro ENTRY name
     .type \name, %function
     .global \name
+    // Cache alignment for function entry.
     .balign 16
 \name:
     .cfi_startproc
-     /* Ensure we get a sane starting CFA. */
+     // Ensure we get a sane starting CFA.
+    .cfi_def_cfa $sp,0
+    // Load $gp. We expect that ".set noreorder" is in effect.
+    .cpload $t9
+    // Declare a local convenience label to be branched to when $gp is already set up.
+.L\name\()_gp_set:
+.endm
+
+     // Declare a function called name, doesn't set up $gp.
+.macro ENTRY_NO_GP name
+    .type \name, %function
+    .global \name
+    // Cache alignment for function entry.
+    .balign 16
+\name:
+    .cfi_startproc
+     // Ensure we get a sane starting CFA.
     .cfi_def_cfa $sp,0
 .endm
 
@@ -43,11 +59,6 @@
     .size \name, .-\name
 .endm
 
-    /* Generates $gp for function calls */
-.macro GENERATE_GLOBAL_POINTER
-    .cpload $t9
-.endm
-
 .macro UNIMPLEMENTED name
     ENTRY \name
     break
diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h
index 6add93b..5bece18 100644
--- a/runtime/arch/mips/asm_support_mips.h
+++ b/runtime/arch/mips/asm_support_mips.h
@@ -19,18 +19,8 @@
 
 #include "asm_support.h"
 
-// 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 120
-// Offset of field Thread::tlsPtr_.exception verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 124
-
 #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64
 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64
 #define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 64
 
-// Expected size of a heap reference
-#define HEAP_REFERENCE_SIZE 4
-
 #endif  // ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_H_
diff --git a/runtime/arch/mips/context_mips.cc b/runtime/arch/mips/context_mips.cc
index 789dbbb..e1f6c06 100644
--- a/runtime/arch/mips/context_mips.cc
+++ b/runtime/arch/mips/context_mips.cc
@@ -17,9 +17,8 @@
 #include "context_mips.h"
 
 #include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
 #include "quick/quick_method_frame_info.h"
-#include "stack.h"
+#include "utils.h"
 
 namespace art {
 namespace mips {
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index d3e7d5e..e86aa1c 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -14,63 +14,23 @@
  * limitations under the License.
  */
 
+#include "atomic.h"
 #include "entrypoints/interpreter/interpreter_entrypoints.h"
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/math_entrypoints.h"
-#include "atomic.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "interpreter/interpreter.h"
 
 namespace art {
 
-// Interpreter entrypoints.
-extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh,
-                                                 const DexFile::CodeItem* code_item,
-                                                 ShadowFrame* shadow_frame, JValue* result);
-extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh,
-                                           const DexFile::CodeItem* code_item,
-                                           ShadowFrame* shadow_frame, JValue* result);
-
-// Portable entrypoints.
-extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
-
 // Cast entrypoints.
 extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
                                             const mirror::Class* ref_class);
-extern "C" void art_quick_check_cast(void*, void*);
-
-// DexCache entrypoints.
-extern "C" void* art_quick_initialize_static_storage(uint32_t, void*);
-extern "C" void* art_quick_initialize_type(uint32_t, void*);
-extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, void*);
-extern "C" void* art_quick_resolve_string(void*, uint32_t);
-
-// Field entrypoints.
-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" 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*);
-extern "C" int64_t art_quick_get64_static(uint32_t);
-extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
-extern "C" void* art_quick_get_obj_static(uint32_t);
-
-// Array entrypoints.
-extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
-extern "C" void art_quick_handle_fill_data(void*, void*);
-
-// Lock entrypoints.
-extern "C" void art_quick_lock_object(void*);
-extern "C" void art_quick_unlock_object(void*);
 
 // Math entrypoints.
 extern int32_t CmpgDouble(double a, double b);
@@ -98,39 +58,6 @@
 // Long long arithmetics - REM_LONG[_2ADDR] and DIV_LONG[_2ADDR]
 extern "C" int64_t __divdi3(int64_t, int64_t);
 extern "C" int64_t __moddi3(int64_t, int64_t);
-extern "C" uint64_t art_quick_shl_long(uint64_t, uint32_t);
-extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t);
-extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t);
-
-// Intrinsic entrypoints.
-extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
-extern "C" int32_t art_quick_string_compareto(void*, void*);
-
-// Invoke entrypoints.
-extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
-extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
-
-// Thread entrypoints.
-extern "C" void art_quick_test_suspend();
-
-// Throw entrypoints.
-extern "C" void art_quick_deliver_exception(void*);
-extern "C" void art_quick_throw_array_bounds(int32_t index, int32_t limit);
-extern "C" void art_quick_throw_div_zero();
-extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
-extern "C" void art_quick_throw_null_pointer_exception();
-extern "C" void art_quick_throw_stack_overflow(void*);
-
-// Generic JNI downcall
-extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
-
-extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
                      PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
@@ -159,15 +86,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/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc
index aa6d68a..c9949d4 100644
--- a/runtime/arch/mips/fault_handler_mips.cc
+++ b/runtime/arch/mips/fault_handler_mips.cc
@@ -29,23 +29,29 @@
 
 namespace art {
 
-void FaultManager::HandleNestedSignal(int sig, siginfo_t* info, void* context) {
+void FaultManager::HandleNestedSignal(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                      void* context ATTRIBUTE_UNUSED) {
 }
 
-void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
-                                             mirror::ArtMethod** out_method,
-                                             uintptr_t* out_return_pc, uintptr_t* out_sp) {
+void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo ATTRIBUTE_UNUSED,
+                                             void* context ATTRIBUTE_UNUSED,
+                                             mirror::ArtMethod** out_method ATTRIBUTE_UNUSED,
+                                             uintptr_t* out_return_pc ATTRIBUTE_UNUSED,
+                                             uintptr_t* out_sp ATTRIBUTE_UNUSED) {
 }
 
-bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
+bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                void* context ATTRIBUTE_UNUSED) {
   return false;
 }
 
-bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) {
+bool SuspensionHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                               void* context ATTRIBUTE_UNUSED) {
   return false;
 }
 
-bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) {
+bool StackOverflowHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                  void* context ATTRIBUTE_UNUSED) {
   return false;
 }
 }       // namespace art
diff --git a/runtime/arch/mips/jni_entrypoints_mips.S b/runtime/arch/mips/jni_entrypoints_mips.S
index e5f4a79..9a79467 100644
--- a/runtime/arch/mips/jni_entrypoints_mips.S
+++ b/runtime/arch/mips/jni_entrypoints_mips.S
@@ -24,7 +24,6 @@
      */
     .extern artFindNativeMethod
 ENTRY art_jni_dlsym_lookup_stub
-    GENERATE_GLOBAL_POINTER
     addiu $sp, $sp, -32          # leave room for $a0, $a1, $a2, $a3, and $ra
     .cfi_adjust_cfa_offset 32
     sw     $ra, 16($sp)
diff --git a/runtime/arch/mips/memcmp16_mips.S b/runtime/arch/mips/memcmp16_mips.S
index 0196edc..aef81af 100644
--- a/runtime/arch/mips/memcmp16_mips.S
+++ b/runtime/arch/mips/memcmp16_mips.S
@@ -20,7 +20,7 @@
 #include "asm_support_mips.S"
 
 // u4 __memcmp16(const u2*, const u2*, size_t);
-ENTRY __memcmp16
+ENTRY_NO_GP __memcmp16
   li  $t0,0
   li  $t1,0
   beqz  $a2,done   /* 0 length string */
diff --git a/runtime/arch/mips/portable_entrypoints_mips.S b/runtime/arch/mips/portable_entrypoints_mips.S
index 7545ce0..d7e7a8e 100644
--- a/runtime/arch/mips/portable_entrypoints_mips.S
+++ b/runtime/arch/mips/portable_entrypoints_mips.S
@@ -21,7 +21,6 @@
 
     .extern artPortableProxyInvokeHandler
 ENTRY art_portable_proxy_invoke_handler
-    GENERATE_GLOBAL_POINTER
     # Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
     # TODO: just save the registers that are needed in artPortableProxyInvokeHandler.
     addiu  $sp, $sp, -64
@@ -72,7 +71,6 @@
      *   [sp + 20] = result type char
      */
 ENTRY art_portable_invoke_stub
-    GENERATE_GLOBAL_POINTER
     sw    $a0, 0($sp)           # save out a0
     addiu $sp, $sp, -16         # spill s0, s1, fp, ra
     .cfi_adjust_cfa_offset 16
@@ -87,7 +85,7 @@
     move  $fp, $sp              # save sp in fp
     .cfi_def_cfa_register 30
     move  $s1, $a3              # move managed thread pointer into s1
-    addiu $s0, $zero, SUSPEND_CHECK_INTERVAL  # reset s0 to suspend check interval
+    addiu $s0, $zero, SUSPEND_CHECK_INTERVAL  # reset s0 to suspend check interval. TODO: unused?
     addiu $t0, $a2, 16          # create space for method pointer in frame
     srl   $t0, $t0, 3           # shift the frame size right 3
     sll   $t0, $t0, 3           # shift the frame size left 3 to align to 16 bytes
@@ -100,7 +98,7 @@
     lw    $a1, 4($sp)           # copy arg value for a1
     lw    $a2, 8($sp)           # copy arg value for a2
     lw    $a3, 12($sp)          # copy arg value for a3
-    lw    $t9, METHOD_PORTABLE_CODE_OFFSET($a0)  # get pointer to the code
+    lw    $t9, MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET($a0)  # get pointer to the code
     jalr  $t9                   # call the method
     sw    $zero, 0($sp)         # store NULL for method* at bottom of frame
     move  $sp, $fp              # restore the stack
@@ -131,3 +129,4 @@
 
 UNIMPLEMENTED art_portable_resolution_trampoline
 UNIMPLEMENTED art_portable_to_interpreter_bridge
+UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 8786222..e878ef7 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -29,7 +29,8 @@
     /*
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kSaveAll)
-     * callee-save: $s0-$s8 + $gp + $ra, 11 total + 1 word padding + 4 open words for args
+     * Callee-save: $s0-$s8 + $gp + $ra, 11 total + 1 word padding + 4 open words for args
+     * Clobbers $t0 and $gp
      */
 .macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     addiu  $sp, $sp, -64
@@ -63,6 +64,13 @@
     sw     $s0, 20($sp)
     .cfi_rel_offset 16, 20
     # 1 word for alignment, 4 open words for args $a0-$a3, bottom will hold Method*
+
+    lw $t0, %got(_ZN3art7Runtime9instance_E)($gp)
+    lw $t0, 0($t0)
+    THIS_LOAD_REQUIRES_READ_BARRIER
+    lw $t0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($t0)
+    sw $t0, 0($sp)                                # Place Method* at bottom of stack.
+    sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
 .endm
 
     /*
@@ -71,7 +79,7 @@
      * Does not include rSUSPEND or rSELF
      * callee-save: $s2-$s8 + $gp + $ra, 9 total + 3 words padding + 4 open words for args
      */
-.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
     addiu  $sp, $sp, -64
     .cfi_adjust_cfa_offset 64
 
@@ -99,9 +107,16 @@
     sw     $s2, 28($sp)
     .cfi_rel_offset 18, 28
     # 3 words for alignment and extra args, 4 open words for args $a0-$a3, bottom will hold Method*
+
+    lw $t0, %got(_ZN3art7Runtime9instance_E)($gp)
+    lw $t0, 0($t0)
+    THIS_LOAD_REQUIRES_READ_BARRIER
+    lw $t0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($t0)
+    sw $t0, 0($sp)                                # Place Method* at bottom of stack.
+    sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
 .endm
 
-.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     lw     $ra, 60($sp)
     .cfi_restore 31
     lw     $s8, 56($sp)
@@ -124,7 +139,7 @@
     .cfi_adjust_cfa_offset -64
 .endm
 
-.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
     lw     $ra, 60($sp)
     .cfi_restore 31
     lw     $s8, 56($sp)
@@ -153,7 +168,7 @@
      * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes non-moving GC.
      * callee-save: $a1-$a3, $s2-$s8 + $gp + $ra, 12 total + 3 words padding + method*
      */
-.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     addiu  $sp, $sp, -64
     .cfi_adjust_cfa_offset 64
 
@@ -187,9 +202,16 @@
     sw     $a1, 4($sp)
     .cfi_rel_offset 5, 4
     # bottom will hold Method*
+
+    lw $t0, %got(_ZN3art7Runtime9instance_E)($gp)
+    lw $t0, 0($t0)
+    THIS_LOAD_REQUIRES_READ_BARRIER
+    lw $t0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($t0)
+    sw $t0, 0($sp)                                # Place Method* at bottom of stack.
+    sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
 .endm
 
-.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+.macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     lw     $ra, 60($sp)
     .cfi_restore 31
     lw     $s8, 56($sp)
@@ -224,15 +246,14 @@
      */
 .macro DELIVER_PENDING_EXCEPTION
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME     # save callee saves for throw
-    move    $a0, rSELF                   # pass Thread::Current
     la      $t9, artDeliverPendingExceptionFromCode
-    jr      $t9                          # artDeliverPendingExceptionFromCode(Thread*, $sp)
-    move    $a1, $sp                     # pass $sp
+    jr      $t9                          # artDeliverPendingExceptionFromCode(Thread*)
+    move    $a0, rSELF                   # pass Thread::Current
 .endm
 
 .macro RETURN_IF_NO_EXCEPTION
     lw     $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     bnez   $t0, 1f                       # success if no exception is pending
     nop
     jr     $ra
@@ -242,7 +263,7 @@
 .endm
 
 .macro RETURN_IF_ZERO
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     bnez   $v0, 1f                       # success?
     nop
     jr     $ra                           # return on success
@@ -252,7 +273,7 @@
 .endm
 
 .macro RETURN_IF_RESULT_IS_NON_ZERO
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     beqz   $v0, 1f                       # success?
     nop
     jr     $ra                           # return on success
@@ -342,12 +363,10 @@
      * the bottom of the thread. On entry r0 holds Throwable*
      */
 ENTRY art_quick_deliver_exception
-    GENERATE_GLOBAL_POINTER
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    move $a1, rSELF                 # pass Thread::Current
     la   $t9, artDeliverExceptionFromCode
-    jr   $t9                        # artDeliverExceptionFromCode(Throwable*, Thread*, $sp)
-    move $a2, $sp                   # pass $sp
+    jr   $t9                        # artDeliverExceptionFromCode(Throwable*, Thread*)
+    move $a1, rSELF                 # pass Thread::Current
 END art_quick_deliver_exception
 
     /*
@@ -355,13 +374,10 @@
      */
     .extern artThrowNullPointerExceptionFromCode
 ENTRY art_quick_throw_null_pointer_exception
-    GENERATE_GLOBAL_POINTER
-.Lart_quick_throw_null_pointer_exception_gp_set:
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    move $a0, rSELF                 # pass Thread::Current
     la   $t9, artThrowNullPointerExceptionFromCode
-    jr   $t9                        # artThrowNullPointerExceptionFromCode(Thread*, $sp)
-    move $a1, $sp                   # pass $sp
+    jr   $t9                        # artThrowNullPointerExceptionFromCode(Thread*)
+    move $a0, rSELF                 # pass Thread::Current
 END art_quick_throw_null_pointer_exception
 
     /*
@@ -369,12 +385,10 @@
      */
     .extern artThrowDivZeroFromCode
 ENTRY art_quick_throw_div_zero
-    GENERATE_GLOBAL_POINTER
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    move $a0, rSELF                 # pass Thread::Current
     la   $t9, artThrowDivZeroFromCode
-    jr   $t9                        # artThrowDivZeroFromCode(Thread*, $sp)
-    move $a1, $sp                   # pass $sp
+    jr   $t9                        # artThrowDivZeroFromCode(Thread*)
+    move $a0, rSELF                 # pass Thread::Current
 END art_quick_throw_div_zero
 
     /*
@@ -382,13 +396,10 @@
      */
     .extern artThrowArrayBoundsFromCode
 ENTRY art_quick_throw_array_bounds
-    GENERATE_GLOBAL_POINTER
-.Lart_quick_throw_array_bounds_gp_set:
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    move $a2, rSELF                 # pass Thread::Current
     la   $t9, artThrowArrayBoundsFromCode
-    jr   $t9                        # artThrowArrayBoundsFromCode(index, limit, Thread*, $sp)
-    move $a3, $sp                   # pass $sp
+    jr   $t9                        # artThrowArrayBoundsFromCode(index, limit, Thread*)
+    move $a2, rSELF                 # pass Thread::Current
 END art_quick_throw_array_bounds
 
     /*
@@ -396,12 +407,10 @@
      */
     .extern artThrowStackOverflowFromCode
 ENTRY art_quick_throw_stack_overflow
-    GENERATE_GLOBAL_POINTER
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    move $a0, rSELF                 # pass Thread::Current
     la   $t9, artThrowStackOverflowFromCode
-    jr   $t9                        # artThrowStackOverflowFromCode(Thread*, $sp)
-    move $a1, $sp                   # pass $sp
+    jr   $t9                        # artThrowStackOverflowFromCode(Thread*)
+    move $a0, rSELF                 # pass Thread::Current
 END art_quick_throw_stack_overflow
 
     /*
@@ -409,12 +418,10 @@
      */
     .extern artThrowNoSuchMethodFromCode
 ENTRY art_quick_throw_no_such_method
-    GENERATE_GLOBAL_POINTER
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    move $a1, rSELF                 # pass Thread::Current
     la   $t9, artThrowNoSuchMethodFromCode
-    jr   $t9                        # artThrowNoSuchMethodFromCode(method_idx, Thread*, $sp)
-    move $a2, $sp                   # pass $sp
+    jr   $t9                        # artThrowNoSuchMethodFromCode(method_idx, Thread*)
+    move $a1, rSELF                 # pass Thread::Current
 END art_quick_throw_no_such_method
 
     /*
@@ -436,9 +443,8 @@
 .macro INVOKE_TRAMPOLINE c_name, cxx_name
     .extern \cxx_name
 ENTRY \c_name
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  # save callee saves in case allocation triggers GC
-    lw    $a2, 64($sp)                    # pass caller Method*
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME  # save callee saves in case allocation triggers GC
+    lw    $a2, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE($sp)                    # pass caller Method*
     move  $t0, $sp                        # save $sp
     addiu $sp, $sp, -32                   # make space for extra args
     .cfi_adjust_cfa_offset 32
@@ -450,7 +456,7 @@
     .cfi_adjust_cfa_offset -32
     move  $a0, $v0                        # save target Method*
     move  $t9, $v1                        # save $v0->code_
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     beqz  $v0, 1f
     nop
     jr    $t9
@@ -479,7 +485,6 @@
      *   [sp + 20] = shorty
      */
 ENTRY art_quick_invoke_stub
-    GENERATE_GLOBAL_POINTER
     sw    $a0, 0($sp)           # save out a0
     addiu $sp, $sp, -16         # spill s0, s1, fp, ra
     .cfi_adjust_cfa_offset 16
@@ -507,7 +512,7 @@
     lw    $a1, 4($sp)           # copy arg value for a1
     lw    $a2, 8($sp)           # copy arg value for a2
     lw    $a3, 12($sp)          # copy arg value for a3
-    lw    $t9, METHOD_QUICK_CODE_OFFSET($a0)  # get pointer to the code
+    lw    $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET($a0)  # get pointer to the code
     jalr  $t9                   # call the method
     sw    $zero, 0($sp)         # store NULL for method* at bottom of frame
     move  $sp, $fp              # restore the stack
@@ -543,11 +548,10 @@
      */
     .extern artHandleFillArrayDataFromCode
 ENTRY art_quick_handle_fill_data
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case exception allocation triggers GC
-    move    $a2, rSELF                         # pass Thread::Current
-    jal     artHandleFillArrayDataFromCode     # (Array*, const DexFile::Payload*, Thread*, $sp)
-    move    $a3, $sp                           # pass $sp
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case exception allocation triggers GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)                   # pass referrer's Method*
+    jal    artHandleFillArrayDataFromCode # (payload offset, Array*, method, Thread*)
+    move   $a3, rSELF                     # pass Thread::Current
     RETURN_IF_ZERO
 END art_quick_handle_fill_data
 
@@ -556,13 +560,11 @@
      */
     .extern artLockObjectFromCode
 ENTRY art_quick_lock_object
-    GENERATE_GLOBAL_POINTER
     beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
     nop
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME      # save callee saves in case we block
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME      # save callee saves in case we block
+    jal     artLockObjectFromCode         # (Object* obj, Thread*)
     move    $a1, rSELF                    # pass Thread::Current
-    jal     artLockObjectFromCode         # (Object* obj, Thread*, $sp)
-    move    $a2, $sp                      # pass $sp
     RETURN_IF_ZERO
 END art_quick_lock_object
 
@@ -571,13 +573,11 @@
      */
     .extern artUnlockObjectFromCode
 ENTRY art_quick_unlock_object
-    GENERATE_GLOBAL_POINTER
     beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
     nop
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case exception allocation triggers GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case exception allocation triggers GC
+    jal     artUnlockObjectFromCode   # (Object* obj, Thread*)
     move    $a1, rSELF                # pass Thread::Current
-    jal     artUnlockObjectFromCode   # (Object* obj, Thread*, $sp)
-    move    $a2, $sp                  # pass $sp
     RETURN_IF_ZERO
 END art_quick_unlock_object
 
@@ -586,7 +586,6 @@
      */
     .extern artThrowClassCastException
 ENTRY art_quick_check_cast
-    GENERATE_GLOBAL_POINTER
     addiu  $sp, $sp, -16
     .cfi_adjust_cfa_offset 16
     sw     $ra, 12($sp)
@@ -608,10 +607,9 @@
     addiu  $sp, $sp, 16
     .cfi_adjust_cfa_offset -16
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    move $a2, rSELF                 # pass Thread::Current
     la   $t9, artThrowClassCastException
-    jr   $t9                        # artThrowClassCastException (Class*, Class*, Thread*, SP)
-    move $a3, $sp                   # pass $sp
+    jr   $t9                        # artThrowClassCastException (Class*, Class*, Thread*)
+    move $a2, rSELF                 # pass Thread::Current
 END art_quick_check_cast
 
     /*
@@ -620,7 +618,6 @@
      * a0 = array, a1 = index, a2 = value
      */
 ENTRY art_quick_aput_obj_with_null_and_bound_check
-    GENERATE_GLOBAL_POINTER
     bnez    $a0, .Lart_quick_aput_obj_with_bound_check_gp_set
     nop
     b .Lart_quick_throw_null_pointer_exception_gp_set
@@ -628,9 +625,7 @@
 END art_quick_aput_obj_with_null_and_bound_check
 
 ENTRY art_quick_aput_obj_with_bound_check
-    GENERATE_GLOBAL_POINTER
-.Lart_quick_aput_obj_with_bound_check_gp_set:
-    lw $t0, ARRAY_LENGTH_OFFSET($a0)
+    lw $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0)
     sltu $t1, $a1, $t0
     bnez $t1, .Lart_quick_aput_obj_gp_set
     nop
@@ -640,19 +635,17 @@
 END art_quick_aput_obj_with_bound_check
 
 ENTRY art_quick_aput_obj
-    GENERATE_GLOBAL_POINTER
-.Lart_quick_aput_obj_gp_set:
     beqz $a2, .Ldo_aput_null
     nop
-    lw $t0, CLASS_OFFSET($a0)
-    lw $t1, CLASS_OFFSET($a2)
-    lw $t0, CLASS_COMPONENT_TYPE_OFFSET($t0)
+    lw $t0, MIRROR_OBJECT_CLASS_OFFSET($a0)
+    lw $t1, MIRROR_OBJECT_CLASS_OFFSET($a2)
+    lw $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($t0)
     bne $t1, $t0, .Lcheck_assignability  # value's type == array's component type - trivial assignability
     nop
 .Ldo_aput:
     sll $a1, $a1, 2
     add $t0, $a0, $a1
-    sw  $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
+    sw  $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0)
     lw  $t0, THREAD_CARD_TABLE_OFFSET(rSELF)
     srl $t1, $a0, 7
     add $t1, $t1, $t0
@@ -662,7 +655,7 @@
 .Ldo_aput_null:
     sll $a1, $a1, 2
     add $t0, $a0, $a1
-    sw  $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
+    sw  $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0)
     jr  $ra
     nop
 .Lcheck_assignability:
@@ -689,10 +682,9 @@
     nop
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     move $a1, $a2
-    move $a2, rSELF                 # pass Thread::Current
     la   $t9, artThrowArrayStoreException
-    jr   $t9                        # artThrowArrayStoreException(Class*, Class*, Thread*, SP)
-    move $a3, $sp                   # pass $sp
+    jr   $t9                        # artThrowArrayStoreException(Class*, Class*, Thread*)
+    move $a2, rSELF                 # pass Thread::Current
 END art_quick_aput_obj
 
     /*
@@ -702,12 +694,10 @@
      */
     .extern artInitializeStaticStorageFromCode
 ENTRY art_quick_initialize_static_storage
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME            # save callee saves in case of GC
-    move    $a2, rSELF                          # pass Thread::Current
-    # artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*, $sp)
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME            # save callee saves in case of GC
+    # artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*)
     jal     artInitializeStaticStorageFromCode
-    move    $a3, $sp                            # pass $sp
+    move    $a2, rSELF                          # pass Thread::Current
     RETURN_IF_RESULT_IS_NON_ZERO
 END art_quick_initialize_static_storage
 
@@ -716,12 +706,10 @@
      */
     .extern artInitializeTypeFromCode
 ENTRY art_quick_initialize_type
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME           # save callee saves in case of GC
-    move    $a2, rSELF                         # pass Thread::Current
-    # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, $sp)
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME           # save callee saves in case of GC
+    # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*)
     jal     artInitializeTypeFromCode
-    move    $a3, $sp                           # pass $sp
+    move    $a2, rSELF                         # pass Thread::Current
     RETURN_IF_RESULT_IS_NON_ZERO
 END art_quick_initialize_type
 
@@ -731,26 +719,67 @@
      */
     .extern artInitializeTypeAndVerifyAccessFromCode
 ENTRY art_quick_initialize_type_and_verify_access
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME           # save callee saves in case of GC
-    move    $a2, rSELF                         # pass Thread::Current
-    # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, $sp)
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME           # save callee saves in case of GC
+    # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*)
     jal     artInitializeTypeAndVerifyAccessFromCode
-    move    $a3, $sp                           # pass $sp
+    move    $a2, rSELF                         # pass Thread::Current
     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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetBooleanStaticFromCode   # (uint32_t field_idx, const Method* referrer, Thread*)
+    move   $a2, rSELF                    # pass Thread::Current
+    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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetByteStaticFromCode      # (uint32_t field_idx, const Method* referrer, Thread*)
+    move   $a2, rSELF                    # pass Thread::Current
+    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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetCharStaticFromCode      # (uint32_t field_idx, const Method* referrer, Thread*)
+    move   $a2, rSELF                    # pass Thread::Current
+    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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetShortStaticFromCode     # (uint32_t field_idx, const Method* referrer, Thread*)
+    move   $a2, rSELF                    # pass Thread::Current
+    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.
      */
     .extern artGet32StaticFromCode
 ENTRY art_quick_get32_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGet32StaticFromCode        # (uint32_t field_idx, const Method* referrer, Thread*)
     move   $a2, rSELF                    # pass Thread::Current
-    jal    artGet32StaticFromCode        # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
-    move   $a3, $sp                      # pass $sp
     RETURN_IF_NO_EXCEPTION
 END art_quick_get32_static
 
@@ -759,12 +788,10 @@
      */
     .extern artGet64StaticFromCode
 ENTRY art_quick_get64_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGet64StaticFromCode        # (uint32_t field_idx, const Method* referrer, Thread*)
     move   $a2, rSELF                    # pass Thread::Current
-    jal    artGet64StaticFromCode        # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
-    move   $a3, $sp                      # pass $sp
     RETURN_IF_NO_EXCEPTION
 END art_quick_get64_static
 
@@ -773,23 +800,66 @@
      */
     .extern artGetObjStaticFromCode
 ENTRY art_quick_get_obj_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetObjStaticFromCode       # (uint32_t field_idx, const Method* referrer, Thread*)
     move   $a2, rSELF                    # pass Thread::Current
-    jal    artGetObjStaticFromCode       # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
-    move   $a3, $sp                      # pass $sp
     RETURN_IF_NO_EXCEPTION
 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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetBooleanInstanceFromCode # (field_idx, Object*, referrer, Thread*)
+    move   $a3, rSELF                    # pass Thread::Current
+    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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetByteInstanceFromCode    # (field_idx, Object*, referrer, Thread*)
+    move   $a3, rSELF                    # pass Thread::Current
+    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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetCharInstanceFromCode    # (field_idx, Object*, referrer, Thread*)
+    move   $a3, rSELF                    # pass Thread::Current
+    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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetShortInstanceFromCode      # (field_idx, Object*, referrer, Thread*)
+    move   $a3, rSELF                    # pass Thread::Current
+    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
 ENTRY art_quick_get32_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
     move   $a3, rSELF                    # pass Thread::Current
     jal    artGet32InstanceFromCode      # (field_idx, Object*, referrer, Thread*, $sp)
     sw     $sp, 16($sp)                  # pass $sp
@@ -801,9 +871,8 @@
      */
     .extern artGet64InstanceFromCode
 ENTRY art_quick_get64_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
     move   $a3, rSELF                    # pass Thread::Current
     jal    artGet64InstanceFromCode      # (field_idx, Object*, referrer, Thread*, $sp)
     sw     $sp, 16($sp)                  # pass $sp
@@ -815,40 +884,58 @@
      */
     .extern artGetObjInstanceFromCode
 ENTRY art_quick_get_obj_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artGetObjInstanceFromCode     # (field_idx, Object*, referrer, Thread*)
     move   $a3, rSELF                    # pass Thread::Current
-    jal    artGetObjInstanceFromCode     # (field_idx, Object*, referrer, Thread*, $sp)
-    sw     $sp, 16($sp)                  # pass $sp
     RETURN_IF_NO_EXCEPTION
 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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artSet8StaticFromCode         # (field_idx, new_val, referrer, Thread*)
+    move   $a3, rSELF                    # pass Thread::Current
+    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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artSet16StaticFromCode        # (field_idx, new_val, referrer, Thread*, $sp)
+    move   $a3, rSELF                    # pass Thread::Current
+    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
 ENTRY art_quick_set32_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artSet32StaticFromCode        # (field_idx, new_val, referrer, Thread*)
     move   $a3, rSELF                    # pass Thread::Current
-    jal    artSet32StaticFromCode        # (field_idx, new_val, referrer, Thread*, $sp)
-    sw     $sp, 16($sp)                  # pass $sp
     RETURN_IF_ZERO
 END art_quick_set32_static
 
     /*
      * 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
-    lw     $a1, 64($sp)                  # pass referrer's Method*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artSet64StaticFromCode        # (field_idx, referrer, new_val, Thread*)
     sw     rSELF, 16($sp)                # pass Thread::Current
-    jal    artSet64StaticFromCode        # (field_idx, referrer, new_val, Thread*, $sp)
-    sw     $sp, 20($sp)                  # pass $sp
     RETURN_IF_ZERO
 END art_quick_set64_static
 
@@ -857,39 +944,59 @@
      */
     .extern artSetObjStaticFromCode
 ENTRY art_quick_set_obj_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artSetObjStaticFromCode       # (field_idx, new_val, referrer, Thread*)
     move   $a3, rSELF                    # pass Thread::Current
-    jal    artSetObjStaticFromCode       # (field_idx, new_val, referrer, Thread*, $sp)
-    sw     $sp, 16($sp)                  # pass $sp
     RETURN_IF_ZERO
 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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a3, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artSet8InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*)
+    sw     rSELF, 16($sp)                # pass Thread::Current
+    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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a3, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artSet16InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*)
+    sw     rSELF, 16($sp)                # pass Thread::Current
+    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
 ENTRY art_quick_set32_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a3, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artSet32InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*)
     sw     rSELF, 16($sp)                # pass Thread::Current
-    jal    artSet32InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*, $sp)
-    sw     $sp, 20($sp)                  # pass $sp
     RETURN_IF_ZERO
 END art_quick_set32_instance
 
     /*
      * 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
-    sw     rSELF, 16($sp)                # pass Thread::Current
-    jal    artSet64InstanceFromCode      # (field_idx, Object*, new_val, Thread*, $sp)
-    sw     $sp, 20($sp)                  # pass $sp
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $t0, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # load referrer's Method*
+    sw     rSELF, 20($sp)                # pass Thread::Current
+    jal    artSet64InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*)
+    sw     $t0, 16($sp)                  # pass referrer's Method*
     RETURN_IF_ZERO
 END art_quick_set64_instance
 
@@ -898,12 +1005,10 @@
      */
     .extern artSetObjInstanceFromCode
 ENTRY art_quick_set_obj_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*
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a3, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
+    jal    artSetObjInstanceFromCode     # (field_idx, Object*, new_val, referrer, Thread*)
     sw     rSELF, 16($sp)                # pass Thread::Current
-    jal    artSetObjInstanceFromCode     # (field_idx, Object*, new_val, referrer, Thread*, $sp)
-    sw     $sp, 20($sp)                  # pass $sp
     RETURN_IF_ZERO
 END art_quick_set_obj_instance
 
@@ -915,12 +1020,10 @@
      */
     .extern artResolveStringFromCode
 ENTRY art_quick_resolve_string
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case of GC
-    move    $a2, rSELF                # pass Thread::Current
-    # artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*, $sp)
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case of GC
+    # artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*)
     jal     artResolveStringFromCode
-    move    $a3, $sp                  # pass $sp
+    move    $a2, rSELF                # pass Thread::Current
     RETURN_IF_RESULT_IS_NON_ZERO
 END art_quick_resolve_string
 
@@ -929,11 +1032,9 @@
 .macro TWO_ARG_DOWNCALL name, entrypoint, return
     .extern \entrypoint
 ENTRY \name
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case of GC
-    move    $a2, rSELF                # pass Thread::Current
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case of GC
     jal     \entrypoint
-    move    $a3, $sp                  # pass $sp
+    move    $a2, rSELF                # pass Thread::Current
     \return
 END \name
 .endm
@@ -941,11 +1042,9 @@
 .macro THREE_ARG_DOWNCALL name, entrypoint, return
     .extern \entrypoint
 ENTRY \name
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case of GC
-    move    $a3, rSELF                # pass Thread::Current
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case of GC
     jal     \entrypoint
-    sw      $sp, 16($sp)              # pass $sp
+    move    $a3, rSELF                # pass Thread::Current
     \return
 END \name
 .endm
@@ -958,18 +1057,16 @@
      */
     .extern artTestSuspendFromCode
 ENTRY art_quick_test_suspend
-    GENERATE_GLOBAL_POINTER
     lh     $a0, THREAD_FLAGS_OFFSET(rSELF)
     bnez   $a0, 1f
     addi  rSUSPEND, $zero, SUSPEND_CHECK_INTERVAL   # reset rSUSPEND to SUSPEND_CHECK_INTERVAL
     jr     $ra
     nop
 1:
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME          # save callee saves for stack crawl
+    jal    artTestSuspendFromCode              # (Thread*)
     move   $a0, rSELF
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME          # save callee saves for stack crawl
-    jal    artTestSuspendFromCode             # (Thread*, $sp)
-    move   $a1, $sp
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
 END art_quick_test_suspend
 
     /*
@@ -978,14 +1075,13 @@
      */
     .extern artQuickProxyInvokeHandler
 ENTRY art_quick_proxy_invoke_handler
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     sw      $a0, 0($sp)            # place proxy method at bottom of frame
     move    $a2, rSELF             # pass Thread::Current
     jal     artQuickProxyInvokeHandler  # (Method* proxy method, receiver, Thread*, SP)
     move    $a3, $sp               # pass $sp
     lw      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     bnez    $t0, 1f
     mtc1    $v0, $f0               # place return value to FP return value
     jr      $ra
@@ -999,31 +1095,29 @@
      * dex method index.
      */
 ENTRY art_quick_imt_conflict_trampoline
-    GENERATE_GLOBAL_POINTER
     lw      $a0, 0($sp)            # load caller Method*
-    lw      $a0, METHOD_DEX_CACHE_METHODS_OFFSET($a0)  # load dex_cache_resolved_methods
+    lw      $a0, MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET($a0)  # load dex_cache_resolved_methods
     sll     $t0, 2                 # convert target method offset to bytes
     add     $a0, $t0               # get address of target method
-    lw      $a0, OBJECT_ARRAY_DATA_OFFSET($a0)  # load the target method
+    lw      $a0, MIRROR_OBJECT_ARRAY_DATA_OFFSET($a0)  # load the target method
     la      $t9, art_quick_invoke_interface_trampoline
     jr      $t9
 END art_quick_imt_conflict_trampoline
 
     .extern artQuickResolutionTrampoline
 ENTRY art_quick_resolution_trampoline
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     move    $a2, rSELF             # pass Thread::Current
     jal     artQuickResolutionTrampoline  # (Method* called, receiver, Thread*, SP)
     move    $a3, $sp               # pass $sp
     beqz    $v0, 1f
     lw      $a0, 0($sp)            # load resolved method to $a0
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     move    $t9, $v0               # code pointer must be in $t9 to generate the global pointer
     jr      $v0                    # tail call to method
     nop
 1:
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
 END art_quick_resolution_trampoline
 
@@ -1031,13 +1125,12 @@
 
     .extern artQuickToInterpreterBridge
 ENTRY art_quick_to_interpreter_bridge
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     move    $a1, rSELF             # pass Thread::Current
     jal     artQuickToInterpreterBridge    # (Method* method, Thread*, SP)
     move    $a2, $sp               # pass $sp
     lw      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     bnez    $t0, 1f
     mtc1    $v0, $f0               # place return value to FP return value
     jr      $ra
@@ -1052,21 +1145,19 @@
     .extern artInstrumentationMethodEntryFromCode
     .extern artInstrumentationMethodExitFromCode
 ENTRY art_quick_instrumentation_entry
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     move     $t0, $sp       # remember bottom of caller's frame
-    addiu    $sp, $sp, -32  # space for args, pad (3 words), arguments (5 words)
+    addiu    $sp, $sp, -32  # space for saved a0, pad (2 words), arguments (4 words)
     .cfi_adjust_cfa_offset 32
     sw       $a0, 28($sp)   # save arg0
-    sw       $ra, 16($sp)   # pass $ra
-    move     $a3, $t0       # pass $sp
-    jal      artInstrumentationMethodEntryFromCode  # (Method*, Object*, Thread*, SP, LR)
+    move     $a3, $ra       # pass $ra
+    jal      artInstrumentationMethodEntryFromCode  # (Method*, Object*, Thread*, LR)
     move     $a2, rSELF     # pass Thread::Current
     move     $t9, $v0       # $t9 holds reference to code
     lw       $a0, 28($sp)   # restore arg0
     addiu    $sp, $sp, 32   # remove args
     .cfi_adjust_cfa_offset -32
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     jalr     $t9            # call method
     nop
 END art_quick_instrumentation_entry
@@ -1075,9 +1166,9 @@
 art_quick_instrumentation_exit:
     .cfi_startproc
     addiu    $t9, $ra, 4    # put current address into $t9 to rebuild $gp
-    GENERATE_GLOBAL_POINTER
+    .cpload  $t9
     move     $ra, $zero     # link register is to here, so clobber with 0 for later checks
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
     move     $t0, $sp       # remember bottom of caller's frame
     addiu    $sp, $sp, -48  # save return values and set up args
     .cfi_adjust_cfa_offset 48
@@ -1110,14 +1201,11 @@
      * will long jump to the upcall with a special exception of -1.
      */
     .extern artDeoptimize
-    .extern artEnterInterpreterFromDeoptimize
 ENTRY art_quick_deoptimize
-    GENERATE_GLOBAL_POINTER
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    move     $a0, rSELF     # pass Thread::current
-    jal      artDeoptimize  # artDeoptimize(Thread*, SP)
+    jal      artDeoptimize  # artDeoptimize(Thread*)
                             # Returns caller method's frame size.
-    move     $a1, $sp       # pass $sp
+    move     $a0, rSELF     # pass Thread::current
 END art_quick_deoptimize
 
     /*
@@ -1130,7 +1218,7 @@
      *   $a1: high word
      *   $a2: shift count
      */
-ENTRY art_quick_shl_long
+ENTRY_NO_GP art_quick_shl_long
     /* shl-long vAA, vBB, vCC */
     sll     $v0, $a0, $a2                    #  rlo<- alo << (shift&31)
     not     $v1, $a2                         #  rhi<- 31-shift  (shift is 5b)
@@ -1154,8 +1242,7 @@
      *   $a1: high word
      *   $a2: shift count
      */
-    .global art_quick_shr_long
-ENTRY art_quick_shr_long
+ENTRY_NO_GP art_quick_shr_long
     sra     $v1, $a1, $a2                    #  rhi<- ahi >> (shift&31)
     srl     $v0, $a0, $a2                    #  rlo<- alo >> (shift&31)
     sra     $a3, $a1, 31                     #  $a3<- sign(ah)
@@ -1180,8 +1267,7 @@
      *   r2: shift count
      */
     /* ushr-long vAA, vBB, vCC */
-    .global art_quick_ushr_long
-ENTRY art_quick_ushr_long
+ENTRY_NO_GP art_quick_ushr_long
     srl     $v1, $a1, $a2                    #  rhi<- ahi >> (shift&31)
     srl     $v0, $a0, $a2                    #  rlo<- alo >> (shift&31)
     not     $a0, $a2                         #  alo<- 31-shift (shift is 5b)
@@ -1194,12 +1280,5 @@
     movn    $v1, $zero, $a2                  #  rhi<- 0 (if shift&0x20)
 END art_quick_ushr_long
 
-ENTRY art_quick_indexof
-    jr $ra
-    nop
-END art_quick_indexof
-
-ENTRY art_quick_string_compareto
-    jr $ra
-    nop
-END art_quick_string_compareto
+UNIMPLEMENTED art_quick_indexof
+UNIMPLEMENTED art_quick_string_compareto
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 864e3f7..0fcd297 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -41,7 +41,7 @@
       for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
         Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
         if (!runtime_->HasCalleeSaveMethod(type)) {
-          runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(type), type);
+          runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(), type);
         }
       }
     }
@@ -260,7 +260,7 @@
           "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
           "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
           "memory");  // clobber.
-#elif defined(__x86_64__) && !defined(__APPLE__)
+#elif defined(__x86_64__) && !defined(__APPLE__) && defined(__clang__)
     // Note: Uses the native convention
     // TODO: Set the thread?
     __asm__ __volatile__(
@@ -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"
 
@@ -485,23 +485,21 @@
           "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
           "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
           "memory");  // clobber.
-#elif defined(__x86_64__) && !defined(__APPLE__)
+#elif defined(__x86_64__) && !defined(__APPLE__) && defined(__clang__)
     // 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
@@ -532,18 +530,6 @@
 #endif
   }
 
-  // Method with 32b arg0, 32b arg1, 64b arg2
-  size_t Invoke3UUWithReferrer(uint32_t arg0, uint32_t arg1, uint64_t arg2, uintptr_t code,
-                               Thread* self, mirror::ArtMethod* referrer) {
-#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__)
-    // Just pass through.
-    return Invoke3WithReferrer(arg0, arg1, arg2, code, self, referrer);
-#else
-    // TODO: Needs 4-param invoke.
-    return 0;
-#endif
-  }
-
   static uintptr_t GetEntrypoint(Thread* self, QuickEntrypointEnum entrypoint) {
     int32_t offset;
 #ifdef __LP64__
@@ -746,17 +732,17 @@
         EXPECT_EQ(LockWord::LockState::kFatLocked, iter_state);
       }
     } else {
-      bool lock;  // Whether to lock or unlock in this step.
+      bool take_lock;  // Whether to lock or unlock in this step.
       if (counts[index] == 0) {
-        lock = true;
+        take_lock = true;
       } else if (counts[index] == kThinLockLoops) {
-        lock = false;
+        take_lock = false;
       } else {
         // Randomly.
-        lock = r.next() % 2 == 0;
+        take_lock = r.next() % 2 == 0;
       }
 
-      if (lock) {
+      if (take_lock) {
         test->Invoke3(reinterpret_cast<size_t>(objects[index].Get()), 0U, 0U, art_quick_lock_object,
                       self);
         counts[index]++;
@@ -1150,7 +1136,7 @@
   // For some reason this does not work, as the type_idx is artificial and outside what the
   // resolved types of c_obj allow...
 
-  if (false) {
+  if ((false)) {
     // Use an arbitrary method from c to use as referrer
     size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex()),    // type_idx
                             reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0)),  // arbitrary
@@ -1223,13 +1209,12 @@
   // Use array so we can index into it and use a matrix for expected results
   // Setup: The first half is standard. The second half uses a non-zero offset.
   // TODO: Shared backing arrays.
-  static constexpr size_t kBaseStringCount  = 8;
-  const char* c[kBaseStringCount] = { "", "", "a", "aa", "ab",
+  const char* c[] = { "", "", "a", "aa", "ab",
       "aacaacaacaacaacaac",  // This one's under the default limit to go to __memcmp16.
       "aacaacaacaacaacaacaacaacaacaacaacaac",     // This one's over.
       "aacaacaacaacaacaacaacaacaacaacaacaaca" };  // As is this one. We need a separate one to
                                                   // defeat object-equal optimizations.
-
+  static constexpr size_t kBaseStringCount  = arraysize(c);
   static constexpr size_t kStringCount = 2 * kBaseStringCount;
 
   StackHandleScope<kStringCount> hs(self);
@@ -1306,17 +1291,262 @@
 }
 
 
-static void GetSet32Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
-                           mirror::ArtMethod* referrer, StubTest* test)
+static void GetSetBooleanStatic(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 = 7;
-  uint32_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF };
+  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::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__))
+  int8_t values[] = { -128, -64, 0, 64, 127 };
+
+  for (size_t i = 0; i < arraysize(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__))
+  uint8_t values[] = { 0, true, 2, 128, 0xFF };
+
+  for (size_t i = 0; i < arraysize(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__))
+  int8_t values[] = { -128, -64, 0, 64, 127 };
+
+  for (size_t i = 0; i < arraysize(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::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__))
+  uint16_t values[] = { 0, 1, 2, 255, 32768, 0xFFFF };
+
+  for (size_t i = 0; i < arraysize(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::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__))
+  int16_t values[] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };
+
+  for (size_t i = 0; i < arraysize(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__))
+  uint16_t values[] = { 0, 1, 2, 255, 32768, 0xFFFF };
+
+  for (size_t i = 0; i < arraysize(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__))
+  int16_t values[] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };
+
+  for (size_t i = 0; i < arraysize(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::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__))
+  uint32_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF };
+
+  for (size_t i = 0; i < arraysize(values); ++i) {
+    test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                              static_cast<size_t>(values[i]),
+                              0U,
                               StubTest::GetEntrypoint(self, kQuickSet32Static),
                               self,
                               referrer);
@@ -1341,10 +1571,9 @@
                              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 = 7;
-  uint32_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF };
+  uint32_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF };
 
-  for (size_t i = 0; i < num_values; ++i) {
+  for (size_t i = 0; i < arraysize(values); ++i) {
     test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                               reinterpret_cast<size_t>(obj->Get()),
                               static_cast<size_t>(values[i]),
@@ -1396,8 +1625,8 @@
 }
 #endif
 
-static void GetSetObjStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
-                            mirror::ArtMethod* referrer, StubTest* test)
+static void GetSetObjStatic(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__))
   set_and_check_static((*f)->GetDexFieldIndex(), nullptr, self, referrer, test);
@@ -1461,14 +1690,13 @@
 
 // TODO: Complete these tests for 32b architectures.
 
-static void GetSet64Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
-                           mirror::ArtMethod* referrer, StubTest* test)
+static void GetSet64Static(Handle<mirror::ArtField>* f, Thread* self, mirror::ArtMethod* referrer,
+                           StubTest* test)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
 #if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__)
-  constexpr size_t num_values = 8;
-  uint64_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF };
+  uint64_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF };
 
-  for (size_t i = 0; i < num_values; ++i) {
+  for (size_t i = 0; i < arraysize(values); ++i) {
     test->Invoke3UWithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                                values[i],
                                StubTest::GetEntrypoint(self, kQuickSet64Static),
@@ -1484,6 +1712,7 @@
     EXPECT_EQ(res, values[i]) << "Iteration " << i;
   }
 #else
+  UNUSED(f, self, referrer, test);
   LOG(INFO) << "Skipping set64static 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 set64static as I don't know how to do that on " << kRuntimeISA << std::endl;
@@ -1495,10 +1724,9 @@
                              Thread* self, mirror::ArtMethod* referrer, StubTest* test)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
 #if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__)
-  constexpr size_t num_values = 8;
-  uint64_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF };
+  uint64_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF };
 
-  for (size_t i = 0; i < num_values; ++i) {
+  for (size_t i = 0; i < arraysize(values); ++i) {
     test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                               reinterpret_cast<size_t>(obj->Get()),
                               static_cast<size_t>(values[i]),
@@ -1521,6 +1749,7 @@
     EXPECT_EQ(res, static_cast<int64_t>(res2));
   }
 #else
+  UNUSED(obj, f, self, referrer, test);
   LOG(INFO) << "Skipping set64instance 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 set64instance as I don't know how to do that on " << kRuntimeISA << std::endl;
@@ -1550,27 +1779,47 @@
     Handle<mirror::ObjectArray<mirror::ArtField>> fields(hs.NewHandle(c.Get()->GetSFields()));
     int32_t num_fields = fields->GetLength();
     for (int32_t i = 0; i < num_fields; ++i) {
-      StackHandleScope<1> hs(self);
-      Handle<mirror::ArtField> f(hs.NewHandle(fields->Get(i)));
+      StackHandleScope<1> hs2(self);
+      Handle<mirror::ArtField> f(hs2.NewHandle(fields->Get(i)));
 
       Primitive::Type type = f->GetTypeAsPrimitiveType();
       switch (type) {
+        case Primitive::Type::kPrimBoolean:
+          if (test_type == type) {
+            GetSetBooleanStatic(&f, self, m.Get(), test);
+          }
+          break;
+        case Primitive::Type::kPrimByte:
+          if (test_type == type) {
+            GetSetByteStatic(&f, self, m.Get(), test);
+          }
+          break;
+        case Primitive::Type::kPrimChar:
+          if (test_type == type) {
+            GetSetCharStatic(&f, self, m.Get(), test);
+          }
+          break;
+        case Primitive::Type::kPrimShort:
+          if (test_type == type) {
+            GetSetShortStatic(&f, self, m.Get(), test);
+          }
+          break;
         case Primitive::Type::kPrimInt:
           if (test_type == type) {
-            GetSet32Static(&obj, &f, self, m.Get(), test);
+            GetSet32Static(&f, self, m.Get(), test);
           }
           break;
 
         case Primitive::Type::kPrimLong:
           if (test_type == type) {
-            GetSet64Static(&obj, &f, self, m.Get(), test);
+            GetSet64Static(&f, self, m.Get(), test);
           }
           break;
 
         case Primitive::Type::kPrimNot:
           // Don't try array.
           if (test_type == type && f->GetTypeDescriptor()[0] != '[') {
-            GetSetObjStatic(&obj, &f, self, m.Get(), test);
+            GetSetObjStatic(&f, self, m.Get(), test);
           }
           break;
 
@@ -1585,11 +1834,31 @@
     Handle<mirror::ObjectArray<mirror::ArtField>> fields(hs.NewHandle(c.Get()->GetIFields()));
     int32_t num_fields = fields->GetLength();
     for (int32_t i = 0; i < num_fields; ++i) {
-      StackHandleScope<1> hs(self);
-      Handle<mirror::ArtField> f(hs.NewHandle(fields->Get(i)));
+      StackHandleScope<1> hs2(self);
+      Handle<mirror::ArtField> f(hs2.NewHandle(fields->Get(i)));
 
       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 +1887,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 +1954,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 +2004,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 +2014,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 +2036,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 +2046,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.
@@ -1779,10 +2087,10 @@
   // Use array so we can index into it and use a matrix for expected results
   // Setup: The first half is standard. The second half uses a non-zero offset.
   // TODO: Shared backing arrays.
-  static constexpr size_t kStringCount = 7;
-  const char* c_str[kStringCount] = { "", "a", "ba", "cba", "dcba", "edcba", "asdfghjkl" };
-  static constexpr size_t kCharCount = 5;
-  const char c_char[kCharCount] = { 'a', 'b', 'c', 'd', 'e' };
+  const char* c_str[] = { "", "a", "ba", "cba", "dcba", "edcba", "asdfghjkl" };
+  static constexpr size_t kStringCount = arraysize(c_str);
+  const char c_char[] = { 'a', 'b', 'c', 'd', 'e' };
+  static constexpr size_t kCharCount = arraysize(c_char);
 
   StackHandleScope<kStringCount> hs(self);
   Handle<mirror::String> s[kStringCount];
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index a578023..fea16da 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -135,16 +135,6 @@
     CFI_DEF_CFA(esp, 4)
 END_MACRO
 
-MACRO1(DEFINE_FUNCTION_NO_HIDE, c_name)
-    FUNCTION_TYPE(\c_name, 0)
-    .globl VAR(c_name, 0)
-    ALIGN_FUNCTION_ENTRY
-VAR(c_name, 0):
-    CFI_STARTPROC
-    // Ensure we get a sane starting CFA.
-    CFI_DEF_CFA(esp, 4)
-END_MACRO
-
 MACRO1(END_FUNCTION, c_name)
     CFI_ENDPROC
     SIZE(\c_name, 0)
@@ -174,20 +164,15 @@
     SIZE(\name, 0)
 END_MACRO
 
-MACRO0(SETUP_GOT_NOSAVE)
+MACRO1(SETUP_GOT_NOSAVE, got_reg)
 #ifndef __APPLE__
-    call __x86.get_pc_thunk.bx
-    addl $_GLOBAL_OFFSET_TABLE_, %ebx
+    .ifc RAW_VAR(got_reg, 0), ebx
+      call __x86.get_pc_thunk.bx
+      addl $_GLOBAL_OFFSET_TABLE_, %ebx
+    .else
+      .error "Unknown GOT register \got_reg"
+    .endif
 #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/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h
index c9f5a25..5a88f80 100644
--- a/runtime/arch/x86/asm_support_x86.h
+++ b/runtime/arch/x86/asm_support_x86.h
@@ -19,20 +19,8 @@
 
 #include "asm_support.h"
 
-// Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 156
-// Offset of field Thread::card_table_ verified in InitCpu
-#define THREAD_CARD_TABLE_OFFSET 120
-// Offset of field Thread::exception_ verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 124
-// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
-#define THREAD_ID_OFFSET 12
-
 #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 32
 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32
 #define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 32
 
-// Expected size of a heap reference
-#define HEAP_REFERENCE_SIZE 4
-
 #endif  // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
diff --git a/runtime/arch/x86/context_x86.cc b/runtime/arch/x86/context_x86.cc
index 37049cf..49aa326 100644
--- a/runtime/arch/x86/context_x86.cc
+++ b/runtime/arch/x86/context_x86.cc
@@ -17,9 +17,9 @@
 #include "context_x86.h"
 
 #include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
 #include "quick/quick_method_frame_info.h"
-#include "stack.h"
+#include "utils.h"
+
 
 namespace art {
 namespace x86 {
@@ -72,6 +72,16 @@
   }
 }
 
+bool X86Context::GetFPR(uint32_t reg ATTRIBUTE_UNUSED, uintptr_t* val ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "Floating-point registers are all caller save in X86";
+  UNREACHABLE();
+}
+
+bool X86Context::SetFPR(uint32_t reg ATTRIBUTE_UNUSED, uintptr_t value ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "Floating-point registers are all caller save in X86";
+  UNREACHABLE();
+}
+
 void X86Context::DoLongJump() {
 #if defined(__i386__)
   // Array of GPR values, filled from the context backward for the long jump pop. We add a slot at
@@ -81,7 +91,7 @@
     gprs[kNumberOfCpuRegisters - i - 1] = gprs_[i] != nullptr ? *gprs_[i] : X86Context::kBadGprBase + i;
   }
   // We want to load the stack pointer one slot below so that the ret will pop eip.
-  uintptr_t esp = gprs[kNumberOfCpuRegisters - ESP - 1] - kWordSize;
+  uintptr_t esp = gprs[kNumberOfCpuRegisters - ESP - 1] - sizeof(intptr_t);
   gprs[kNumberOfCpuRegisters] = esp;
   *(reinterpret_cast<uintptr_t*>(esp)) = eip_;
   __asm__ __volatile__(
diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h
index a350b25..01c8b82 100644
--- a/runtime/arch/x86/context_x86.h
+++ b/runtime/arch/x86/context_x86.h
@@ -62,15 +62,9 @@
 
   bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
-  bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE {
-    LOG(FATAL) << "Floating-point registers are all caller save in X86";
-    return false;
-  }
+  bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE;
 
-  bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE {
-    LOG(FATAL) << "Floating-point registers are all caller save in X86";
-    return false;
-  }
+  bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
   void SmashCallerSaves() OVERRIDE;
   void DoLongJump() OVERRIDE;
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index a072996..48d6c80 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -18,97 +18,16 @@
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "interpreter/interpreter.h"
 
 namespace art {
 
-// Interpreter entrypoints.
-extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh,
-                                                  const DexFile::CodeItem* code_item,
-                                                  ShadowFrame* shadow_frame, JValue* result);
-extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh,
-                                           const DexFile::CodeItem* code_item,
-                                           ShadowFrame* shadow_frame, JValue* result);
-
-// Portable entrypoints.
-extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
-
 // Cast entrypoints.
 extern "C" uint32_t art_quick_is_assignable(const mirror::Class* klass,
-                                                const mirror::Class* ref_class);
-extern "C" void art_quick_check_cast(void*, void*);
-
-// DexCache entrypoints.
-extern "C" void* art_quick_initialize_static_storage(uint32_t, void*);
-extern "C" void* art_quick_initialize_type(uint32_t, void*);
-extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, void*);
-extern "C" void* art_quick_resolve_string(void*, uint32_t);
-
-// Field entrypoints.
-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" 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*);
-extern "C" int64_t art_quick_get64_static(uint32_t);
-extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
-extern "C" void* art_quick_get_obj_static(uint32_t);
-
-// Array entrypoints.
-extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
-extern "C" void art_quick_handle_fill_data(void*, void*);
-
-// Lock entrypoints.
-extern "C" void art_quick_lock_object(void*);
-extern "C" void art_quick_unlock_object(void*);
-
-// Math entrypoints.
-extern "C" int64_t art_quick_d2l(double);
-extern "C" int64_t art_quick_f2l(float);
-extern "C" int64_t art_quick_ldiv(int64_t, int64_t);
-extern "C" int64_t art_quick_lmod(int64_t, int64_t);
-extern "C" int64_t art_quick_lmul(int64_t, int64_t);
-extern "C" uint64_t art_quick_lshl(uint64_t, uint32_t);
-extern "C" uint64_t art_quick_lshr(uint64_t, uint32_t);
-extern "C" uint64_t art_quick_lushr(uint64_t, uint32_t);
-
-// Intrinsic entrypoints.
-extern "C" int32_t art_quick_string_compareto(void*, void*);
-extern "C" void* art_quick_memcpy(void*, const void*, size_t);
-
-// Invoke entrypoints.
-extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
-extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
-
-// Thread entrypoints.
-extern "C" void art_quick_test_suspend();
-
-// Throw entrypoints.
-extern "C" void art_quick_deliver_exception(void*);
-extern "C" void art_quick_throw_array_bounds(int32_t index, int32_t limit);
-extern "C" void art_quick_throw_div_zero();
-extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
-extern "C" void art_quick_throw_null_pointer_exception();
-extern "C" void art_quick_throw_stack_overflow(void*);
-
-// Generic JNI downcall
-extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
-
-extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
+                                            const mirror::Class* ref_class);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
                      PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
@@ -137,15 +56,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..ad962e2 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_INTENDED;
+
       // 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:
@@ -180,6 +184,7 @@
 
       case 0x80:        // group 1, byte immediate.
       case 0x83:
+      case 0xc6:
         modrm = *pc++;
         has_modrm = true;
         immediate_size = 1;
@@ -188,7 +193,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 +208,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;
     }
   }
@@ -226,7 +231,7 @@
   return pc - startpc;
 }
 
-void FaultManager::HandleNestedSignal(int sig, siginfo_t* info, void* context) {
+void FaultManager::HandleNestedSignal(int, siginfo_t*, void* context) {
   // For the Intel architectures we need to go to an assembly language
   // stub.  This is because the 32 bit call to longjmp is much different
   // from the 64 bit ABI call and pushing things onto the stack inside this
@@ -279,7 +284,7 @@
   *out_return_pc = reinterpret_cast<uintptr_t>(pc + instr_size);
 }
 
-bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
+bool NullPointerHandler::Action(int, siginfo_t*, void* context) {
   struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
   uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
   uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_ESP);
@@ -319,7 +324,7 @@
 // The offset from fs is Thread::ThreadSuspendTriggerOffset().
 // To check for a suspend check, we examine the instructions that caused
 // the fault.
-bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) {
+bool SuspensionHandler::Action(int, siginfo_t*, void* context) {
   // These are the instructions to check for.  The first one is the mov eax, fs:[xxx]
   // where xxx is the offset of the suspend trigger.
 #if defined(__x86_64__)
@@ -393,7 +398,7 @@
 // This is done before any frame is established in the method.  The return
 // address for the previous method is on the stack at ESP.
 
-bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) {
+bool StackOverflowHandler::Action(int, siginfo_t* info, void* context) {
   struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
   uintptr_t sp = static_cast<uintptr_t>(uc->CTX_ESP);
 
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..a7c4124 100644
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ b/runtime/arch/x86/portable_entrypoints_x86.S
@@ -37,7 +37,7 @@
     andl LITERAL(0xFFFFFFF0), %ebx    // align frame size to 16 bytes
     subl LITERAL(12), %ebx        // remove space for return address, ebx, and ebp
     subl %ebx, %esp               // reserve stack space for argument array
-    SETUP_GOT_NOSAVE              // reset ebx to GOT table
+    SETUP_GOT_NOSAVE ebx          // reset ebx to GOT table
     lea  4(%esp), %eax            // use stack pointer + method ptr as dest for memcpy
     pushl 20(%ebp)                // push size of region to memcpy
     pushl 16(%ebp)                // push arg array as source of memcpy
@@ -46,7 +46,7 @@
     addl LITERAL(12), %esp        // pop arguments to memcpy
     mov 12(%ebp), %eax            // move method pointer into eax
     mov %eax, (%esp)              // push method pointer onto stack
-    call *METHOD_PORTABLE_CODE_OFFSET(%eax) // call the method
+    call *MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET(%eax) // call the method
     mov %ebp, %esp                // restore stack pointer
     POP ebx                       // pop ebx
     POP ebp                       // pop ebp
@@ -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)
@@ -115,21 +111,21 @@
   ret
 END_FUNCTION art_portable_resolution_trampoline
 
-DEFINE_FUNCTION_NO_HIDE art_portable_to_interpreter_bridge
+DEFINE_FUNCTION art_portable_to_interpreter_bridge
   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)
   ret
 END_FUNCTION art_portable_to_interpreter_bridge
+
+UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 75c8646..0109a7c 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -22,12 +22,21 @@
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kSaveAll)
      */
-MACRO0(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME)
+MACRO2(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME, got_reg, temp_reg)
     PUSH edi  // Save callee saves (ebx is saved/restored by the upcall)
     PUSH esi
     PUSH ebp
-    subl  MACRO_LITERAL(16), %esp  // Grow stack by 4 words, bottom word will hold Method*
-    CFI_ADJUST_CFA_OFFSET(16)
+    subl  MACRO_LITERAL(12), %esp  // Grow stack by 3 words.
+    CFI_ADJUST_CFA_OFFSET(12)
+    SETUP_GOT_NOSAVE RAW_VAR(got_reg, 0)
+    // Load Runtime::instance_ from GOT.
+    movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1)
+    movl (REG_VAR(temp_reg, 1)), REG_VAR(temp_reg, 1)
+    // Push save all callee-save method.
+    pushl RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg, 1))
+    CFI_ADJUST_CFA_OFFSET(4)
+    // Store esp as the top quick frame.
+    movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET
     // Ugly compile-time check, but we only have the preprocessor.
     // Last +4: implicit return address pushed on stack when caller made call.
 #if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 3*4 + 16 + 4)
@@ -39,12 +48,21 @@
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kRefsOnly)
      */
-MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME)
+MACRO2(SETUP_REFS_ONLY_CALLEE_SAVE_FRAME, got_reg, temp_reg)
     PUSH edi  // Save callee saves (ebx is saved/restored by the upcall)
     PUSH esi
     PUSH ebp
-    subl  MACRO_LITERAL(16), %esp  // Grow stack by 4 words, bottom word will hold Method*
-    CFI_ADJUST_CFA_OFFSET(16)
+    subl  MACRO_LITERAL(12), %esp  // Grow stack by 3 words.
+    CFI_ADJUST_CFA_OFFSET(12)
+    SETUP_GOT_NOSAVE VAR(got_reg, 0)
+    // Load Runtime::instance_ from GOT.
+    movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1)
+    movl (REG_VAR(temp_reg, 1)), REG_VAR(temp_reg, 1)
+    // Push save all callee-save method.
+    pushl RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg, 1))
+    CFI_ADJUST_CFA_OFFSET(4)
+    // Store esp as the top quick frame.
+    movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET
 
     // Ugly compile-time check, but we only have the preprocessor.
     // Last +4: implicit return address pushed on stack when caller made call.
@@ -53,7 +71,7 @@
 #endif
 END_MACRO
 
-MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME)
+MACRO0(RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME)
     addl MACRO_LITERAL(16), %esp  // Unwind stack up to saved values
     CFI_ADJUST_CFA_OFFSET(-16)
     POP ebp  // Restore callee saves (ebx is saved/restored by the upcall)
@@ -65,14 +83,22 @@
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
      */
-MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME)
+MACRO2(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME, got_reg, temp_reg)
     PUSH edi  // Save callee saves
     PUSH esi
     PUSH ebp
     PUSH ebx  // Save args
     PUSH edx
     PUSH ecx
-    PUSH eax   // Align stack, eax will be clobbered by Method*
+    SETUP_GOT_NOSAVE VAR(got_reg, 0)
+    // Load Runtime::instance_ from GOT.
+    movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1)
+    movl (REG_VAR(temp_reg, 1)), REG_VAR(temp_reg, 1)
+    // Push save all callee-save method.
+    pushl RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg, 1))
+    CFI_ADJUST_CFA_OFFSET(4)
+    // Store esp as the stop quick frame.
+    movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET
 
     // Ugly compile-time check, but we only have the preprocessor.
     // Last +4: implicit return address pushed on stack when caller made call.
@@ -81,7 +107,23 @@
 #endif
 END_MACRO
 
-MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME)
+    /*
+     * Macro that sets up the callee save frame to conform with
+     * Runtime::CreateCalleeSaveMethod(kRefsAndArgs) where the method is passed in EAX.
+     */
+MACRO0(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX)
+    PUSH edi  // Save callee saves
+    PUSH esi
+    PUSH ebp
+    PUSH ebx  // Save args
+    PUSH edx
+    PUSH ecx
+    PUSH eax  // Store the ArtMethod reference at the bottom of the stack.
+    // Store esp as the stop quick frame.
+    movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET
+END_MACRO
+
+MACRO0(RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME)
     addl MACRO_LITERAL(4), %esp  // Remove padding
     CFI_ADJUST_CFA_OFFSET(-4)
     POP ecx  // Restore args except eax
@@ -97,63 +139,54 @@
      * exception is Thread::Current()->exception_.
      */
 MACRO0(DELIVER_PENDING_EXCEPTION)
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME         // save callee saves for throw
-    mov %esp, %ecx
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx  // save callee saves for throw
     // Outgoing argument set up
-    subl  MACRO_LITERAL(8), %esp             // Alignment padding
-    CFI_ADJUST_CFA_OFFSET(8)
-    PUSH ecx                                 // pass SP
-    pushl %fs:THREAD_SELF_OFFSET             // pass Thread::Current()
+    subl  MACRO_LITERAL(12), %esp              // Alignment padding
+    CFI_ADJUST_CFA_OFFSET(12)
+    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
+    call SYMBOL(artDeliverPendingExceptionFromCode)  // artDeliverPendingExceptionFromCode(Thread*)
+    int3                                       // unreached
 END_MACRO
 
 MACRO2(NO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
     DEFINE_FUNCTION RAW_VAR(c_name, 0)
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
-    mov %esp, %ecx
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  ebx, ebx  // save all registers as basis for long jump context
     // Outgoing argument set up
-    subl  MACRO_LITERAL(8), %esp  // alignment padding
-    CFI_ADJUST_CFA_OFFSET(8)
-    PUSH ecx                      // pass SP
+    subl  MACRO_LITERAL(12), %esp  // alignment padding
+    CFI_ADJUST_CFA_OFFSET(12)
     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)
+    call VAR(cxx_name, 1)         // cxx_name(Thread*)
     int3                          // unreached
     END_FUNCTION RAW_VAR(c_name, 0)
 END_MACRO
 
 MACRO2(ONE_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
     DEFINE_FUNCTION RAW_VAR(c_name, 0)
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx  // save all registers as basis for long jump context
     mov %esp, %ecx
     // Outgoing argument set up
-    PUSH eax                      // alignment padding
-    PUSH ecx                      // pass SP
+    subl  MACRO_LITERAL(8), %esp  // alignment padding
+    CFI_ADJUST_CFA_OFFSET(8)
     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)
+    call VAR(cxx_name, 1)         // cxx_name(arg1, Thread*)
     int3                          // unreached
     END_FUNCTION RAW_VAR(c_name, 0)
 END_MACRO
 
 MACRO2(TWO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
     DEFINE_FUNCTION RAW_VAR(c_name, 0)
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
-    mov %esp, %edx
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx  // save all registers as basis for long jump context
     // Outgoing argument set up
-    PUSH edx                      // pass SP
+    PUSH eax                      // alignment padding
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     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)
+    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, Thread*)
     int3                          // unreached
     END_FUNCTION RAW_VAR(c_name, 0)
 END_MACRO
@@ -208,18 +241,9 @@
      */
 MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name)
     DEFINE_FUNCTION RAW_VAR(c_name, 0)
-    // Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
-    // return address
-    PUSH edi
-    PUSH esi
-    PUSH ebp
-    PUSH ebx  // Save args
-    PUSH edx
-    PUSH ecx
-    PUSH eax    // <-- callee save Method* to go here
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME ebx, ebx
     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
@@ -229,7 +253,7 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
-    call VAR(cxx_name, 1)     // cxx_name(arg1, arg2, arg3, Thread*, SP)
+    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, arg3, Thread*, SP)
     movl %edx, %edi               // save code pointer in EDI
     addl MACRO_LITERAL(36), %esp  // Pop arguments skip eax
     CFI_ADJUST_CFA_OFFSET(-36)
@@ -280,7 +304,7 @@
     andl LITERAL(0xFFFFFFF0), %ebx    // align frame size to 16 bytes
     subl LITERAL(12), %ebx        // remove space for return address, ebx, and ebp
     subl %ebx, %esp               // reserve stack space for argument array
-    SETUP_GOT_NOSAVE              // clobbers ebx (harmless here)
+    SETUP_GOT_NOSAVE ebx          // clobbers ebx (harmless here)
     lea  4(%esp), %eax            // use stack pointer + method ptr as dest for memcpy
     pushl 20(%ebp)                // push size of region to memcpy
     pushl 16(%ebp)                // push arg array as source of memcpy
@@ -292,7 +316,7 @@
     mov 4(%esp), %ecx             // copy arg1 into ecx
     mov 8(%esp), %edx             // copy arg2 into edx
     mov 12(%esp), %ebx            // copy arg3 into ebx
-    call *METHOD_QUICK_CODE_OFFSET(%eax) // call the method
+    call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%eax) // call the method
     mov %ebp, %esp                // restore stack pointer
     CFI_DEF_CFA_REGISTER(esp)
     POP ebx                       // pop ebx
@@ -316,79 +340,124 @@
 
 MACRO3(NO_ARG_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
-    SETUP_GOT_NOSAVE              // clobbers ebx (harmless here)
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx  // save ref containing registers for GC
     // Outgoing argument set up
-    subl MACRO_LITERAL(8), %esp   // push padding
-    CFI_ADJUST_CFA_OFFSET(8)
-    PUSH edx                      // pass SP
+    subl MACRO_LITERAL(12), %esp  // push padding
+    CFI_ADJUST_CFA_OFFSET(12)
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    call VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
+    call VAR(cxx_name, 1)         // cxx_name(Thread*)
     addl MACRO_LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    RESTORE_REFS_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(ONE_ARG_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
-    SETUP_GOT_NOSAVE              // clobbers EBX
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx  // save ref containing registers for GC
     // Outgoing argument set up
-    PUSH eax                      // push padding
-    PUSH edx                      // pass SP
+    subl MACRO_LITERAL(8), %esp   // push padding
+    CFI_ADJUST_CFA_OFFSET(8)
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass arg1
-    call VAR(cxx_name, 1)     // cxx_name(arg1, Thread*, SP)
+    call VAR(cxx_name, 1)         // cxx_name(arg1, Thread*)
     addl MACRO_LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    RESTORE_REFS_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_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
-    SETUP_GOT_NOSAVE              // clobbers EBX
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx  // save ref containing registers for GC
     // Outgoing argument set up
-    PUSH edx                      // pass SP
+    PUSH eax                      // push padding
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
-    call VAR(cxx_name, 1)     // cxx_name(arg1, arg2, Thread*, SP)
+    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, Thread*)
     addl MACRO_LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    RESTORE_REFS_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(THREE_ARG_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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx  // save ref containing registers for GC
     // Outgoing argument set up
-    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)
     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
+    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, arg3, Thread*)
+    addl MACRO_LITERAL(16), %esp  // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-16)
+    RESTORE_REFS_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(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
+    DEFINE_FUNCTION RAW_VAR(c_name, 0)
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx // save ref containing registers for GC
+    // Outgoing argument set up
+    mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ecx  // get referrer
+    PUSH eax                      // push padding
+    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*)
+    addl MACRO_LITERAL(16), %esp  // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-16)
+    RESTORE_REFS_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_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC
+    // Outgoing argument set up
+    mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %edx  // get referrer
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    PUSH edx                      // pass referrer
+    PUSH ecx                      // pass arg2
+    PUSH eax                      // pass arg1
+    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, referrer, Thread*)
+    addl MACRO_LITERAL(16), %esp  // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-16)
+    RESTORE_REFS_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(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
+    DEFINE_FUNCTION RAW_VAR(c_name, 0)
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx  // save ref containing registers for GC
+    // Outgoing argument set up
+    mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ebx  // get referrer
+    subl MACRO_LITERAL(12), %esp  // alignment padding
+    CFI_ADJUST_CFA_OFFSET(12)
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    PUSH ebx                      // pass referrer
+    PUSH edx                      // pass arg3
+    PUSH ecx                      // pass arg2
+    PUSH eax                      // pass arg1
+    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, arg3, referrer, Thread*)
+    addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    RESTORE_REFS_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
@@ -410,9 +479,8 @@
 END_MACRO
 
 MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION)
-    mov %fs:THREAD_EXCEPTION_OFFSET, %ebx // get exception field
-    testl %ebx, %ebx               // ebx == 0 ?
-    jnz 1f                         // if ebx != 0 goto 1
+    cmpl MACRO_LITERAL(0),%fs:THREAD_EXCEPTION_OFFSET // exception field == 0 ?
+    jne 1f                         // if exception field != 0 goto 1
     ret                            // return
 1:                                 // deliver exception on current thread
     DELIVER_PENDING_EXCEPTION
@@ -527,13 +595,13 @@
 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
 
-TWO_ARG_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
 
 DEFINE_FUNCTION art_quick_lock_object
     testl %eax, %eax                      // null check object/eax
     jz   .Lslow_lock
 .Lretry_lock:
-    movl LOCK_WORD_OFFSET(%eax), %ecx     // ecx := lock word
+    movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx  // ecx := lock word
     test LITERAL(0xC0000000), %ecx        // test the 2 high bits.
     jne  .Lslow_lock                      // slow path if either of the two high bits are set.
     movl %fs:THREAD_ID_OFFSET, %edx       // edx := thread id
@@ -542,11 +610,11 @@
     // unlocked case - %edx holds thread id with count of 0
     movl %eax, %ecx                       // remember object in case of retry
     xor  %eax, %eax                       // eax == 0 for comparison with lock word in cmpxchg
-    lock cmpxchg  %edx, LOCK_WORD_OFFSET(%ecx)
+    lock cmpxchg  %edx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx)
     jnz  .Lcmpxchg_fail                   // cmpxchg failed retry
     ret
 .Lcmpxchg_fail:
-    movl  %ecx, %eax                       // restore eax
+    movl  %ecx, %eax                      // restore eax
     jmp  .Lretry_lock
 .Lalready_thin:
     cmpw %cx, %dx                         // do we hold the lock already?
@@ -554,29 +622,28 @@
     addl LITERAL(65536), %ecx             // increment recursion count
     test LITERAL(0xC0000000), %ecx        // overflowed if either of top two bits are set
     jne  .Lslow_lock                      // count overflowed so go slow
-    movl %ecx, LOCK_WORD_OFFSET(%eax)     // update lockword, cmpxchg not necessary as we hold lock
+    // update lockword, cmpxchg not necessary as we hold lock
+    movl %ecx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax)
     ret
 .Lslow_lock:
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    SETUP_GOT_NOSAVE              // clobbers EBX
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx  // save ref containing registers for GC
     // Outgoing argument set up
-    PUSH eax                      // push padding
-    PUSH edx                      // pass SP
+    subl LITERAL(8), %esp         // alignment padding
+    CFI_ADJUST_CFA_OFFSET(8)
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass object
-    call SYMBOL(artLockObjectFromCode)  // artLockObjectFromCode(object, Thread*, SP)
+    call SYMBOL(artLockObjectFromCode)  // artLockObjectFromCode(object, Thread*)
     addl LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
     RETURN_IF_EAX_ZERO
 END_FUNCTION art_quick_lock_object
 
 DEFINE_FUNCTION art_quick_unlock_object
     testl %eax, %eax                      // null check object/eax
     jz   .Lslow_unlock
-    movl LOCK_WORD_OFFSET(%eax), %ecx     // ecx := lock word
+    movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx  // ecx := lock word
     movl %fs:THREAD_ID_OFFSET, %edx       // edx := thread id
     test LITERAL(0xC0000000), %ecx
     jnz  .Lslow_unlock                    // lock word contains a monitor
@@ -584,31 +651,28 @@
     jne  .Lslow_unlock
     cmpl LITERAL(65536), %ecx
     jae  .Lrecursive_thin_unlock
-    movl LITERAL(0), LOCK_WORD_OFFSET(%eax)
+    movl LITERAL(0), MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax)
     ret
 .Lrecursive_thin_unlock:
     subl LITERAL(65536), %ecx
-    mov  %ecx, LOCK_WORD_OFFSET(%eax)
+    mov  %ecx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax)
     ret
 .Lslow_unlock:
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    SETUP_GOT_NOSAVE              // clobbers EBX
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx  // save ref containing registers for GC
     // Outgoing argument set up
-    PUSH eax                      // push padding
-    PUSH edx                      // pass SP
+    subl LITERAL(8), %esp         // alignment padding
+    CFI_ADJUST_CFA_OFFSET(8)
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass object
-    call SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*, SP)
+    call SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*)
     addl LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
     RETURN_IF_EAX_ZERO
 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 +683,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
@@ -634,15 +697,14 @@
     POP ecx
     addl LITERAL(4), %esp
     CFI_ADJUST_CFA_OFFSET(-12)
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
-    mov %esp, %edx
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  ebx, ebx  // save all registers as basis for long jump context
     // Outgoing argument set up
-    PUSH edx                      // pass SP
+    PUSH eax                      // alignment padding
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
-    call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP)
+    call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*)
     int3                          // unreached
 END_FUNCTION art_quick_check_cast
 
@@ -658,7 +720,7 @@
 END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
 
 DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
-    movl ARRAY_LENGTH_OFFSET(%eax), %ebx
+    movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ebx
     cmpl %ebx, %ecx
     jb SYMBOL(art_quick_aput_obj)
     mov %ecx, %eax
@@ -669,18 +731,19 @@
 DEFINE_FUNCTION art_quick_aput_obj
     test %edx, %edx              // store of null
     jz .Ldo_aput_null
-    movl CLASS_OFFSET(%eax), %ebx
-    movl CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
-    cmpl CLASS_OFFSET(%edx), %ebx // value's type == array's component type - trivial assignability
+    movl MIRROR_OBJECT_CLASS_OFFSET(%eax), %ebx
+    movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
+    // value's type == array's component type - trivial assignability
+    cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ebx
     jne .Lcheck_assignability
 .Ldo_aput:
-    movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
+    movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
     movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
     shrl LITERAL(7), %eax
     movb %dl, (%edx, %eax)
     ret
 .Ldo_aput_null:
-    movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
+    movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
     ret
 .Lcheck_assignability:
     PUSH eax                     // save arguments
@@ -688,10 +751,9 @@
     PUSH edx
     subl LITERAL(8), %esp        // alignment padding
     CFI_ADJUST_CFA_OFFSET(8)
-    pushl CLASS_OFFSET(%edx)     // pass arg2 - type of the value to be stored
+    pushl MIRROR_OBJECT_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)
@@ -700,7 +762,7 @@
     POP  edx
     POP  ecx
     POP  eax
-    movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)  // do the aput
+    movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)  // do the aput
     movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
     shrl LITERAL(7), %eax
     movb %dl, (%edx, %eax)
@@ -709,20 +771,19 @@
     POP  edx
     POP  ecx
     POP  eax
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
-    mov %esp, %ecx
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx // save all registers as basis for long jump context
     // Outgoing argument set up
-    PUSH ecx                      // pass SP
+    PUSH eax                      // alignment padding
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH edx                      // pass arg2 - value
     PUSH eax                      // pass arg1 - array
-    call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP)
+    call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*)
     int3                          // unreached
 END_FUNCTION art_quick_aput_obj
 
 DEFINE_FUNCTION art_quick_memcpy
-    SETUP_GOT_NOSAVE              // clobbers EBX
+    SETUP_GOT_NOSAVE ebx          // clobbers EBX
     PUSH edx                      // pass arg3
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
@@ -738,7 +799,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 +808,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 +822,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 +835,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,247 +889,82 @@
     ret
 END_FUNCTION art_quick_lushr
 
-DEFINE_FUNCTION art_quick_set32_instance
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
-    mov %esp, %ebx                // remember SP
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, 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_get_short_static, artGetShortStaticFromCode, 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
+
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, 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_get_short_instance, artGetShortInstanceFromCode, 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
+
+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_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_EAX_ZERO
+
+// Call artSet64InstanceFromCode with 4 word size arguments and the referrer.
+DEFINE_FUNCTION art_quick_set64_instance
+    movd %ebx, %xmm0
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx  // save ref containing registers for GC
+    movd %xmm0, %ebx
+    // Outgoing argument set up
     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
-    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)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
-    RETURN_IF_EAX_ZERO            // return or deliver exception
-END_FUNCTION art_quick_set32_instance
-
-DEFINE_FUNCTION art_quick_set64_instance
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    subl LITERAL(8), %esp         // alignment padding
-    CFI_ADJUST_CFA_OFFSET(8)
-    PUSH esp                      // pass SP-8
-    addl LITERAL(8), (%esp)       // fix SP on stack by adding 8
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    pushl (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+12)(%esp)  // pass referrer
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ebx                      // pass high half of new_val
     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)
+    call SYMBOL(artSet64InstanceFromCode)  // (field_idx, Object*, new_val, referrer, Thread*)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
     RETURN_IF_EAX_ZERO            // return or deliver exception
 END_FUNCTION art_quick_set64_instance
 
-DEFINE_FUNCTION art_quick_set_obj_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
-    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)
-    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_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
-
-DEFINE_FUNCTION art_quick_get64_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(artGet64InstanceFromCode)  // (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_get64_instance
-
-DEFINE_FUNCTION art_quick_get_obj_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(artGetObjInstanceFromCode) // (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_get_obj_instance
-
-DEFINE_FUNCTION art_quick_set32_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
-    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)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
-    RETURN_IF_EAX_ZERO            // return or deliver exception
-END_FUNCTION art_quick_set32_static
-
+// Call artSet64StaticFromCode with 3 word size arguments plus with the referrer in the 2nd position
+// so that new_val is aligned on even registers were we passing arguments in registers.
 DEFINE_FUNCTION art_quick_set64_static
-    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
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx  // save ref containing registers for GC
+    mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ebx  // get referrer
+    subl LITERAL(12), %esp        // alignment padding
+    CFI_ADJUST_CFA_OFFSET(12)
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    mov 32(%ebx), %ebx            // get referrer
     PUSH edx                      // pass high half of new_val
     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)
+    call SYMBOL(artSet64StaticFromCode)  // (field_idx, referrer, new_val, Thread*)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
     RETURN_IF_EAX_ZERO            // return or deliver exception
 END_FUNCTION art_quick_set64_static
 
-DEFINE_FUNCTION art_quick_set_obj_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
-    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
-
 DEFINE_FUNCTION art_quick_proxy_invoke_handler
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame and Method*
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX
     PUSH esp                      // pass SP
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     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
@@ -1089,22 +981,21 @@
 DEFINE_FUNCTION art_quick_imt_conflict_trampoline
     PUSH ecx
     movl 8(%esp), %eax            // load caller Method*
-    movl METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax  // load dex_cache_resolved_methods
+    movl MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax  // load dex_cache_resolved_methods
     movd %xmm0, %ecx              // get target method index stored in xmm0
-    movl OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax  // load the target method
+    movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax  // load the target method
     POP ecx
     jmp SYMBOL(art_quick_invoke_interface_trampoline)
 END_FUNCTION art_quick_imt_conflict_trampoline
 
 DEFINE_FUNCTION art_quick_resolution_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME ebx, ebx
     movl %esp, %edi
     PUSH EDI                      // pass SP. do not just PUSH ESP; that messes up unwinding
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     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
@@ -1119,14 +1010,12 @@
     xchgl 0(%esp),%edi            // restore EDI and place code pointer as only value on stack
     ret                           // tail call into method
 1:
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
 END_FUNCTION art_quick_resolution_trampoline
 
-DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    // This also stores the native ArtMethod reference at the bottom of the stack.
-
+DEFINE_FUNCTION art_quick_generic_jni_trampoline
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX
     movl %esp, %ebp                 // save SP at callee-save frame
     CFI_DEF_CFA_REGISTER(ebp)
     subl LITERAL(5120), %esp
@@ -1134,12 +1023,10 @@
     // (Thread*,  SP)
     //  (esp)    4(esp)   <= C calling convention
     //  fs:...    ebp     <= where they are
-    // Also: PLT, so need GOT in ebx.
 
     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.
@@ -1200,26 +1087,25 @@
     movl %ebp, %esp
     CFI_DEF_CFA_REGISTER(esp)
 .Lexception_in_native:
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
 END_FUNCTION art_quick_generic_jni_trampoline
 
-DEFINE_FUNCTION_NO_HIDE art_quick_to_interpreter_bridge
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame
+DEFINE_FUNCTION art_quick_to_interpreter_bridge
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME  ebx, ebx  // save frame
     mov %esp, %edx                // remember SP
     PUSH eax                      // alignment padding
     PUSH edx                      // pass SP
     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
     punpckldq %xmm1, %xmm0
     addl LITERAL(16), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
 END_FUNCTION art_quick_to_interpreter_bridge
 
@@ -1227,26 +1113,23 @@
      * Routine that intercepts method calls and returns.
      */
 DEFINE_FUNCTION art_quick_instrumentation_entry
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    movl  %esp, %edx              // Save SP.
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME ebx, edx
     PUSH eax                      // Save eax which will be clobbered by the callee-save method.
-    subl LITERAL(8), %esp         // Align stack.
-    CFI_ADJUST_CFA_OFFSET(8)
+    subl LITERAL(12), %esp        // Align stack.
+    CFI_ADJUST_CFA_OFFSET(12)
     pushl 40(%esp)                // Pass LR.
     CFI_ADJUST_CFA_OFFSET(4)
-    PUSH edx                      // Pass SP.
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
     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)
+    call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, LR)
     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.
     movl SYMBOL(art_quick_instrumentation_exit)@GOT(%ebx), %ebx
+    // Place instrumentation exit as return pc. ebx holds the GOT computed on entry.
     movl %ebx, 32(%esp)
-                                  // Place instrumentation exit as return pc.
     movl (%esp), %eax             // Restore eax.
     movl 8(%esp), %ecx            // Restore ecx.
     movl 12(%esp), %edx           // Restore edx.
@@ -1259,7 +1142,7 @@
 
 DEFINE_FUNCTION art_quick_instrumentation_exit
     pushl LITERAL(0)              // Push a fake return PC as there will be none on the stack.
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx
     mov  %esp, %ecx               // Remember SP
     subl LITERAL(8), %esp         // Save float return value.
     CFI_ADJUST_CFA_OFFSET(8)
@@ -1274,7 +1157,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.
@@ -1286,7 +1168,7 @@
     movq (%esp), %xmm0            // Restore fpr return value.
     addl LITERAL(8), %esp
     CFI_ADJUST_CFA_OFFSET(-8)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     addl LITERAL(4), %esp         // Remove fake return pc.
     jmp   *%ecx                   // Return.
 END_FUNCTION art_quick_instrumentation_exit
@@ -1297,15 +1179,12 @@
      */
 DEFINE_FUNCTION art_quick_deoptimize
     pushl %ebx                    // Fake that we were called.
-    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
-    mov  %esp, %ecx               // Remember SP.
-    subl LITERAL(8), %esp         // Align stack.
-    CFI_ADJUST_CFA_OFFSET(8)
-    PUSH ecx                      // Pass SP.
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx
+    subl LITERAL(12), %esp        // Align stack.
+    CFI_ADJUST_CFA_OFFSET(12)
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
     CFI_ADJUST_CFA_OFFSET(4)
-    SETUP_GOT_NOSAVE              // clobbers EBX
-    call SYMBOL(artDeoptimize)  // artDeoptimize(Thread*, SP)
+    call SYMBOL(artDeoptimize)    // artDeoptimize(Thread*)
     int3                          // Unreachable.
 END_FUNCTION art_quick_deoptimize
 
@@ -1319,15 +1198,15 @@
 DEFINE_FUNCTION art_quick_string_compareto
     PUSH esi                    // push callee save reg
     PUSH edi                    // push callee save reg
-    mov STRING_COUNT_OFFSET(%eax), %edx
-    mov STRING_COUNT_OFFSET(%ecx), %ebx
-    mov STRING_VALUE_OFFSET(%eax), %esi
-    mov STRING_VALUE_OFFSET(%ecx), %edi
-    mov STRING_OFFSET_OFFSET(%eax), %eax
-    mov STRING_OFFSET_OFFSET(%ecx), %ecx
+    mov MIRROR_STRING_COUNT_OFFSET(%eax), %edx
+    mov MIRROR_STRING_COUNT_OFFSET(%ecx), %ebx
+    mov MIRROR_STRING_VALUE_OFFSET(%eax), %esi
+    mov MIRROR_STRING_VALUE_OFFSET(%ecx), %edi
+    mov MIRROR_STRING_OFFSET_OFFSET(%eax), %eax
+    mov MIRROR_STRING_OFFSET_OFFSET(%ecx), %ecx
     /* Build pointers to the start of string data */
-    lea  STRING_DATA_OFFSET(%esi, %eax, 2), %esi
-    lea  STRING_DATA_OFFSET(%edi, %ecx, 2), %edi
+    lea  MIRROR_CHAR_ARRAY_DATA_OFFSET(%esi, %eax, 2), %esi
+    lea  MIRROR_CHAR_ARRAY_DATA_OFFSET(%edi, %ecx, 2), %edi
     /* Calculate min length and count diff */
     mov   %edx, %ecx
     mov   %edx, %eax
@@ -1362,7 +1241,7 @@
 //  eax: address of jmp_buf in TLS
 
 DEFINE_FUNCTION art_nested_signal_return
-    SETUP_GOT_NOSAVE                // sets %ebx for call into PLT
+    SETUP_GOT_NOSAVE ebx            // sets %ebx for call into PLT
     movl LITERAL(1), %ecx
     pushl %ecx                      // second arg to longjmp (1)
     pushl %eax                      // first arg to longjmp (jmp_buf)
diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S
index f7acbdb..5964314 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)
@@ -130,16 +132,6 @@
     CFI_DEF_CFA(rsp, 8)
 END_MACRO
 
-MACRO1(DEFINE_FUNCTION_NO_HIDE, c_name)
-    FUNCTION_TYPE(\c_name, 0)
-    .globl VAR(c_name, 0)
-    ALIGN_FUNCTION_ENTRY
-VAR(c_name, 0):
-    CFI_STARTPROC
-    // Ensure we get a sane starting CFA.
-    CFI_DEF_CFA(rsp, 8)
-END_MACRO
-
 MACRO1(END_FUNCTION, c_name)
     CFI_ENDPROC
     SIZE(\c_name, 0)
@@ -170,18 +162,6 @@
     SIZE(\name, 0)
 END_MACRO
 
-MACRO1(UNIMPLEMENTED_NO_HIDE,name)
-    FUNCTION_TYPE(\name, 0)
-    .globl VAR(name, 0)
-    ALIGN_FUNCTION_ENTRY
-VAR(name, 0):
-    CFI_STARTPROC
-    int3
-    int3
-    CFI_ENDPROC
-    SIZE(\name, 0)
-END_MACRO
-
 MACRO0(UNREACHABLE)
     int3
 END_MACRO
diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h
index 40958dc..eddd172 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.h
+++ b/runtime/arch/x86_64/asm_support_x86_64.h
@@ -19,30 +19,8 @@
 
 #include "asm_support.h"
 
-// Note: these callee save methods loads require read barriers.
-// Offset of field Runtime::callee_save_methods_[kSaveAll]
-#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0
-// Offset of field Runtime::callee_save_methods_[kRefsOnly]
-#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET 8
-// Offset of field Runtime::callee_save_methods_[kRefsAndArgs]
-#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 16
-
-// Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 192
-// Offset of field Thread::card_table_ verified in InitCpu
-#define THREAD_CARD_TABLE_OFFSET 120
-// Offset of field Thread::exception_ verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 128
-// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
-#define THREAD_ID_OFFSET 12
-
 #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64 + 4*8
 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64 + 4*8
 #define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 176 + 4*8
 
-// Expected size of a heap reference
-#define HEAP_REFERENCE_SIZE 4
-// Expected size of a stack reference
-#define STACK_REFERENCE_SIZE 4
-
 #endif  // ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
diff --git a/runtime/arch/x86_64/context_x86_64.cc b/runtime/arch/x86_64/context_x86_64.cc
index 7699eaf..6e9b99c 100644
--- a/runtime/arch/x86_64/context_x86_64.cc
+++ b/runtime/arch/x86_64/context_x86_64.cc
@@ -17,9 +17,8 @@
 #include "context_x86_64.h"
 
 #include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
 #include "quick/quick_method_frame_info.h"
-#include "stack.h"
+#include "utils.h"
 
 namespace art {
 namespace x86_64 {
@@ -129,7 +128,7 @@
   }
 
   // We want to load the stack pointer one slot below so that the ret will pop eip.
-  uintptr_t rsp = gprs[kNumberOfCpuRegisters - RSP - 1] - kWordSize;
+  uintptr_t rsp = gprs[kNumberOfCpuRegisters - RSP - 1] - sizeof(intptr_t);
   gprs[kNumberOfCpuRegisters] = rsp;
   *(reinterpret_cast<uintptr_t*>(rsp)) = rip_;
 
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 35a0cf4..a2766f7 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -18,102 +18,22 @@
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/math_entrypoints.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "interpreter/interpreter.h"
 
 namespace art {
 
-// Interpreter entrypoints.
-extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh,
-                                                  const DexFile::CodeItem* code_item,
-                                                  ShadowFrame* shadow_frame, JValue* result);
-extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh,
-                                                   const DexFile::CodeItem* code_item,
-                                                   ShadowFrame* shadow_frame, JValue* result);
-
-// Portable entrypoints.
-extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
-
 // Cast entrypoints.
 extern "C" uint32_t art_quick_assignable_from_code(const mirror::Class* klass,
-                                            const mirror::Class* ref_class);
-extern "C" void art_quick_check_cast(void*, void*);
-
-// DexCache entrypoints.
-extern "C" void* art_quick_initialize_static_storage(uint32_t, void*);
-extern "C" void* art_quick_initialize_type(uint32_t, void*);
-extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, void*);
-extern "C" void* art_quick_resolve_string(void*, uint32_t);
-
-// Field entrypoints.
-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" 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*);
-extern "C" int64_t art_quick_get64_static(uint32_t);
-extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
-extern "C" void* art_quick_get_obj_static(uint32_t);
-
-// Array entrypoints.
-extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
-extern "C" void art_quick_handle_fill_data(void*, void*);
-
-// Lock entrypoints.
-extern "C" void art_quick_lock_object(void*);
-extern "C" void art_quick_unlock_object(void*);
-
-// Math entrypoints.
-extern "C" int64_t art_quick_d2l(double);
-extern "C" int64_t art_quick_f2l(float);
-extern "C" int64_t art_quick_ldiv(int64_t, int64_t);
-extern "C" int64_t art_quick_lmod(int64_t, int64_t);
-extern "C" int64_t art_quick_lmul(int64_t, int64_t);
-extern "C" uint64_t art_quick_lshl(uint64_t, uint32_t);
-extern "C" uint64_t art_quick_lshr(uint64_t, uint32_t);
-extern "C" uint64_t art_quick_lushr(uint64_t, uint32_t);
-
-// Intrinsic entrypoints.
-extern "C" int32_t art_quick_string_compareto(void*, void*);
-extern "C" void* art_quick_memcpy(void*, const void*, size_t);
-
-// Invoke entrypoints.
-extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
-extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
-
-// Thread entrypoints.
-extern "C" void art_quick_test_suspend();
-
-// Throw entrypoints.
-extern "C" void art_quick_deliver_exception(void*);
-extern "C" void art_quick_throw_array_bounds(int32_t index, int32_t limit);
-extern "C" void art_quick_throw_div_zero();
-extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
-extern "C" void art_quick_throw_null_pointer_exception();
-extern "C" void art_quick_throw_stack_overflow(void*);
-
-// Generic JNI entrypoint
-extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
-
-extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
+                                                   const mirror::Class* ref_class);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
                      PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
 #if defined(__APPLE__)
+  UNUSED(ipoints, jpoints, ppoints, qpoints);
   UNIMPLEMENTED(FATAL);
 #else
   // Interpreter
@@ -141,15 +61,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/portable_entrypoints_x86_64.S b/runtime/arch/x86_64/portable_entrypoints_x86_64.S
index 7b84d17..3a54005 100644
--- a/runtime/arch/x86_64/portable_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/portable_entrypoints_x86_64.S
@@ -25,4 +25,6 @@
 
 UNIMPLEMENTED art_portable_resolution_trampoline
 
-UNIMPLEMENTED_NO_HIDE art_portable_to_interpreter_bridge
+UNIMPLEMENTED art_portable_to_interpreter_bridge
+
+UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 5798092..bed7238 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -57,25 +57,25 @@
     PUSH r12  // Callee save.
     PUSH rbp  // Callee save.
     PUSH rbx  // Callee save.
-    // Create space for FPR args, plus padding for alignment
-    subq LITERAL(4 * 8), %rsp
-    CFI_ADJUST_CFA_OFFSET(4 * 8)
+    // Create space for FPR args, plus space for StackReference<ArtMethod>.
+    subq MACRO_LITERAL(4 * 8 + 8), %rsp
+    CFI_ADJUST_CFA_OFFSET(4 * 8 + 8)
     // Save FPRs.
-    movq %xmm12, 0(%rsp)
-    movq %xmm13, 8(%rsp)
-    movq %xmm14, 16(%rsp)
-    movq %xmm15, 24(%rsp)
-    subq MACRO_LITERAL(8), %rsp  // Space for Method* (also aligns the frame).
-    CFI_ADJUST_CFA_OFFSET(8)
+    movq %xmm12, 8(%rsp)
+    movq %xmm13, 16(%rsp)
+    movq %xmm14, 24(%rsp)
+    movq %xmm15, 32(%rsp)
     // R10 := ArtMethod* for save all callee save frame method.
     THIS_LOAD_REQUIRES_READ_BARRIER
     movq RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
     // Store ArtMethod* to bottom of stack.
     movq %r10, 0(%rsp)
+    // Store rsp as the top quick frame.
+    movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET
 
     // Ugly compile-time check, but we only have the preprocessor.
     // Last +8: implicit return address pushed on stack when caller made call.
-#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 6*8 + 4*8 + 8 + 8)
+#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 6 * 8 + 4 * 8 + 8 + 8)
 #error "SAVE_ALL_CALLEE_SAVE_FRAME(X86_64) size not as expected."
 #endif
 #endif  // __APPLE__
@@ -85,7 +85,7 @@
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kRefsOnly)
      */
-MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME)
+MACRO0(SETUP_REFS_ONLY_CALLEE_SAVE_FRAME)
 #if defined(__APPLE__)
     int3
     int3
@@ -100,9 +100,9 @@
     PUSH r12  // Callee save.
     PUSH rbp  // Callee save.
     PUSH rbx  // Callee save.
-    // Create space for FPR args, plus padding for alignment
-    subq LITERAL(8 + 4*8), %rsp
-    CFI_ADJUST_CFA_OFFSET(8 + 4*8)
+    // Create space for FPR args, plus space for StackReference<ArtMethod>.
+    subq LITERAL(8 + 4 * 8), %rsp
+    CFI_ADJUST_CFA_OFFSET(8 + 4 * 8)
     // Save FPRs.
     movq %xmm12, 8(%rsp)
     movq %xmm13, 16(%rsp)
@@ -113,16 +113,18 @@
     movq RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
     // Store ArtMethod* to bottom of stack.
     movq %r10, 0(%rsp)
+    // Store rsp as the stop quick frame.
+    movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET
 
     // Ugly compile-time check, but we only have the preprocessor.
     // Last +8: implicit return address pushed on stack when caller made call.
-#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 6*8 + 4*8 + 8 + 8)
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 6 * 8 + 4 * 8 + 8 + 8)
 #error "REFS_ONLY_CALLEE_SAVE_FRAME(X86_64) size not as expected."
 #endif
 #endif  // __APPLE__
 END_MACRO
 
-MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME)
+MACRO0(RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME)
     movq 8(%rsp), %xmm12
     movq 16(%rsp), %xmm13
     movq 24(%rsp), %xmm14
@@ -142,7 +144,7 @@
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
      */
-MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME)
+MACRO0(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME)
 #if defined(__APPLE__)
     int3
     int3
@@ -162,12 +164,13 @@
     PUSH rbx  // Callee save.
     PUSH rdx  // Quick arg 2.
     PUSH rcx  // Quick arg 3.
-    // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*.
+    // Create space for FPR args and create 2 slots, 1 of padding and 1 for the
+    // StackReference<ArtMethod>.
     subq MACRO_LITERAL(80 + 4 * 8), %rsp
     CFI_ADJUST_CFA_OFFSET(80 + 4 * 8)
     // R10 := ArtMethod* for ref and args callee save frame method.
     THIS_LOAD_REQUIRES_READ_BARRIER
-    movq RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
+    movq RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
     // Save FPRs.
     movq %xmm0, 16(%rsp)
     movq %xmm1, 24(%rsp)
@@ -183,16 +186,54 @@
     movq %xmm15, 104(%rsp)
     // Store ArtMethod* to bottom of stack.
     movq %r10, 0(%rsp)
+    // Store rsp as the top quick frame.
+    movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET
 
     // Ugly compile-time check, but we only have the preprocessor.
     // Last +8: implicit return address pushed on stack when caller made call.
-#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 11*8 + 4*8 + 80 + 8)
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 11 * 8 + 4 * 8 + 80 + 8)
 #error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(X86_64) size not as expected."
 #endif
 #endif  // __APPLE__
 END_MACRO
 
-MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME)
+MACRO0(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI)
+    // Save callee and GPR args, mixed together to agree with core spills bitmap.
+    PUSH r15  // Callee save.
+    PUSH r14  // Callee save.
+    PUSH r13  // Callee save.
+    PUSH r12  // Callee save.
+    PUSH r9   // Quick arg 5.
+    PUSH r8   // Quick arg 4.
+    PUSH rsi  // Quick arg 1.
+    PUSH rbp  // Callee save.
+    PUSH rbx  // Callee save.
+    PUSH rdx  // Quick arg 2.
+    PUSH rcx  // Quick arg 3.
+    // Create space for FPR args and create 2 slots, 1 of padding and 1 for the
+    // StackReference<ArtMethod>.
+    subq LITERAL(80 + 4 * 8), %rsp
+    CFI_ADJUST_CFA_OFFSET(80 + 4 * 8)
+    // Save FPRs.
+    movq %xmm0, 16(%rsp)
+    movq %xmm1, 24(%rsp)
+    movq %xmm2, 32(%rsp)
+    movq %xmm3, 40(%rsp)
+    movq %xmm4, 48(%rsp)
+    movq %xmm5, 56(%rsp)
+    movq %xmm6, 64(%rsp)
+    movq %xmm7, 72(%rsp)
+    movq %xmm12, 80(%rsp)
+    movq %xmm13, 88(%rsp)
+    movq %xmm14, 96(%rsp)
+    movq %xmm15, 104(%rsp)
+    // Store ArtMethod to bottom of stack.
+    movq %rdi, 0(%rsp)
+    // Store rsp as the stop quick frame.
+    movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET
+END_MACRO
+
+MACRO0(RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME)
     // Restore FPRs.
     movq 16(%rsp), %xmm0
     movq 24(%rsp), %xmm1
@@ -229,10 +270,9 @@
      */
 MACRO0(DELIVER_PENDING_EXCEPTION)
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME         // save callee saves for throw
-    // (Thread*, SP) setup
+    // (Thread*) setup
     movq %gs:THREAD_SELF_OFFSET, %rdi
-    movq %rsp, %rsi
-    call SYMBOL(artDeliverPendingExceptionFromCode)  // artDeliverPendingExceptionFromCode(Thread*, SP)
+    call SYMBOL(artDeliverPendingExceptionFromCode)  // artDeliverPendingExceptionFromCode(Thread*)
     UNREACHABLE
 END_MACRO
 
@@ -240,9 +280,8 @@
     DEFINE_FUNCTION VAR(c_name, 0)
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
     // Outgoing argument set up
-    movq %rsp, %rsi                    // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rdi  // pass Thread::Current()
-    call VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(Thread*)
     UNREACHABLE
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
@@ -251,9 +290,8 @@
     DEFINE_FUNCTION VAR(c_name, 0)
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
     // Outgoing argument set up
-    movq %rsp, %rdx                    // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rsi  // pass Thread::Current()
-    call VAR(cxx_name, 1)     // cxx_name(arg1, Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(arg1, Thread*)
     UNREACHABLE
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
@@ -262,9 +300,8 @@
     DEFINE_FUNCTION VAR(c_name, 0)
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
     // Outgoing argument set up
-    movq %rsp, %rcx                    // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
-    call VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(Thread*)
     UNREACHABLE
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
@@ -321,7 +358,7 @@
      */
 MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name)
     DEFINE_FUNCTION VAR(c_name, 0)
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  // save callee saves in case allocation triggers GC
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME  // save callee saves in case allocation triggers GC
     // Helper signature is always
     // (method_idx, *this_object, *caller_method, *self, sp)
 
@@ -333,7 +370,7 @@
                                                            // save the code pointer
     movq %rax, %rdi
     movq %rdx, %rax
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
 
     testq %rdi, %rdi
     jz 1f
@@ -481,7 +518,7 @@
     LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished
     LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished
 .Lgpr_setup_finished:
-    call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+    call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
     movq %rbp, %rsp               // Restore stack pointer.
     CFI_DEF_CFA_REGISTER(rsp)
     POP r9                        // Pop r9 - shorty*.
@@ -564,7 +601,7 @@
     LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished2
     LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2
 .Lgpr_setup_finished2:
-    call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+    call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
     movq %rbp, %rsp               // Restore stack pointer.
     CFI_DEF_CFA_REGISTER(rsp)
     POP r9                        // Pop r9 - shorty*.
@@ -639,88 +676,81 @@
 
 MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
     DEFINE_FUNCTION VAR(c_name, 0)
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME    // save ref containing registers for GC
     // Outgoing argument set up
-    movq %rsp, %rsi                   // pass SP
-    movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current()
-    call VAR(cxx_name, 1)         // cxx_name(Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
-    CALL_MACRO(return_macro, 2)       // return or deliver exception
+    movq %gs:THREAD_SELF_OFFSET, %rdi    // pass Thread::Current()
+    call VAR(cxx_name, 1)                // cxx_name(Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    CALL_MACRO(return_macro, 2)          // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
 
 MACRO3(ONE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
     DEFINE_FUNCTION VAR(c_name, 0)
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME   // save ref containing registers for GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME    // save ref containing registers for GC
     // Outgoing argument set up
-    movq %rsp, %rdx                    // pass SP
-    movq %gs:THREAD_SELF_OFFSET, %rsi  // pass Thread::Current()
-    call VAR(cxx_name, 1)          // cxx_name(arg0, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
-    CALL_MACRO(return_macro, 2)        // return or deliver exception
+    movq %gs:THREAD_SELF_OFFSET, %rsi    // pass Thread::Current()
+    call VAR(cxx_name, 1)                // cxx_name(arg0, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    CALL_MACRO(return_macro, 2)          // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
 
 MACRO3(TWO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
     DEFINE_FUNCTION VAR(c_name, 0)
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME   // save ref containing registers for GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME    // save ref containing registers for GC
     // Outgoing argument set up
-    movq %rsp, %rcx                    // pass SP
-    movq %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
-    call VAR(cxx_name, 1)          // cxx_name(arg0, arg1, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
-    CALL_MACRO(return_macro, 2)       // return or deliver exception
+    movq %gs:THREAD_SELF_OFFSET, %rdx    // pass Thread::Current()
+    call VAR(cxx_name, 1)                // cxx_name(arg0, arg1, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    CALL_MACRO(return_macro, 2)          // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
 
 MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
     DEFINE_FUNCTION VAR(c_name, 0)
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME   // save ref containing registers for GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME   // save ref containing registers for GC
     // Outgoing argument set up
-    movq %rsp, %r8                     // pass SP
-    movq %gs:THREAD_SELF_OFFSET, %rcx  // pass Thread::Current()
-    call VAR(cxx_name, 1)          // cxx_name(arg0, arg1, arg2, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
-    CALL_MACRO(return_macro, 2)        // return or deliver exception
+    movq %gs:THREAD_SELF_OFFSET, %rcx   // pass Thread::Current()
+    call VAR(cxx_name, 1)               // cxx_name(arg0, arg1, arg2, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+    CALL_MACRO(return_macro, 2)         // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
 
 MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
     DEFINE_FUNCTION VAR(c_name, 0)
-    movl 8(%rsp), %esi                 // pass referrer
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-                                       // arg0 is in rdi
-    movq %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
-    movq %rsp, %rcx                    // pass SP
-    call VAR(cxx_name, 1)          // cxx_name(arg0, referrer, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+    movl 8(%rsp), %esi                  // pass referrer
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+                                        // arg0 is in rdi
+    movq %gs:THREAD_SELF_OFFSET, %rdx   // pass Thread::Current()
+    call VAR(cxx_name, 1)               // cxx_name(arg0, referrer, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
     CALL_MACRO(return_macro, 2)
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
 
 MACRO3(TWO_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
     DEFINE_FUNCTION VAR(c_name, 0)
-    movl 8(%rsp), %edx                 // pass referrer
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-                                       // arg0 and arg1 are in rdi/rsi
-    movq %gs:THREAD_SELF_OFFSET, %rcx  // pass Thread::Current()
-    movq %rsp, %r8                     // pass SP
-    call VAR(cxx_name, 1)          // (arg0, arg1, referrer, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+    movl 8(%rsp), %edx                  // pass referrer
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+                                        // arg0 and arg1 are in rdi/rsi
+    movq %gs:THREAD_SELF_OFFSET, %rcx   // pass Thread::Current()
+    call VAR(cxx_name, 1)               // (arg0, arg1, referrer, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
     CALL_MACRO(return_macro, 2)
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
 
 MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
     DEFINE_FUNCTION VAR(c_name, 0)
-    movl 8(%rsp), %ecx                 // pass referrer
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-                                       // arg0, arg1, and arg2 are in rdi/rsi/rdx
+    movl 8(%rsp), %ecx                  // pass referrer
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+                                        // arg0, arg1, and arg2 are in rdi/rsi/rdx
     movq %gs:THREAD_SELF_OFFSET, %r8    // pass Thread::Current()
-    movq %rsp, %r9                     // pass SP
-    call VAR(cxx_name, 1)          // cxx_name(arg0, arg1, arg2, referrer, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
-    CALL_MACRO(return_macro, 2)        // return or deliver exception
+    call VAR(cxx_name, 1)               // cxx_name(arg0, arg1, arg2, referrer, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+    CALL_MACRO(return_macro, 2)         // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
 
@@ -858,13 +888,13 @@
 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
 
-TWO_ARG_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
 
 DEFINE_FUNCTION art_quick_lock_object
     testl %edi, %edi                      // Null check object/rdi.
     jz   .Lslow_lock
 .Lretry_lock:
-    movl LOCK_WORD_OFFSET(%edi), %ecx     // ecx := lock word.
+    movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx  // ecx := lock word.
     test LITERAL(0xC0000000), %ecx        // Test the 2 high bits.
     jne  .Lslow_lock                      // Slow path if either of the two high bits are set.
     movl %gs:THREAD_ID_OFFSET, %edx       // edx := thread id
@@ -872,7 +902,7 @@
     jnz  .Lalready_thin                   // Lock word contains a thin lock.
     // unlocked case - %edx holds thread id with count of 0
     xor  %eax, %eax                       // eax == 0 for comparison with lock word in cmpxchg
-    lock cmpxchg  %edx, LOCK_WORD_OFFSET(%edi)
+    lock cmpxchg  %edx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi)
     jnz  .Lretry_lock                     // cmpxchg failed retry
     ret
 .Lalready_thin:
@@ -881,21 +911,21 @@
     addl LITERAL(65536), %ecx             // increment recursion count
     test LITERAL(0xC0000000), %ecx        // overflowed if either of top two bits are set
     jne  .Lslow_lock                      // count overflowed so go slow
-    movl %ecx, LOCK_WORD_OFFSET(%edi)     // update lockword, cmpxchg not necessary as we hold lock
+    // update lockword, cmpxchg not necessary as we hold lock
+    movl %ecx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi)
     ret
 .Lslow_lock:
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
     movq %gs:THREAD_SELF_OFFSET, %rsi     // pass Thread::Current()
-    movq %rsp, %rdx                       // pass SP
-    call SYMBOL(artLockObjectFromCode)  // artLockObjectFromCode(object, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME    // restore frame up to return address
+    call SYMBOL(artLockObjectFromCode)    // artLockObjectFromCode(object, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME   // restore frame up to return address
     RETURN_IF_EAX_ZERO
 END_FUNCTION art_quick_lock_object
 
 DEFINE_FUNCTION art_quick_unlock_object
     testl %edi, %edi                      // null check object/edi
     jz   .Lslow_unlock
-    movl LOCK_WORD_OFFSET(%edi), %ecx     // ecx := lock word
+    movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx  // ecx := lock word
     movl %gs:THREAD_ID_OFFSET, %edx       // edx := thread id
     test LITERAL(0xC0000000), %ecx
     jnz  .Lslow_unlock                    // lock word contains a monitor
@@ -903,18 +933,17 @@
     jne  .Lslow_unlock
     cmpl LITERAL(65536), %ecx
     jae  .Lrecursive_thin_unlock
-    movl LITERAL(0), LOCK_WORD_OFFSET(%edi)
+    movl LITERAL(0), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi)
     ret
 .Lrecursive_thin_unlock:
     subl LITERAL(65536), %ecx
-    mov  %ecx, LOCK_WORD_OFFSET(%edi)
+    mov  %ecx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi)
     ret
 .Lslow_unlock:
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
     movq %gs:THREAD_SELF_OFFSET, %rsi     // pass Thread::Current()
-    movq %rsp, %rdx                       // pass SP
-    call SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME    // restore frame up to return address
+    call SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME   // restore frame up to return address
     RETURN_IF_EAX_ZERO
 END_FUNCTION art_quick_unlock_object
 
@@ -935,9 +964,8 @@
     POP rsi                           // Pop arguments
     POP rdi
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
-    mov %rsp, %rcx                    // pass SP
     mov %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
-    call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP)
+    call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*)
     int3                              // unreached
 END_FUNCTION art_quick_check_cast
 
@@ -969,8 +997,8 @@
     int3
     int3
 #else
-    movl ARRAY_LENGTH_OFFSET(%edi), %ecx
-//  movl ARRAY_LENGTH_OFFSET(%rdi), %ecx      // This zero-extends, so value(%rcx)=value(%ecx)
+    movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %ecx
+//  movl MIRROR_ARRAY_LENGTH_OFFSET(%rdi), %ecx  // This zero-extends, so value(%rcx)=value(%ecx)
     cmpl %ecx, %esi
     jb art_quick_aput_obj
     mov %esi, %edi
@@ -986,24 +1014,24 @@
     testl %edx, %edx                // store of null
 //  test %rdx, %rdx
     jz .Ldo_aput_null
-    movl CLASS_OFFSET(%edi), %ecx
-//  movq CLASS_OFFSET(%rdi), %rcx
-    movl CLASS_COMPONENT_TYPE_OFFSET(%ecx), %ecx
-//  movq CLASS_COMPONENT_TYPE_OFFSET(%rcx), %rcx
-    cmpl CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability
-//  cmpq CLASS_OFFSET(%rdx), %rcx
+    movl MIRROR_OBJECT_CLASS_OFFSET(%edi), %ecx
+//  movq MIRROR_OBJECT_CLASS_OFFSET(%rdi), %rcx
+    movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ecx), %ecx
+//  movq MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %rcx
+    cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability
+//  cmpq MIRROR_CLASS_OFFSET(%rdx), %rcx
     jne .Lcheck_assignability
 .Ldo_aput:
-    movl %edx, OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
-//  movq %rdx, OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
+    movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
+//  movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
     movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
     shrl LITERAL(7), %edi
 //  shrl LITERAL(7), %rdi
     movb %dl, (%rdx, %rdi)                       // Note: this assumes that top 32b of %rdi are zero
     ret
 .Ldo_aput_null:
-    movl %edx, OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
-//  movq %rdx, OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
+    movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
+//  movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
     ret
 .Lcheck_assignability:
     // Save arguments.
@@ -1015,7 +1043,7 @@
     SETUP_FP_CALLEE_SAVE_FRAME
 
                                   // "Uncompress" = do nothing, as already zero-extended on load.
-    movl CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class.
+    movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class.
     movq %rcx, %rdi               // Pass arg1 = array's component type.
 
     call SYMBOL(artIsAssignableFromCode)  // (Class* a, Class* b)
@@ -1032,8 +1060,8 @@
     POP  rsi
     POP  rdi
 
-    movl %edx, OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
-//  movq %rdx, OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
+    movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
+//  movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
     movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
     shrl LITERAL(7), %edi
 //  shrl LITERAL(7), %rdi
@@ -1052,12 +1080,10 @@
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // Save all registers as basis for long jump context.
 
     // Outgoing argument set up.
-    movq %rsp, %rcx                         // Pass arg 4 = SP.
     movq %rdx, %rsi                         // Pass arg 2 = value.
-    movq %gs:THREAD_SELF_OFFSET, %rdx // Pass arg 3 = Thread::Current().
+    movq %gs:THREAD_SELF_OFFSET, %rdx       // Pass arg 3 = Thread::Current().
                                             // Pass arg 1 = array.
-
-    call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP)
+    call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*)
     int3                          // unreached
 END_FUNCTION art_quick_aput_obj
 
@@ -1076,72 +1102,54 @@
 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_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
 
 // This is singled out as the argument order is different.
 DEFINE_FUNCTION art_quick_set64_static
-    movq %rsi, %rdx                    // pass new_val
-    movl 8(%rsp), %esi                 // pass referrer
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
-                                       // field_idx is in rdi
-    movq %gs:THREAD_SELF_OFFSET, %rcx  // pass Thread::Current()
-    movq %rsp, %r8                     // pass SP
-    call SYMBOL(artSet64StaticFromCode)  // (field_idx, referrer, new_val, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
-    RETURN_IF_EAX_ZERO                 // return or deliver exception
+    movq %rsi, %rdx                      // pass new_val
+    movl 8(%rsp), %esi                   // pass referrer
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+                                         // field_idx is in rdi
+    movq %gs:THREAD_SELF_OFFSET, %rcx    // pass Thread::Current()
+    call SYMBOL(artSet64StaticFromCode)  // (field_idx, referrer, new_val, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    RETURN_IF_EAX_ZERO                   // return or deliver exception
 END_FUNCTION art_quick_set64_static
 
 
 DEFINE_FUNCTION art_quick_proxy_invoke_handler
-    // Save callee and GPR args, mixed together to agree with core spills bitmap of ref. and args
-    // callee save frame.
-    PUSH r15  // Callee save.
-    PUSH r14  // Callee save.
-    PUSH r13  // Callee save.
-    PUSH r12  // Callee save.
-    PUSH r9   // Quick arg 5.
-    PUSH r8   // Quick arg 4.
-    PUSH rsi  // Quick arg 1.
-    PUSH rbp  // Callee save.
-    PUSH rbx  // Callee save.
-    PUSH rdx  // Quick arg 2.
-    PUSH rcx  // Quick arg 3.
-    // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*.
-    subq LITERAL(80 + 4*8), %rsp
-    CFI_ADJUST_CFA_OFFSET(80 + 4*8)
-    // Save FPRs.
-    movq %xmm0, 16(%rsp)
-    movq %xmm1, 24(%rsp)
-    movq %xmm2, 32(%rsp)
-    movq %xmm3, 40(%rsp)
-    movq %xmm4, 48(%rsp)
-    movq %xmm5, 56(%rsp)
-    movq %xmm6, 64(%rsp)
-    movq %xmm7, 72(%rsp)
-    movq %xmm12, 80(%rsp)
-    movq %xmm13, 88(%rsp)
-    movq %xmm14, 96(%rsp)
-    movq %xmm15, 104(%rsp)
-    // Store proxy method to bottom of stack.
-    movq %rdi, 0(%rsp)
-    movq %gs:THREAD_SELF_OFFSET, %rdx  // Pass Thread::Current().
-    movq %rsp, %rcx                    // Pass SP.
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI
+
+    movq %gs:THREAD_SELF_OFFSET, %rdx       // Pass Thread::Current().
+    movq %rsp, %rcx                         // Pass SP.
     call SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
-    movq %rax, %xmm0                   // Copy return value in case of float returns.
-    addq LITERAL(168 + 4*8), %rsp            // Pop arguments.
+    movq %rax, %xmm0                        // Copy return value in case of float returns.
+    addq LITERAL(168 + 4*8), %rsp           // Pop arguments.
     CFI_ADJUST_CFA_OFFSET(-168 - 4*8)
     RETURN_OR_DELIVER_PENDING_EXCEPTION
 END_FUNCTION art_quick_proxy_invoke_handler
@@ -1156,20 +1164,20 @@
     int3
 #else
     movl 8(%rsp), %edi            // load caller Method*
-    movl METHOD_DEX_CACHE_METHODS_OFFSET(%rdi), %edi  // load dex_cache_resolved_methods
-    movl OBJECT_ARRAY_DATA_OFFSET(%rdi, %rax, 4), %edi  // load the target method
+    movl MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET(%rdi), %edi  // load dex_cache_resolved_methods
+    movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rax, 4), %edi  // load the target method
     jmp art_quick_invoke_interface_trampoline
 #endif  // __APPLE__
 END_FUNCTION art_quick_imt_conflict_trampoline
 
 DEFINE_FUNCTION art_quick_resolution_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     movq %gs:THREAD_SELF_OFFSET, %rdx
     movq %rsp, %rcx
     call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)
     movq %rax, %r10               // Remember returned code pointer in R10.
     movq (%rsp), %rdi             // Load called method into RDI.
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     testq %r10, %r10              // If code pointer is NULL goto deliver pending exception.
     jz 1f
     jmp *%r10                     // Tail call into method.
@@ -1254,38 +1262,12 @@
     /*
      * Called to do a generic JNI down-call
      */
-DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline
-    // Save callee and GPR args, mixed together to agree with core spills bitmap.
-    PUSH r15  // Callee save.
-    PUSH r14  // Callee save.
-    PUSH r13  // Callee save.
-    PUSH r12  // Callee save.
-    PUSH r9   // Quick arg 5.
-    PUSH r8   // Quick arg 4.
-    PUSH rsi  // Quick arg 1.
-    PUSH rbp  // Callee save.
-    PUSH rbx  // Callee save.
-    PUSH rdx  // Quick arg 2.
-    PUSH rcx  // Quick arg 3.
-    // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*.
-    subq LITERAL(80 + 4*8), %rsp
-    CFI_ADJUST_CFA_OFFSET(80 + 4*8)
-    // Save FPRs.
-    movq %xmm0, 16(%rsp)
-    movq %xmm1, 24(%rsp)
-    movq %xmm2, 32(%rsp)
-    movq %xmm3, 40(%rsp)
-    movq %xmm4, 48(%rsp)
-    movq %xmm5, 56(%rsp)
-    movq %xmm6, 64(%rsp)
-    movq %xmm7, 72(%rsp)
-    movq %xmm12, 80(%rsp)
-    movq %xmm13, 88(%rsp)
-    movq %xmm14, 96(%rsp)
-    movq %xmm15, 104(%rsp)
-    movq %rdi, 0(%rsp)              // Store native ArtMethod* to bottom of stack.
+DEFINE_FUNCTION art_quick_generic_jni_trampoline
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI
+
     movq %rsp, %rbp                 // save SP at (old) callee-save frame
     CFI_DEF_CFA_REGISTER(rbp)
+
     //
     // reserve a lot of space
     //
@@ -1441,12 +1423,12 @@
      * RDI = method being called / to bridge to.
      * RSI, RDX, RCX, R8, R9 are arguments to that method.
      */
-DEFINE_FUNCTION_NO_HIDE art_quick_to_interpreter_bridge
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // Set up frame and save arguments.
+DEFINE_FUNCTION art_quick_to_interpreter_bridge
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME   // Set up frame and save arguments.
     movq %gs:THREAD_SELF_OFFSET, %rsi      // RSI := Thread::Current()
     movq %rsp, %rdx                        // RDX := sp
     call SYMBOL(artQuickToInterpreterBridge)  // (method, Thread*, SP)
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME  // TODO: no need to restore arguments in this case.
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME  // TODO: no need to restore arguments in this case.
     movq %rax, %xmm0                   // Place return value also into floating point return value.
     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
 END_FUNCTION art_quick_to_interpreter_bridge
@@ -1459,15 +1441,14 @@
     int3
     int3
 #else
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
 
     movq %rdi, %r12               // Preserve method pointer in a callee-save.
 
     movq %gs:THREAD_SELF_OFFSET, %rdx   // Pass thread.
-    movq %rsp, %rcx                     // Pass SP.
     movq FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp), %r8   // Pass return PC.
 
-    call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
+    call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, LR)
 
                                   // %rax = result of call.
     movq %r12, %rdi               // Reload method pointer.
@@ -1475,7 +1456,7 @@
     leaq art_quick_instrumentation_exit(%rip), %r12   // Set up return through instrumentation
     movq %r12, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp) // exit.
 
-    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
 
     jmp *%rax                     // Tail call to intended method.
 #endif  // __APPLE__
@@ -1484,7 +1465,7 @@
 DEFINE_FUNCTION art_quick_instrumentation_exit
     pushq LITERAL(0)          // Push a fake return PC as there will be none on the stack.
 
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
 
     // We need to save rax and xmm0. We could use a callee-save from SETUP_REF_ONLY, but then
     // we would need to fully restore it. As there are a good number of callee-save registers, it
@@ -1524,9 +1505,8 @@
     pushq %rsi                     // Fake that we were called. Use hidden arg.
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
                                    // Stack should be aligned now.
-    movq %rsp, %rsi                           // Pass SP.
     movq %gs:THREAD_SELF_OFFSET, %rdi         // Pass Thread.
-    call SYMBOL(artDeoptimize) // artDeoptimize(Thread*, SP)
+    call SYMBOL(artDeoptimize) // artDeoptimize(Thread*)
     int3                           // Unreachable.
 END_FUNCTION art_quick_deoptimize
 
@@ -1539,15 +1519,15 @@
      *    rsi:   comp string object (known non-null)
      */
 DEFINE_FUNCTION art_quick_string_compareto
-    movl STRING_COUNT_OFFSET(%edi), %r8d
-    movl STRING_COUNT_OFFSET(%esi), %r9d
-    movl STRING_VALUE_OFFSET(%edi), %r10d
-    movl STRING_VALUE_OFFSET(%esi), %r11d
-    movl STRING_OFFSET_OFFSET(%edi), %eax
-    movl STRING_OFFSET_OFFSET(%esi), %ecx
+    movl MIRROR_STRING_COUNT_OFFSET(%edi), %r8d
+    movl MIRROR_STRING_COUNT_OFFSET(%esi), %r9d
+    movl MIRROR_STRING_VALUE_OFFSET(%edi), %r10d
+    movl MIRROR_STRING_VALUE_OFFSET(%esi), %r11d
+    movl MIRROR_STRING_OFFSET_OFFSET(%edi), %eax
+    movl MIRROR_STRING_OFFSET_OFFSET(%esi), %ecx
     /* Build pointers to the start of string data */
-    leal STRING_DATA_OFFSET(%r10d, %eax, 2), %esi
-    leal STRING_DATA_OFFSET(%r11d, %ecx, 2), %edi
+    leal MIRROR_CHAR_ARRAY_DATA_OFFSET(%r10d, %eax, 2), %esi
+    leal MIRROR_CHAR_ARRAY_DATA_OFFSET(%r11d, %ecx, 2), %edi
     /* Calculate min length and count diff */
     movl  %r8d, %ecx
     movl  %r8d, %eax
@@ -1593,5 +1573,3 @@
     call PLT_SYMBOL(longjmp)
     int3                            // won't get here
 END_FUNCTION art_nested_signal_return
-
-
diff --git a/runtime/arch/x86_64/thread_x86_64.cc b/runtime/arch/x86_64/thread_x86_64.cc
index 6dff2b4..553b656 100644
--- a/runtime/arch/x86_64/thread_x86_64.cc
+++ b/runtime/arch/x86_64/thread_x86_64.cc
@@ -49,29 +49,16 @@
 
   // Sanity check that reads from %gs point to this Thread*.
   Thread* self_check;
-  CHECK_EQ(THREAD_SELF_OFFSET, SelfOffset<8>().Int32Value());
   __asm__ __volatile__("movq %%gs:(%1), %0"
       : "=r"(self_check)  // output
       : "r"(THREAD_SELF_OFFSET)  // input
       :);  // clobber
   CHECK_EQ(self_check, this);
-
-  // Sanity check other offsets.
-  CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET),
-           Runtime::GetCalleeSaveMethodOffset(Runtime::kSaveAll));
-  CHECK_EQ(static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET),
-           Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsOnly));
-  CHECK_EQ(static_cast<size_t>(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET),
-           Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsAndArgs));
-  CHECK_EQ(THREAD_EXCEPTION_OFFSET, ExceptionOffset<8>().Int32Value());
-  CHECK_EQ(THREAD_CARD_TABLE_OFFSET, CardTableOffset<8>().Int32Value());
-  CHECK_EQ(THREAD_ID_OFFSET, ThinLockIdOffset<8>().Int32Value());
 }
 
 void Thread::CleanupCpu() {
   // Sanity check that reads from %gs point to this Thread*.
   Thread* self_check;
-  CHECK_EQ(THREAD_SELF_OFFSET, SelfOffset<8>().Int32Value());
   __asm__ __volatile__("movq %%gs:(%1), %0"
       : "=r"(self_check)  // output
       : "r"(THREAD_SELF_OFFSET)  // input
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index b36b1b7..bedef58 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -17,61 +17,147 @@
 #ifndef ART_RUNTIME_ASM_SUPPORT_H_
 #define ART_RUNTIME_ASM_SUPPORT_H_
 
+#if defined(__cplusplus)
+#include "mirror/art_method.h"
+#include "mirror/class.h"
+#include "mirror/string.h"
+#include "runtime.h"
+#include "thread.h"
+#endif
+
 #include "read_barrier_c.h"
 
-// Value loaded into rSUSPEND for quick. When this value is counted down to zero we do a suspend
-// check.
-#define SUSPEND_CHECK_INTERVAL (96)
+#if defined(__arm__) ||  defined(__aarch64__) || defined(__mips__)
+// In quick code for ARM, ARM64 and MIPS we make poor use of registers and perform frequent suspend
+// checks in the event of loop back edges. The SUSPEND_CHECK_INTERVAL constant is loaded into a
+// register at the point of an up-call or after handling a suspend check. It reduces the number of
+// loads of the TLS suspend check value by the given amount (turning it into a decrement and compare
+// of a register). This increases the time for a thread to respond to requests from GC and the
+// debugger, damaging GC performance and creating other unwanted artifacts. For example, this count
+// has the effect of making loops and Java code look cold in profilers, where the count is reset
+// impacts where samples will occur. Reducing the count as much as possible improves profiler
+// accuracy in tools like traceview.
+// TODO: get a compiler that can do a proper job of loop optimization and remove this.
+#define SUSPEND_CHECK_INTERVAL 96
+#endif
+
+#if defined(__cplusplus)
+
+#ifndef ADD_TEST_EQ  // Allow #include-r to replace with their own.
+#define ADD_TEST_EQ(x, y) CHECK_EQ(x, y);
+#endif
+
+static inline void CheckAsmSupportOffsetsAndSizes() {
+#else
+#define ADD_TEST_EQ(x, y)
+#endif
+
+// Size of references to the heap on the stack.
+#define STACK_REFERENCE_SIZE 4
+ADD_TEST_EQ(static_cast<size_t>(STACK_REFERENCE_SIZE), sizeof(art::StackReference<art::mirror::Object>))
+
+// Note: these callee save methods loads require read barriers.
+// Offset of field Runtime::callee_save_methods_[kSaveAll]
+#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0
+ADD_TEST_EQ(static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET),
+            art::Runtime::GetCalleeSaveMethodOffset(art::Runtime::kSaveAll))
+
+// Offset of field Runtime::callee_save_methods_[kRefsOnly]
+#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET __SIZEOF_POINTER__
+ADD_TEST_EQ(static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET),
+            art::Runtime::GetCalleeSaveMethodOffset(art::Runtime::kRefsOnly))
+
+// Offset of field Runtime::callee_save_methods_[kRefsAndArgs]
+#define RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET (2 * __SIZEOF_POINTER__)
+ADD_TEST_EQ(static_cast<size_t>(RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET),
+            art::Runtime::GetCalleeSaveMethodOffset(art::Runtime::kRefsAndArgs))
+
+// Offset of field Thread::tls32_.state_and_flags.
+#define THREAD_FLAGS_OFFSET 0
+ADD_TEST_EQ(THREAD_FLAGS_OFFSET,
+            art::Thread::ThreadFlagsOffset<__SIZEOF_POINTER__>().Int32Value())
+
+// Offset of field Thread::tls32_.thin_lock_thread_id.
+#define THREAD_ID_OFFSET 12
+ADD_TEST_EQ(THREAD_ID_OFFSET,
+            art::Thread::ThinLockIdOffset<__SIZEOF_POINTER__>().Int32Value())
+
+// Offset of field Thread::tlsPtr_.card_table.
+#define THREAD_CARD_TABLE_OFFSET 120
+ADD_TEST_EQ(THREAD_CARD_TABLE_OFFSET,
+            art::Thread::CardTableOffset<__SIZEOF_POINTER__>().Int32Value())
+
+// Offset of field Thread::tlsPtr_.exception.
+#define THREAD_EXCEPTION_OFFSET (THREAD_CARD_TABLE_OFFSET + __SIZEOF_POINTER__)
+ADD_TEST_EQ(THREAD_EXCEPTION_OFFSET,
+            art::Thread::ExceptionOffset<__SIZEOF_POINTER__>().Int32Value())
+
+// Offset of field Thread::tlsPtr_.managed_stack.top_quick_frame_.
+#define THREAD_TOP_QUICK_FRAME_OFFSET (THREAD_CARD_TABLE_OFFSET + (3 * __SIZEOF_POINTER__))
+ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET,
+            art::Thread::TopOfManagedStackOffset<__SIZEOF_POINTER__>().Int32Value())
+
+// Offset of field Thread::tlsPtr_.managed_stack.top_quick_frame_.
+#define THREAD_SELF_OFFSET (THREAD_CARD_TABLE_OFFSET + (8 * __SIZEOF_POINTER__))
+ADD_TEST_EQ(THREAD_SELF_OFFSET,
+            art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value())
 
 // Offsets within java.lang.Object.
-#define CLASS_OFFSET 0
-#define LOCK_WORD_OFFSET 4
+#define MIRROR_OBJECT_CLASS_OFFSET 0
+ADD_TEST_EQ(MIRROR_OBJECT_CLASS_OFFSET, art::mirror::Object::ClassOffset().Int32Value())
+#define MIRROR_OBJECT_LOCK_WORD_OFFSET 4
+ADD_TEST_EQ(MIRROR_OBJECT_LOCK_WORD_OFFSET, art::mirror::Object::MonitorOffset().Int32Value())
 
-#if !defined(USE_BAKER_OR_BROOKS_READ_BARRIER)
+#if defined(USE_BAKER_OR_BROOKS_READ_BARRIER)
+#define MIRROR_OBJECT_HEADER_SIZE 16
+#else
+#define MIRROR_OBJECT_HEADER_SIZE 8
+#endif
+ADD_TEST_EQ(size_t(MIRROR_OBJECT_HEADER_SIZE), sizeof(art::mirror::Object))
 
 // Offsets within java.lang.Class.
-#define CLASS_COMPONENT_TYPE_OFFSET 12
+#define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET,
+            art::mirror::Class::ComponentTypeOffset().Int32Value())
 
 // Array offsets.
-#define ARRAY_LENGTH_OFFSET 8
-#define OBJECT_ARRAY_DATA_OFFSET 12
+#define MIRROR_ARRAY_LENGTH_OFFSET      MIRROR_OBJECT_HEADER_SIZE
+ADD_TEST_EQ(MIRROR_ARRAY_LENGTH_OFFSET, art::mirror::Array::LengthOffset().Int32Value())
+
+#define MIRROR_CHAR_ARRAY_DATA_OFFSET   (4 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_CHAR_ARRAY_DATA_OFFSET,
+            art::mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value())
+
+#define MIRROR_OBJECT_ARRAY_DATA_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_OBJECT_ARRAY_DATA_OFFSET,
+    art::mirror::Array::DataOffset(
+        sizeof(art::mirror::HeapReference<art::mirror::Object>)).Int32Value())
 
 // Offsets within java.lang.String.
-#define STRING_VALUE_OFFSET 8
-#define STRING_COUNT_OFFSET 12
-#define STRING_OFFSET_OFFSET 20
-#define STRING_DATA_OFFSET 12
+#define MIRROR_STRING_VALUE_OFFSET  MIRROR_OBJECT_HEADER_SIZE
+ADD_TEST_EQ(MIRROR_STRING_VALUE_OFFSET, art::mirror::String::ValueOffset().Int32Value())
 
-// 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
+#define MIRROR_STRING_COUNT_OFFSET  (4 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_STRING_COUNT_OFFSET, art::mirror::String::CountOffset().Int32Value())
 
-#else
+#define MIRROR_STRING_OFFSET_OFFSET (12 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_STRING_OFFSET_OFFSET, art::mirror::String::OffsetOffset().Int32Value())
 
-// Offsets within java.lang.Class.
-#define CLASS_COMPONENT_TYPE_OFFSET 20
+// Offsets within java.lang.reflect.ArtMethod.
+#define MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET,
+            art::mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())
 
-// Array offsets.
-#define ARRAY_LENGTH_OFFSET 16
-#define OBJECT_ARRAY_DATA_OFFSET 20
+#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET     (32 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET,
+            art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value())
 
-// Offsets within java.lang.String.
-#define STRING_VALUE_OFFSET 16
-#define STRING_COUNT_OFFSET 20
-#define STRING_OFFSET_OFFSET 28
-#define STRING_DATA_OFFSET 20
+#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET        (40 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET,
+            art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())
 
-// Offsets within java.lang.Method.
-#define METHOD_DEX_CACHE_METHODS_OFFSET 20
-#define METHOD_PORTABLE_CODE_OFFSET 48
-#define METHOD_QUICK_CODE_OFFSET 56
-
-#endif  // USE_BAKER_OR_BROOKS_READ_BARRIER
+#if defined(__cplusplus)
+}  // End of CheckAsmSupportOffsets.
+#endif
 
 #endif  // ART_RUNTIME_ASM_SUPPORT_H_
diff --git a/runtime/atomic.h b/runtime/atomic.h
index e57c0c0..cf61277 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -293,17 +293,17 @@
 
 typedef Atomic<int32_t> AtomicInteger;
 
-COMPILE_ASSERT(sizeof(AtomicInteger) == sizeof(int32_t), weird_atomic_int_size);
-COMPILE_ASSERT(alignof(AtomicInteger) == alignof(int32_t),
-               atomic_int_alignment_differs_from_that_of_underlying_type);
-COMPILE_ASSERT(sizeof(Atomic<int64_t>) == sizeof(int64_t), weird_atomic_int64_size);
+static_assert(sizeof(AtomicInteger) == sizeof(int32_t), "Weird AtomicInteger size");
+static_assert(alignof(AtomicInteger) == alignof(int32_t),
+              "AtomicInteger alignment differs from that of underlyingtype");
+static_assert(sizeof(Atomic<int64_t>) == sizeof(int64_t), "Weird Atomic<int64> size");
 
 // Assert the alignment of 64-bit integers is 64-bit. This isn't true on certain 32-bit
 // architectures (e.g. x86-32) but we know that 64-bit integers here are arranged to be 8-byte
 // aligned.
 #if defined(__LP64__)
-  COMPILE_ASSERT(alignof(Atomic<int64_t>) == alignof(int64_t),
-                 atomic_int64_alignment_differs_from_that_of_underlying_type);
+  static_assert(alignof(Atomic<int64_t>) == alignof(int64_t),
+                "Atomic<int64> alignment differs from that of underlying type");
 #endif
 
 }  // namespace art
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 5f43bec..5a8fbb3 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -23,7 +23,7 @@
 
 Barrier::Barrier(int count)
     : count_(count),
-      lock_("GC barrier lock"),
+      lock_("GC barrier lock", kThreadSuspendCountLock),
       condition_("GC barrier condition", lock_) {
 }
 
@@ -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..5ca88e8 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_);
@@ -49,7 +50,7 @@
   // Counter, when this reaches 0 all people blocked on the barrier are signalled.
   int count_ GUARDED_BY(lock_);
 
-  Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  Mutex lock_ ACQUIRED_AFTER(Locks::abort_lock_);
   ConditionVariable condition_ GUARDED_BY(lock_);
 };
 
diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc
index 39d51a5..4f2fc07 100644
--- a/runtime/base/allocator.cc
+++ b/runtime/base/allocator.cc
@@ -25,20 +25,16 @@
 
 namespace art {
 
-Atomic<uint64_t> TrackedAllocators::bytes_used_[kAllocatorTagCount];
-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() {}
 
-  virtual void* Alloc(size_t size) {
+  void* Alloc(size_t size) {
     return calloc(sizeof(uint8_t), size);
   }
 
-  virtual void Free(void* p) {
+  void Free(void* p) {
     free(p);
   }
 
@@ -48,18 +44,20 @@
 
 MallocAllocator g_malloc_allocator;
 
-class NoopAllocator : public Allocator {
+class NoopAllocator FINAL : public Allocator {
  public:
   explicit NoopAllocator() {}
   ~NoopAllocator() {}
 
-  virtual void* Alloc(size_t size) {
+  void* Alloc(size_t size) {
+    UNUSED(size);
     LOG(FATAL) << "NoopAllocator::Alloc should not be called";
-    return NULL;
+    UNREACHABLE();
   }
 
-  virtual void Free(void* p) {
+  void Free(void* p) {
     // Noop.
+    UNUSED(p);
   }
 
  private:
@@ -76,13 +74,20 @@
   return &g_noop_allocator;
 }
 
-void TrackedAllocators::Dump(std::ostream& os) {
+namespace TrackedAllocators {
+
+// These globals are safe since they don't have any non-trivial destructors.
+Atomic<size_t> g_bytes_used[kAllocatorTagCount];
+volatile size_t g_max_bytes_used[kAllocatorTagCount];
+Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount];
+
+void Dump(std::ostream& os) {
   if (kEnableTrackingAllocator) {
     os << "Dumping native memory usage\n";
     for (size_t i = 0; i < kAllocatorTagCount; ++i) {
-      uint64_t bytes_used = bytes_used_[i].LoadRelaxed();
-      uint64_t max_bytes_used = max_bytes_used_[i].LoadRelaxed();
-      uint64_t total_bytes_used = total_bytes_used_[i].LoadRelaxed();
+      uint64_t bytes_used = g_bytes_used[i].LoadRelaxed();
+      uint64_t max_bytes_used = g_max_bytes_used[i];
+      uint64_t total_bytes_used = g_total_bytes_used[i].LoadRelaxed();
       if (total_bytes_used != 0) {
         os << static_cast<AllocatorTag>(i) << " active=" << bytes_used << " max="
            << max_bytes_used << " total=" << total_bytes_used << "\n";
@@ -91,4 +96,6 @@
   }
 }
 
+}  // namespace TrackedAllocators
+
 }  // namespace art
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index da5ac29..5a09c96 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -22,7 +22,7 @@
 #include "atomic.h"
 #include "base/macros.h"
 #include "base/mutex.h"
-#include "utils.h"
+#include "base/type_static_if.h"
 
 namespace art {
 
@@ -66,30 +66,40 @@
   kAllocatorTagCompileTimeClassPath,
   kAllocatorTagOatFile,
   kAllocatorTagDexFileVerifier,
+  kAllocatorTagRosAlloc,
   kAllocatorTagCount,  // Must always be last element.
 };
 std::ostream& operator<<(std::ostream& os, const AllocatorTag& tag);
 
-class TrackedAllocators {
- public:
-  static bool Add(uint32_t tag, AtomicInteger* bytes_used);
-  static void Dump(std::ostream& os);
-  static void RegisterAllocation(AllocatorTag tag, uint64_t bytes) {
-    total_bytes_used_[tag].FetchAndAddSequentiallyConsistent(bytes);
-    uint64_t new_bytes = bytes_used_[tag].FetchAndAddSequentiallyConsistent(bytes) + bytes;
-    max_bytes_used_[tag].StoreRelaxed(std::max(max_bytes_used_[tag].LoadRelaxed(), new_bytes));
-  }
-  static void RegisterFree(AllocatorTag tag, uint64_t bytes) {
-    bytes_used_[tag].FetchAndSubSequentiallyConsistent(bytes);
-  }
+namespace TrackedAllocators {
 
- private:
-  static Atomic<uint64_t> bytes_used_[kAllocatorTagCount];
-  static Atomic<uint64_t> max_bytes_used_[kAllocatorTagCount];
-  static Atomic<uint64_t> total_bytes_used_[kAllocatorTagCount];
-};
+// Running count of number of bytes used for this kind of allocation. Increased by allocations,
+// decreased by deallocations.
+extern Atomic<size_t> g_bytes_used[kAllocatorTagCount];
 
-// Tracking allocator, tracks how much memory is used.
+// Largest value of bytes used seen.
+extern volatile size_t g_max_bytes_used[kAllocatorTagCount];
+
+// Total number of bytes allocated of this kind.
+extern Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount];
+
+void Dump(std::ostream& os);
+
+inline void RegisterAllocation(AllocatorTag tag, size_t bytes) {
+  g_total_bytes_used[tag].FetchAndAddSequentiallyConsistent(bytes);
+  size_t new_bytes = g_bytes_used[tag].FetchAndAddSequentiallyConsistent(bytes) + bytes;
+  if (g_max_bytes_used[tag] < new_bytes) {
+    g_max_bytes_used[tag] = new_bytes;
+  }
+}
+
+inline void RegisterFree(AllocatorTag tag, size_t bytes) {
+  g_bytes_used[tag].FetchAndSubSequentiallyConsistent(bytes);
+}
+
+}  // namespace TrackedAllocators
+
+// Tracking allocator for use with STL types, tracks how much memory is used.
 template<class T, AllocatorTag kTag>
 class TrackingAllocatorImpl : public std::allocator<T> {
  public:
@@ -104,11 +114,12 @@
   // Used internally by STL data structures.
   template <class U>
   TrackingAllocatorImpl(const TrackingAllocatorImpl<U, kTag>& alloc) throw() {
+    UNUSED(alloc);
   }
 
   // Used internally by STL data structures.
   TrackingAllocatorImpl() throw() {
-    COMPILE_ASSERT(kTag < kAllocatorTagCount, must_be_less_than_count);
+    static_assert(kTag < kAllocatorTagCount, "kTag must be less than kAllocatorTagCount");
   }
 
   // Enables an allocator for objects of one type to allocate storage for objects of another type.
@@ -119,6 +130,7 @@
   };
 
   pointer allocate(size_type n, const_pointer hint = 0) {
+    UNUSED(hint);
     const size_t size = n * sizeof(T);
     TrackedAllocators::RegisterAllocation(GetTag(), size);
     return reinterpret_cast<pointer>(malloc(size));
@@ -131,7 +143,7 @@
     free(p);
   }
 
-  static AllocatorTag GetTag() {
+  static constexpr AllocatorTag GetTag() {
     return kTag;
   }
 };
@@ -149,6 +161,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_field.h b/runtime/base/bit_field.h
index e041bd0..fd65d50 100644
--- a/runtime/base/bit_field.h
+++ b/runtime/base/bit_field.h
@@ -22,7 +22,7 @@
 
 namespace art {
 
-static const uword kUwordOne = 1U;
+static constexpr uintptr_t kUintPtrTOne = 1U;
 
 // BitField is a template for encoding and decoding a bit field inside
 // an unsigned machine word.
@@ -31,18 +31,18 @@
  public:
   // Tells whether the provided value fits into the bit field.
   static bool IsValid(T value) {
-    return (static_cast<uword>(value) & ~((kUwordOne << size) - 1)) == 0;
+    return (static_cast<uintptr_t>(value) & ~((kUintPtrTOne << size) - 1)) == 0;
   }
 
   // Returns a uword mask of the bit field.
-  static uword Mask() {
-    return (kUwordOne << size) - 1;
+  static uintptr_t Mask() {
+    return (kUintPtrTOne << size) - 1;
   }
 
   // Returns a uword mask of the bit field which can be applied directly to
   // the raw unshifted bits.
-  static uword MaskInPlace() {
-    return ((kUwordOne << size) - 1) << position;
+  static uintptr_t MaskInPlace() {
+    return ((kUintPtrTOne << size) - 1) << position;
   }
 
   // Returns the shift count needed to right-shift the bit field to
@@ -57,22 +57,22 @@
   }
 
   // Returns a uword with the bit field value encoded.
-  static uword Encode(T value) {
+  static uintptr_t Encode(T value) {
     DCHECK(IsValid(value));
-    return static_cast<uword>(value) << position;
+    return static_cast<uintptr_t>(value) << position;
   }
 
   // Extracts the bit field from the value.
-  static T Decode(uword value) {
-    return static_cast<T>((value >> position) & ((kUwordOne << size) - 1));
+  static T Decode(uintptr_t value) {
+    return static_cast<T>((value >> position) & ((kUintPtrTOne << size) - 1));
   }
 
   // Returns a uword with the bit field value encoded based on the
   // original value. Only the bits corresponding to this bit field
   // will be changed.
-  static uword Update(T value, uword original) {
+  static uintptr_t Update(T value, uintptr_t original) {
     DCHECK(IsValid(value));
-    return (static_cast<uword>(value) << position) |
+    return (static_cast<uintptr_t>(value) << position) |
         (~MaskInPlace() & original);
   }
 };
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..4390180 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -16,20 +16,17 @@
 
 #include "bit_vector.h"
 
+#include <limits>
+#include <sstream>
+
+#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,12 +37,12 @@
                      Allocator* allocator,
                      uint32_t storage_size,
                      uint32_t* storage)
-  : allocator_(allocator),
-    expandable_(expandable),
+  : storage_(storage),
     storage_size_(storage_size),
-    storage_(storage) {
-  COMPILE_ASSERT(sizeof(*storage_) == kWordBytes, check_word_bytes);
-  COMPILE_ASSERT(sizeof(*storage_) * 8u == kWordBits, check_word_bits);
+    allocator_(allocator),
+    expandable_(expandable) {
+  static_assert(sizeof(*storage_) == kWordBytes, "word bytes");
+  static_assert(sizeof(*storage_) * 8u == kWordBits, "word bits");
   if (storage_ == nullptr) {
     storage_size_ = BitsToWords(start_bits);
     storage_ = static_cast<uint32_t*>(allocator_->Alloc(storage_size_ * kWordBytes));
@@ -56,59 +53,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 +79,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 +99,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 +116,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);
@@ -208,10 +148,7 @@
 
   // Is the storage size smaller than src's?
   if (storage_size_ < union_with_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,14 +218,14 @@
 
   uint32_t idx;
   // We can set every storage element with -1.
-  for (idx = 0; idx < (num_bits >> 5); idx++) {
-    storage_[idx] = -1;
+  for (idx = 0; idx < WordIndex(num_bits); idx++) {
+    storage_[idx] = std::numeric_limits<uint32_t>::max();
   }
 
   // Handle the potentially last few bits.
   uint32_t rem_num_bits = num_bits & 0x1f;
   if (rem_num_bits != 0) {
-    storage_[idx] = (1 << rem_num_bits) - 1;
+    storage_[idx] = (1U << rem_num_bits) - 1;
     ++idx;
   }
 
@@ -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,27 @@
   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);
+    // TODO: collect stats on space wasted because of resize.
+
+    // Free old storage.
+    allocator_->Free(storage_);
+
+    // Set fields.
+    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..31fd0e7 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 {
@@ -140,4 +141,30 @@
   EXPECT_EQ(64u, bv.NumSetBits());
 }
 
+TEST(BitVector, UnionIfNotIn) {
+  {
+    BitVector first(2, true, Allocator::GetMallocAllocator());
+    BitVector second(5, true, Allocator::GetMallocAllocator());
+    BitVector third(5, true, Allocator::GetMallocAllocator());
+
+    second.SetBit(64);
+    third.SetBit(64);
+    bool changed = first.UnionIfNotIn(&second, &third);
+    EXPECT_EQ(0u, first.NumSetBits());
+    EXPECT_FALSE(changed);
+  }
+
+  {
+    BitVector first(2, true, Allocator::GetMallocAllocator());
+    BitVector second(5, true, Allocator::GetMallocAllocator());
+    BitVector third(5, true, Allocator::GetMallocAllocator());
+
+    second.SetBit(64);
+    bool changed = first.UnionIfNotIn(&second, &third);
+    EXPECT_EQ(1u, first.NumSetBits());
+    EXPECT_TRUE(changed);
+    EXPECT_TRUE(first.IsBitSet(64));
+  }
+}
+
 }  // namespace art
diff --git a/runtime/base/casts.h b/runtime/base/casts.h
index be94c2e..c7e39a2 100644
--- a/runtime/base/casts.h
+++ b/runtime/base/casts.h
@@ -19,6 +19,8 @@
 
 #include <assert.h>
 #include <string.h>
+#include <type_traits>
+
 #include "base/macros.h"
 
 namespace art {
@@ -65,16 +67,9 @@
 
 template<typename To, typename From>     // use like this: down_cast<T*>(foo);
 inline To down_cast(From* f) {                   // so we only accept pointers
-  // Ensures that To is a sub-type of From *.  This test is here only
-  // for compile-time type checking, and has no overhead in an
-  // optimized build at run-time, as it will be optimized away
-  // completely.
-  if (false) {
-    implicit_cast<From*, To>(0);
-  }
+  static_assert(std::is_base_of<From, typename std::remove_pointer<To>::type>::value,
+                "down_cast unsafe as To is not a subtype of From");
 
-  //
-  // assert(f == NULL || dynamic_cast<To>(f) != NULL);  // RTTI: debug mode only!
   return static_cast<To>(f);
 }
 
@@ -82,7 +77,7 @@
 inline Dest bit_cast(const Source& source) {
   // Compile time assertion: sizeof(Dest) == sizeof(Source)
   // A compile error here means your Dest and Source have different sizes.
-  COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), verify_sizes_are_equal);
+  static_assert(sizeof(Dest) == sizeof(Source), "sizes should be equal");
   Dest dest;
   memcpy(&dest, &source, sizeof(dest));
   return dest;
diff --git a/runtime/base/dumpable.h b/runtime/base/dumpable.h
new file mode 100644
index 0000000..3c316cc
--- /dev/null
+++ b/runtime/base/dumpable.h
@@ -0,0 +1,53 @@
+/*
+ * 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_BASE_DUMPABLE_H_
+#define ART_RUNTIME_BASE_DUMPABLE_H_
+
+#include "base/macros.h"
+
+namespace art {
+
+// A convenience to allow any class with a "Dump(std::ostream& os)" member function
+// but without an operator<< to be used as if it had an operator<<. Use like this:
+//
+//   os << Dumpable<MyType>(my_type_instance);
+//
+template<typename T>
+class Dumpable FINAL {
+ public:
+  explicit Dumpable(const T& value) : value_(value) {
+  }
+
+  void Dump(std::ostream& os) const {
+    value_.Dump(os);
+  }
+
+ private:
+  const T& value_;
+
+  DISALLOW_COPY_AND_ASSIGN(Dumpable);
+};
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const Dumpable<T>& rhs) {
+  rhs.Dump(os);
+  return os;
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_DUMPABLE_H_
diff --git a/runtime/base/hex_dump.cc b/runtime/base/hex_dump.cc
index 936c52b..5423ff0 100644
--- a/runtime/base/hex_dump.cc
+++ b/runtime/base/hex_dump.cc
@@ -35,7 +35,7 @@
   static const char gHexDigit[] = "0123456789abcdef";
   const unsigned char* addr = reinterpret_cast<const unsigned char*>(address_);
   // 01234560: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef
-  char out[(kBitsPerWord / 4) + /* offset */
+  char out[(kBitsPerIntPtrT / 4) + /* offset */
            1 + /* colon */
            (16 * 3) + /* 16 hex digits and space */
            2 + /* white space */
@@ -49,7 +49,7 @@
     offset = 0;
   }
   memset(out, ' ', sizeof(out)-1);
-  out[kBitsPerWord / 4] = ':';
+  out[kBitsPerIntPtrT / 4] = ':';
   out[sizeof(out)-1] = '\0';
 
   size_t byte_count = byte_count_;
@@ -58,11 +58,11 @@
     size_t line_offset = offset & ~0x0f;
 
     char* hex = out;
-    char* asc = out + (kBitsPerWord / 4) + /* offset */ 1 + /* colon */
+    char* asc = out + (kBitsPerIntPtrT / 4) + /* offset */ 1 + /* colon */
         (16 * 3) + /* 16 hex digits and space */ 2 /* white space */;
 
-    for (int i = 0; i < (kBitsPerWord / 4); i++) {
-      *hex++ = gHexDigit[line_offset >> (kBitsPerWord - 4)];
+    for (int i = 0; i < (kBitsPerIntPtrT / 4); i++) {
+      *hex++ = gHexDigit[line_offset >> (kBitsPerIntPtrT - 4)];
       line_offset <<= 4;
     }
     hex++;
diff --git a/runtime/base/hex_dump_test.cc b/runtime/base/hex_dump_test.cc
index 3d782b2..bfd5c75 100644
--- a/runtime/base/hex_dump_test.cc
+++ b/runtime/base/hex_dump_test.cc
@@ -56,7 +56,7 @@
   std::ostringstream oss;
   oss << HexDump(&g16byte_aligned_number, 8, true, "");
   // Compare ignoring pointer.
-  EXPECT_STREQ(oss.str().c_str() + (kBitsPerWord / 4),
+  EXPECT_STREQ(oss.str().c_str() + (kBitsPerIntPtrT / 4),
                ": 68 67 66 65 64 63 62 61                          hgfedcba        ");
 }
 
diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h
index 4c18ce4..b329a31 100644
--- a/runtime/base/histogram-inl.h
+++ b/runtime/base/histogram-inl.h
@@ -195,6 +195,11 @@
   DCHECK_LE(std::abs(out_data->perc_.back() - 1.0), 0.001);
 }
 
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wfloat-equal"
+#endif
+
 template <class Value>
 inline double Histogram<Value>::Percentile(double per, const CumulativeData& data) const {
   DCHECK_GT(data.perc_.size(), 0ull);
@@ -235,6 +240,10 @@
   return value;
 }
 
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
 }  // namespace art
 #endif  // ART_RUNTIME_BASE_HISTOGRAM_INL_H_
 
diff --git a/runtime/base/histogram_test.cc b/runtime/base/histogram_test.cc
index 454f2ab..7aa5f90 100644
--- a/runtime/base/histogram_test.cc
+++ b/runtime/base/histogram_test.cc
@@ -41,14 +41,14 @@
     hist->AddValue(static_cast<uint64_t>(50));
   }
   mean = hist->Mean();
-  EXPECT_EQ(mean, 50);
+  EXPECT_DOUBLE_EQ(mean, 50.0);
   hist->Reset();
   hist->AddValue(9);
   hist->AddValue(17);
   hist->AddValue(28);
   hist->AddValue(28);
   mean = hist->Mean();
-  EXPECT_EQ(20.5, mean);
+  EXPECT_DOUBLE_EQ(20.5, mean);
 }
 
 TEST(Histtest, VarianceTest) {
@@ -60,7 +60,7 @@
   hist->AddValue(28);
   hist->AddValue(28);
   variance = hist->Variance();
-  EXPECT_EQ(64.25, variance);
+  EXPECT_DOUBLE_EQ(64.25, variance);
 }
 
 TEST(Histtest, Percentile) {
@@ -236,7 +236,7 @@
   }
   hist->CreateHistogram(&data);
   per_995 = hist->Percentile(0.995, data);
-  EXPECT_EQ(per_995, 0);
+  EXPECT_DOUBLE_EQ(per_995, 0.0);
   hist->Reset();
   for (size_t idx = 0; idx < 200; idx++) {
     for (uint64_t val = 1ull; val <= 4ull; val++) {
@@ -246,8 +246,8 @@
   hist->CreateHistogram(&data);
   per_005 = hist->Percentile(0.005, data);
   per_995 = hist->Percentile(0.995, data);
-  EXPECT_EQ(1, per_005);
-  EXPECT_EQ(4, per_995);
+  EXPECT_DOUBLE_EQ(1.0, per_005);
+  EXPECT_DOUBLE_EQ(4.0, per_995);
 }
 
 TEST(Histtest, SpikyValues) {
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index b2ad1d0..b781d60 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -16,17 +16,25 @@
 
 #include "logging.h"
 
+#include <sstream>
+
 #include "base/mutex.h"
 #include "runtime.h"
 #include "thread-inl.h"
 #include "utils.h"
 
+// Headers for LogMessage::LogLine.
+#ifdef HAVE_ANDROID_OS
+#include "cutils/log.h"
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
 namespace art {
 
 LogVerbosity gLogVerbosity;
 
-std::vector<std::string> gVerboseMethods;
-
 unsigned int gAborting = 0;
 
 static LogSeverity gMinimumLogSeverity = INFO;
@@ -47,14 +55,6 @@
                                                         : "art";
 }
 
-// Configure logging based on ANDROID_LOG_TAGS environment variable.
-// We need to parse a string that looks like
-//
-//      *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
-//
-// The tag (or '*' for the global level) comes first, followed by a colon
-// and a letter indicating the minimum priority level we're expected to log.
-// This can be used to reveal or conceal logs with specific tags.
 void InitLogging(char* argv[]) {
   if (gCmdLine.get() != nullptr) {
     return;
@@ -65,27 +65,27 @@
   // Stash the command line for later use. We can use /proc/self/cmdline on Linux to recover this,
   // but we don't have that luxury on the Mac, and there are a couple of argv[0] variants that are
   // commonly used.
-  if (argv != NULL) {
+  if (argv != nullptr) {
     gCmdLine.reset(new std::string(argv[0]));
-    for (size_t i = 1; argv[i] != NULL; ++i) {
+    for (size_t i = 1; argv[i] != nullptr; ++i) {
       gCmdLine->append(" ");
       gCmdLine->append(argv[i]);
     }
     gProgramInvocationName.reset(new std::string(argv[0]));
     const char* last_slash = strrchr(argv[0], '/');
-    gProgramInvocationShortName.reset(new std::string((last_slash != NULL) ? last_slash + 1
+    gProgramInvocationShortName.reset(new std::string((last_slash != nullptr) ? last_slash + 1
                                                                            : argv[0]));
   } else {
-    // TODO: fall back to /proc/self/cmdline when argv is NULL on Linux
+    // TODO: fall back to /proc/self/cmdline when argv is NULL on Linux.
     gCmdLine.reset(new std::string("<unset>"));
   }
   const char* tags = getenv("ANDROID_LOG_TAGS");
-  if (tags == NULL) {
+  if (tags == nullptr) {
     return;
   }
 
   std::vector<std::string> specs;
-  Split(tags, ' ', specs);
+  Split(tags, ' ', &specs);
   for (size_t i = 0; i < specs.size(); ++i) {
     // "tag-pattern:[vdiwefs]"
     std::string spec(specs[i]);
@@ -119,47 +119,145 @@
   }
 }
 
-LogMessageData::LogMessageData(const char* file, int line, LogSeverity severity, int error)
-    : file(file),
-      line_number(line),
-      severity(severity),
-      error(error) {
-  const char* last_slash = strrchr(file, '/');
-  file = (last_slash == NULL) ? file : last_slash + 1;
-}
+// This indirection greatly reduces the stack impact of having
+// lots of checks/logging in a function.
+class LogMessageData {
+ public:
+  LogMessageData(const char* file, unsigned int line, LogSeverity severity, int error)
+      : file_(file),
+        line_number_(line),
+        severity_(severity),
+        error_(error) {
+    const char* last_slash = strrchr(file, '/');
+    file = (last_slash == nullptr) ? file : last_slash + 1;
+  }
 
+  const char * GetFile() const {
+    return file_;
+  }
+
+  unsigned int GetLineNumber() const {
+    return line_number_;
+  }
+
+  LogSeverity GetSeverity() const {
+    return severity_;
+  }
+
+  int GetError() const {
+    return error_;
+  }
+
+  std::ostream& GetBuffer() {
+    return buffer_;
+  }
+
+  std::string ToString() const {
+    return buffer_.str();
+  }
+
+ private:
+  std::ostringstream buffer_;
+  const char* const file_;
+  const unsigned int line_number_;
+  const LogSeverity severity_;
+  const int error_;
+
+  DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+};
+
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, int error)
+  : data_(new LogMessageData(file, line, severity, error)) {
+}
 LogMessage::~LogMessage() {
-  if (data_->severity < gMinimumLogSeverity) {
+  if (data_->GetSeverity() < gMinimumLogSeverity) {
     return;  // No need to format something we're not going to output.
   }
 
   // Finish constructing the message.
-  if (data_->error != -1) {
-    data_->buffer << ": " << strerror(data_->error);
+  if (data_->GetError() != -1) {
+    data_->GetBuffer() << ": " << strerror(data_->GetError());
   }
-  std::string msg(data_->buffer.str());
+  std::string msg(data_->ToString());
 
   // Do the actual logging with the lock held.
   {
     MutexLock mu(Thread::Current(), *Locks::logging_lock_);
     if (msg.find('\n') == std::string::npos) {
-      LogLine(*data_, msg.c_str());
+      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), msg.c_str());
     } else {
       msg += '\n';
       size_t i = 0;
       while (i < msg.size()) {
         size_t nl = msg.find('\n', i);
         msg[nl] = '\0';
-        LogLine(*data_, &msg[i]);
+        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), &msg[i]);
         i = nl + 1;
       }
     }
   }
 
   // Abort if necessary.
-  if (data_->severity == FATAL) {
+  if (data_->GetSeverity() == FATAL) {
     Runtime::Abort();
   }
 }
 
+std::ostream& LogMessage::stream() {
+  return data_->GetBuffer();
+}
+
+#ifdef HAVE_ANDROID_OS
+static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
+  ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN,
+  ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL
+};
+static_assert(arraysize(kLogSeverityToAndroidLogPriority) == INTERNAL_FATAL + 1,
+              "Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity");
+#endif
+
+void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity log_severity,
+                         const char* message) {
+#ifdef HAVE_ANDROID_OS
+  const char* tag = ProgramInvocationShortName();
+  int priority = kLogSeverityToAndroidLogPriority[log_severity];
+  if (priority == ANDROID_LOG_FATAL) {
+    LOG_PRI(priority, tag, "%s:%u] %s", file, line, message);
+  } else {
+    LOG_PRI(priority, tag, "%s", message);
+  }
+#else
+  static const char* log_characters = "VDIWEFF";
+  CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
+  char severity = log_characters[log_severity];
+  fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n",
+          ProgramInvocationShortName(), severity, getpid(), ::art::GetTid(), file, line, message);
+#endif
+}
+
+void LogMessage::LogLineLowStack(const char* file, unsigned int line, LogSeverity log_severity,
+                                 const char* message) {
+#ifdef HAVE_ANDROID_OS
+  // TODO: be more conservative on stack usage here.
+  LogLine(file, line, log_severity, message);
+#else
+  static const char* log_characters = "VDIWEFF";
+  CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
+
+  const char* program_name = ProgramInvocationShortName();
+  write(STDERR_FILENO, program_name, strlen(program_name));
+  write(STDERR_FILENO, " ", 1);
+  write(STDERR_FILENO, &log_characters[log_severity], 1);
+  write(STDERR_FILENO, " ", 1);
+  // TODO: pid and tid.
+  write(STDERR_FILENO, file, strlen(file));
+  // TODO: line.
+  UNUSED(line);
+  write(STDERR_FILENO, "] ", 2);
+  write(STDERR_FILENO, message, strlen(message));
+  write(STDERR_FILENO, "\n", 1);
+#endif
+}
+
 }  // namespace art
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index caeb946..ae83e33 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -17,284 +17,21 @@
 #ifndef ART_RUNTIME_BASE_LOGGING_H_
 #define ART_RUNTIME_BASE_LOGGING_H_
 
-#include <cerrno>
-#include <cstring>
-#include <iostream>  // NOLINT
 #include <memory>
-#include <sstream>
-#include <signal.h>
-#include <vector>
+#include <ostream>
 
 #include "base/macros.h"
-#include "log_severity.h"
-
-#define CHECK(x) \
-  if (UNLIKELY(!(x))) \
-    ::art::LogMessage(__FILE__, __LINE__, FATAL, -1).stream() \
-        << "Check failed: " #x << " "
-
-#define CHECK_OP(LHS, RHS, OP) \
-  for (auto _values = ::art::MakeEagerEvaluator(LHS, RHS); \
-       UNLIKELY(!(_values.lhs OP _values.rhs)); /* empty */) \
-    ::art::LogMessage(__FILE__, __LINE__, FATAL, -1).stream() \
-        << "Check failed: " << #LHS << " " << #OP << " " << #RHS \
-        << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
-
-#define CHECK_EQ(x, y) CHECK_OP(x, y, ==)
-#define CHECK_NE(x, y) CHECK_OP(x, y, !=)
-#define CHECK_LE(x, y) CHECK_OP(x, y, <=)
-#define CHECK_LT(x, y) CHECK_OP(x, y, <)
-#define CHECK_GE(x, y) CHECK_OP(x, y, >=)
-#define CHECK_GT(x, y) CHECK_OP(x, y, >)
-
-#define CHECK_STROP(s1, s2, sense) \
-  if (UNLIKELY((strcmp(s1, s2) == 0) != sense)) \
-    LOG(FATAL) << "Check failed: " \
-               << "\"" << s1 << "\"" \
-               << (sense ? " == " : " != ") \
-               << "\"" << s2 << "\""
-
-#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
-#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
-
-#define CHECK_PTHREAD_CALL(call, args, what) \
-  do { \
-    int rc = call args; \
-    if (rc != 0) { \
-      errno = rc; \
-      PLOG(FATAL) << # call << " failed for " << what; \
-    } \
-  } while (false)
-
-// CHECK that can be used in a constexpr function. For example,
-//    constexpr int half(int n) {
-//      return
-//          DCHECK_CONSTEXPR(n >= 0, , 0)
-//          CHECK_CONSTEXPR((n & 1) == 0), << "Extra debugging output: n = " << n, 0)
-//          n / 2;
-//    }
-#define CHECK_CONSTEXPR(x, out, dummy) \
-  (UNLIKELY(!(x))) ? (LOG(FATAL) << "Check failed: " << #x out, dummy) :
-
-#ifndef NDEBUG
-
-#define DCHECK(x) CHECK(x)
-#define DCHECK_EQ(x, y) CHECK_EQ(x, y)
-#define DCHECK_NE(x, y) CHECK_NE(x, y)
-#define DCHECK_LE(x, y) CHECK_LE(x, y)
-#define DCHECK_LT(x, y) CHECK_LT(x, y)
-#define DCHECK_GE(x, y) CHECK_GE(x, y)
-#define DCHECK_GT(x, y) CHECK_GT(x, y)
-#define DCHECK_STREQ(s1, s2) CHECK_STREQ(s1, s2)
-#define DCHECK_STRNE(s1, s2) CHECK_STRNE(s1, s2)
-#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
-
-#else  // NDEBUG
-
-#define DCHECK(condition) \
-  while (false) \
-    CHECK(condition)
-
-#define DCHECK_EQ(val1, val2) \
-  while (false) \
-    CHECK_EQ(val1, val2)
-
-#define DCHECK_NE(val1, val2) \
-  while (false) \
-    CHECK_NE(val1, val2)
-
-#define DCHECK_LE(val1, val2) \
-  while (false) \
-    CHECK_LE(val1, val2)
-
-#define DCHECK_LT(val1, val2) \
-  while (false) \
-    CHECK_LT(val1, val2)
-
-#define DCHECK_GE(val1, val2) \
-  while (false) \
-    CHECK_GE(val1, val2)
-
-#define DCHECK_GT(val1, val2) \
-  while (false) \
-    CHECK_GT(val1, val2)
-
-#define DCHECK_STREQ(str1, str2) \
-  while (false) \
-    CHECK_STREQ(str1, str2)
-
-#define DCHECK_STRNE(str1, str2) \
-  while (false) \
-    CHECK_STRNE(str1, str2)
-
-#define DCHECK_CONSTEXPR(x, out, dummy) \
-  (false && (x)) ? (dummy) :
-
-#endif
-
-#define LOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, -1).stream()
-#define PLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, errno).stream()
-
-#define LG LOG(INFO)
-
-#define UNIMPLEMENTED(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
-
-#define VLOG_IS_ON(module) UNLIKELY(::art::gLogVerbosity.module)
-#define VLOG(module) if (VLOG_IS_ON(module)) ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream()
-#define VLOG_STREAM(module) ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream()
-
-//
-// Implementation details beyond this point.
-//
 
 namespace art {
 
-template <typename LHS, typename RHS>
-struct EagerEvaluator {
-  EagerEvaluator(LHS lhs, RHS rhs) : lhs(lhs), rhs(rhs) { }
-  LHS lhs;
-  RHS rhs;
-};
-
-// We want char*s to be treated as pointers, not strings. If you want them treated like strings,
-// you'd need to use CHECK_STREQ and CHECK_STRNE anyway to compare the characters rather than their
-// addresses. We could express this more succinctly with std::remove_const, but this is quick and
-// easy to understand, and works before we have C++0x. We rely on signed/unsigned warnings to
-// 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)) { } \
-    const void* lhs; \
-    const void* rhs; \
-  }
-EAGER_PTR_EVALUATOR(const char*, const char*);
-EAGER_PTR_EVALUATOR(const char*, char*);
-EAGER_PTR_EVALUATOR(char*, const char*);
-EAGER_PTR_EVALUATOR(char*, char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(const signed char*, signed char*);
-EAGER_PTR_EVALUATOR(signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(signed char*, signed char*);
-
-template <typename LHS, typename RHS>
-EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
-  return EagerEvaluator<LHS, RHS>(lhs, rhs);
-}
-
-// This indirection greatly reduces the stack impact of having
-// lots of checks/logging in a function.
-struct LogMessageData {
- public:
-  LogMessageData(const char* file, int line, LogSeverity severity, int error);
-  std::ostringstream buffer;
-  const char* const file;
-  const int line_number;
-  const LogSeverity severity;
-  const int error;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LogMessageData);
-};
-
-class LogMessage {
- public:
-  LogMessage(const char* file, int line, LogSeverity severity, int error)
-    : data_(new LogMessageData(file, line, severity, error)) {
-  }
-
-  ~LogMessage();  // TODO: enable LOCKS_EXCLUDED(Locks::logging_lock_).
-
-  std::ostream& stream() {
-    return data_->buffer;
-  }
-
- private:
-  static void LogLine(const LogMessageData& data, const char*);
-
-  const std::unique_ptr<LogMessageData> data_;
-
-  friend void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context);
-  friend class Mutex;
-  DISALLOW_COPY_AND_ASSIGN(LogMessage);
-};
-
-// A convenience to allow any class with a "Dump(std::ostream& os)" member function
-// but without an operator<< to be used as if it had an operator<<. Use like this:
-//
-//   os << Dumpable<MyType>(my_type_instance);
-//
-template<typename T>
-class Dumpable {
- public:
-  explicit Dumpable(T& value) : value_(value) {
-  }
-
-  void Dump(std::ostream& os) const {
-    value_.Dump(os);
-  }
-
- private:
-  T& value_;
-
-  DISALLOW_COPY_AND_ASSIGN(Dumpable);
-};
-
-template<typename T>
-std::ostream& operator<<(std::ostream& os, const Dumpable<T>& rhs) {
-  rhs.Dump(os);
-  return os;
-}
-
-template<typename T>
-class ConstDumpable {
- public:
-  explicit ConstDumpable(const T& value) : value_(value) {
-  }
-
-  void Dump(std::ostream& os) const {
-    value_.Dump(os);
-  }
-
- private:
-  const T& value_;
-
-  DISALLOW_COPY_AND_ASSIGN(ConstDumpable);
-};
-
-template<typename T>
-std::ostream& operator<<(std::ostream& os, const ConstDumpable<T>& rhs) {
-  rhs.Dump(os);
-  return os;
-}
-
-// Helps you use operator<< in a const char*-like context such as our various 'F' methods with
-// format strings.
-template<typename T>
-class ToStr {
- public:
-  explicit ToStr(const T& value) {
-    std::ostringstream os;
-    os << value;
-    s_ = os.str();
-  }
-
-  const char* c_str() const {
-    return s_.c_str();
-  }
-
-  const std::string& str() const {
-    return s_;
-  }
-
- private:
-  std::string s_;
-  DISALLOW_COPY_AND_ASSIGN(ToStr);
+enum LogSeverity {
+  VERBOSE,
+  DEBUG,
+  INFO,
+  WARNING,
+  ERROR,
+  FATAL,
+  INTERNAL_FATAL,  // For Runtime::Abort.
 };
 
 // The members of this struct are the valid arguments to VLOG and VLOG_IS_ON in code,
@@ -315,20 +52,208 @@
   bool verifier;
 };
 
+// Global log verbosity setting, initialized by InitLogging.
 extern LogVerbosity gLogVerbosity;
 
-extern std::vector<std::string> gVerboseMethods;
-
-// Used on fatal exit. Prevents recursive aborts. Allows us to disable
-// some error checking to ensure fatal shutdown makes forward progress.
+// 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive
+// aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown
+// makes forward progress.
 extern unsigned int gAborting;
 
+// Configure logging based on ANDROID_LOG_TAGS environment variable.
+// We need to parse a string that looks like
+//
+//      *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+//
+// The tag (or '*' for the global level) comes first, followed by a colon
+// and a letter indicating the minimum priority level we're expected to log.
+// This can be used to reveal or conceal logs with specific tags.
 extern void InitLogging(char* argv[]);
 
+// Returns the command line used to invoke the current tool or nullptr if InitLogging hasn't been
+// performed.
 extern const char* GetCmdLine();
+
+// The command used to start the ART runtime, such as "/system/bin/dalvikvm". If InitLogging hasn't
+// been performed then just returns "art"
 extern const char* ProgramInvocationName();
+
+// A short version of the command used to start the ART runtime, such as "dalvikvm". If InitLogging
+// hasn't been performed then just returns "art"
 extern const char* ProgramInvocationShortName();
 
+// Logs a message to logcat on Android otherwise to stderr. If the severity is FATAL it also causes
+// an abort. For example: LOG(FATAL) << "We didn't expect to reach here";
+#define LOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, -1).stream()
+
+// A variant of LOG that also logs the current errno value. To be used when library calls fail.
+#define PLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, errno).stream()
+
+// Marker that code is yet to be implemented.
+#define UNIMPLEMENTED(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+
+// Is verbose logging enabled for the given module? Where the module is defined in LogVerbosity.
+#define VLOG_IS_ON(module) UNLIKELY(::art::gLogVerbosity.module)
+
+// Variant of LOG that logs when verbose logging is enabled for a module. For example,
+// VLOG(jni) << "A JNI operation was performed";
+#define VLOG(module) \
+  if (VLOG_IS_ON(module)) \
+    ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream()
+
+// Return the stream associated with logging for the given module.
+#define VLOG_STREAM(module) ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream()
+
+// Check whether condition x holds and LOG(FATAL) if not. The value of the expression x is only
+// evaluated once. Extra logging can be appended using << after. For example,
+// CHECK(false == true) results in a log message of "Check failed: false == true".
+#define CHECK(x) \
+  if (UNLIKELY(!(x))) \
+    ::art::LogMessage(__FILE__, __LINE__, ::art::FATAL, -1).stream() \
+        << "Check failed: " #x << " "
+
+// Helper for CHECK_xx(x,y) macros.
+#define CHECK_OP(LHS, RHS, OP) \
+  for (auto _values = ::art::MakeEagerEvaluator(LHS, RHS); \
+       UNLIKELY(!(_values.lhs OP _values.rhs)); /* empty */) \
+    ::art::LogMessage(__FILE__, __LINE__, ::art::FATAL, -1).stream() \
+        << "Check failed: " << #LHS << " " << #OP << " " << #RHS \
+        << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
+
+
+// Check whether a condition holds between x and y, LOG(FATAL) if not. The value of the expressions
+// x and y is evaluated once. Extra logging can be appended using << after. For example,
+// CHECK_NE(0 == 1, false) results in "Check failed: false != false (0==1=false, false=false) ".
+#define CHECK_EQ(x, y) CHECK_OP(x, y, ==)
+#define CHECK_NE(x, y) CHECK_OP(x, y, !=)
+#define CHECK_LE(x, y) CHECK_OP(x, y, <=)
+#define CHECK_LT(x, y) CHECK_OP(x, y, <)
+#define CHECK_GE(x, y) CHECK_OP(x, y, >=)
+#define CHECK_GT(x, y) CHECK_OP(x, y, >)
+
+// Helper for CHECK_STRxx(s1,s2) macros.
+#define CHECK_STROP(s1, s2, sense) \
+  if (UNLIKELY((strcmp(s1, s2) == 0) != sense)) \
+    LOG(::art::FATAL) << "Check failed: " \
+        << "\"" << s1 << "\"" \
+        << (sense ? " == " : " != ") \
+        << "\"" << s2 << "\""
+
+// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
+#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
+#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
+
+// Perform the pthread function call(args), LOG(FATAL) on error.
+#define CHECK_PTHREAD_CALL(call, args, what) \
+  do { \
+    int rc = call args; \
+    if (rc != 0) { \
+      errno = rc; \
+      PLOG(::art::FATAL) << # call << " failed for " << what; \
+    } \
+  } while (false)
+
+// CHECK that can be used in a constexpr function. For example,
+//    constexpr int half(int n) {
+//      return
+//          DCHECK_CONSTEXPR(n >= 0, , 0)
+//          CHECK_CONSTEXPR((n & 1) == 0), << "Extra debugging output: n = " << n, 0)
+//          n / 2;
+//    }
+#define CHECK_CONSTEXPR(x, out, dummy) \
+  (UNLIKELY(!(x))) ? (LOG(::art::FATAL) << "Check failed: " << #x out, dummy) :
+
+
+// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally CHECK should be
+// used unless profiling identifies a CHECK as being in performance critical code.
+#if defined(NDEBUG)
+static constexpr bool kEnableDChecks = false;
+#else
+static constexpr bool kEnableDChecks = true;
+#endif
+
+#define DCHECK(x) if (::art::kEnableDChecks) CHECK(x)
+#define DCHECK_EQ(x, y) if (::art::kEnableDChecks) CHECK_EQ(x, y)
+#define DCHECK_NE(x, y) if (::art::kEnableDChecks) CHECK_NE(x, y)
+#define DCHECK_LE(x, y) if (::art::kEnableDChecks) CHECK_LE(x, y)
+#define DCHECK_LT(x, y) if (::art::kEnableDChecks) CHECK_LT(x, y)
+#define DCHECK_GE(x, y) if (::art::kEnableDChecks) CHECK_GE(x, y)
+#define DCHECK_GT(x, y) if (::art::kEnableDChecks) CHECK_GT(x, y)
+#define DCHECK_STREQ(s1, s2) if (::art::kEnableDChecks) CHECK_STREQ(s1, s2)
+#define DCHECK_STRNE(s1, s2) if (::art::kEnableDChecks) CHECK_STRNE(s1, s2)
+#if defined(NDEBUG)
+#define DCHECK_CONSTEXPR(x, out, dummy)
+#else
+#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
+#endif
+
+// Temporary class created to evaluate the LHS and RHS, used with MakeEagerEvaluator to infer the
+// types of LHS and RHS.
+template <typename LHS, typename RHS>
+struct EagerEvaluator {
+  EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) { }
+  LHS lhs;
+  RHS rhs;
+};
+
+// Helper function for CHECK_xx.
+template <typename LHS, typename RHS>
+static inline EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
+  return EagerEvaluator<LHS, RHS>(lhs, rhs);
+}
+
+// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated as strings. To
+// compare strings use CHECK_STREQ and CHECK_STRNE. We rely on signed/unsigned warnings to
+// protect you against combinations not explicitly listed below.
+#define EAGER_PTR_EVALUATOR(T1, T2) \
+  template <> struct EagerEvaluator<T1, T2> { \
+    EagerEvaluator(T1 l, T2 r) \
+        : lhs(reinterpret_cast<const void*>(l)), \
+          rhs(reinterpret_cast<const void*>(r)) { } \
+    const void* lhs; \
+    const void* rhs; \
+  }
+EAGER_PTR_EVALUATOR(const char*, const char*);
+EAGER_PTR_EVALUATOR(const char*, char*);
+EAGER_PTR_EVALUATOR(char*, const char*);
+EAGER_PTR_EVALUATOR(char*, char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(const signed char*, signed char*);
+EAGER_PTR_EVALUATOR(signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(signed char*, signed char*);
+
+// Data for the log message, not stored in LogMessage to avoid increasing the stack size.
+class LogMessageData;
+
+// A LogMessage is a temporarily scoped object used by LOG and the unlikely part of a CHECK. The
+// destructor will abort if the severity is FATAL.
+class LogMessage {
+ public:
+  LogMessage(const char* file, unsigned int line, LogSeverity severity, int error);
+
+  ~LogMessage();  // TODO: enable LOCKS_EXCLUDED(Locks::logging_lock_).
+
+  // Returns the stream associated with the message, the LogMessage performs output when it goes
+  // out of scope.
+  std::ostream& stream();
+
+  // The routine that performs the actual logging.
+  static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* msg);
+
+  // A variant of the above for use with little stack.
+  static void LogLineLowStack(const char* file, unsigned int line, LogSeverity severity,
+                              const char* msg);
+
+ private:
+  const std::unique_ptr<LogMessageData> data_;
+
+  DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_LOGGING_H_
diff --git a/runtime/base/logging_android.cc b/runtime/base/logging_android.cc
deleted file mode 100644
index 9b1ac58..0000000
--- a/runtime/base/logging_android.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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 "logging.h"
-
-#include <unistd.h>
-
-#include <iostream>
-
-#include "base/stringprintf.h"
-#include "cutils/log.h"
-
-namespace art {
-
-static const int kLogSeverityToAndroidLogPriority[] = {
-  ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN,
-  ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL
-};
-
-void LogMessage::LogLine(const LogMessageData& data, const char* message) {
-  const char* tag = ProgramInvocationShortName();
-  int priority = kLogSeverityToAndroidLogPriority[data.severity];
-  if (priority == ANDROID_LOG_FATAL) {
-    LOG_PRI(priority, tag, "%s:%d] %s", data.file, data.line_number, message);
-  } else {
-    LOG_PRI(priority, tag, "%s", message);
-  }
-}
-
-}  // namespace art
diff --git a/runtime/base/logging_linux.cc b/runtime/base/logging_linux.cc
deleted file mode 100644
index 0399128..0000000
--- a/runtime/base/logging_linux.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 "logging.h"
-
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cstdio>
-#include <cstring>
-#include <iostream>
-
-#include "base/stringprintf.h"
-#include "utils.h"
-
-namespace art {
-
-void LogMessage::LogLine(const LogMessageData& data, const char* message) {
-  char severity = "VDIWEFF"[data.severity];
-  fprintf(stderr, "%s %c %5d %5d %s:%d] %s\n",
-          ProgramInvocationShortName(), severity, getpid(), ::art::GetTid(),
-          data.file, data.line_number, message);
-}
-
-}  // namespace art
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index fae9271..66d6fab 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -41,44 +41,35 @@
 #define FINAL
 #endif
 
-// The COMPILE_ASSERT macro can be used to verify that a compile time
-// expression is true. For example, you could use it to verify the
-// size of a static array:
-//
-//   COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
-//                  content_type_names_incorrect_size);
-//
-// or to make sure a struct is smaller than a certain size:
-//
-//   COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
-//
-// The second argument to the macro is the name of the variable. If
-// the expression is false, most compilers will issue a warning/error
-// containing the name of the variable.
+// Declare a friend relationship in a class with a test. Used rather that FRIEND_TEST to avoid
+// globally importing gtest/gtest.h into the main ART header files.
+#define ART_FRIEND_TEST(test_set_name, individual_test)\
+friend class test_set_name##_##individual_test##_Test
 
-template <bool>
-struct CompileAssert {
-};
-
-#define COMPILE_ASSERT(expr, msg) \
-  typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] // NOLINT
-
-// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
-// It goes in the private: declarations in a class.
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
+// declarations in a class.
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
 #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
-  TypeName(const TypeName&);               \
-  void operator=(const TypeName&)
+  TypeName(const TypeName&) = delete;  \
+  void operator=(const TypeName&) = delete
+#endif
 
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
+// A macro to disallow all the implicit constructors, namely the default constructor, copy
+// constructor and operator= functions.
 //
-// This should be used in the private: declarations for a class
-// that wants to prevent anyone from instantiating it. This is
-// especially useful for classes containing only static methods.
+// This should be used in the private: declarations for a class that wants to prevent anyone from
+// instantiating it. This is especially useful for classes containing only static methods.
 #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
-  TypeName();                                    \
+  TypeName() = delete;  \
   DISALLOW_COPY_AND_ASSIGN(TypeName)
 
+// A macro to disallow new and delete operators for a class. It goes in the private: declarations.
+#define DISALLOW_ALLOCATION() \
+  public: \
+    ALWAYS_INLINE void operator delete(void*, size_t) { UNREACHABLE(); } \
+  private: \
+    void* operator new(size_t) = delete
+
 // The arraysize(arr) macro returns the # of elements in an array arr.
 // The expression is a compile-time constant, and therefore can be
 // used in defining new arrays, for example.  If you use arraysize on
@@ -178,7 +169,62 @@
 #define PURE __attribute__ ((__pure__))
 #define WARN_UNUSED __attribute__((warn_unused_result))
 
-template<typename T> void UNUSED(const T&) {}
+// A deprecated function to call to create a false use of the parameter, for example:
+//   int foo(int x) { UNUSED(x); return 10; }
+// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
+template<typename... T> void UNUSED(const T&...) {}
+
+// An attribute to place on a parameter to a function, for example:
+//   int foo(int x ATTRIBUTE_UNUSED) { return 10; }
+// to avoid compiler warnings.
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+
+// Define that a position within code is unreachable, for example:
+//   int foo () { LOG(FATAL) << "Don't call me"; UNREACHABLE(); }
+// without the UNREACHABLE a return statement would be necessary.
+#define UNREACHABLE  __builtin_unreachable
+
+// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
+// between switch labels:
+//  switch (x) {
+//    case 40:
+//    case 41:
+//      if (truth_is_out_there) {
+//        ++x;
+//        FALLTHROUGH_INTENDED;  // Use instead of/along with annotations in
+//                               // comments.
+//      } else {
+//        return x;
+//      }
+//    case 42:
+//      ...
+//
+//  As shown in the example above, the FALLTHROUGH_INTENDED macro should be
+//  followed by a semicolon. It is designed to mimic control-flow statements
+//  like 'break;', so it can be placed in most places where 'break;' can, but
+//  only if there are no statements on the execution path between it and the
+//  next switch label.
+//
+//  When compiled with clang in C++11 mode, the FALLTHROUGH_INTENDED macro is
+//  expanded to [[clang::fallthrough]] attribute, which is analysed when
+//  performing switch labels fall-through diagnostic ('-Wimplicit-fallthrough').
+//  See clang documentation on language extensions for details:
+//  http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
+//
+//  When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
+//  effect on diagnostics.
+//
+//  In either case this macro has no effect on runtime behavior and performance
+//  of code.
+#if defined(__clang__) && __cplusplus >= 201103L && defined(__has_warning)
+#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
+#endif
+#endif
+
+#ifndef FALLTHROUGH_INTENDED
+#define FALLTHROUGH_INTENDED do { } while (0)
+#endif
 
 // Annotalysis thread-safety analysis support.
 #if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index f70db35..c310191 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -21,58 +21,29 @@
 
 #include "mutex.h"
 
-#define ATRACE_TAG ATRACE_TAG_DALVIK
-
-#include "cutils/trace.h"
-
 #include "base/stringprintf.h"
+#include "base/value_object.h"
 #include "runtime.h"
 #include "thread.h"
 
-namespace art {
-
-#define CHECK_MUTEX_CALL(call, args) CHECK_PTHREAD_CALL(call, args, name_)
-
 #if ART_USE_FUTEXES
 #include "linux/futex.h"
 #include "sys/syscall.h"
 #ifndef SYS_futex
 #define SYS_futex __NR_futex
 #endif
+#endif  // ART_USE_FUTEXES
+
+#define CHECK_MUTEX_CALL(call, args) CHECK_PTHREAD_CALL(call, args, name_)
+
+namespace art {
+
+#if ART_USE_FUTEXES
 static inline int futex(volatile int *uaddr, int op, int val, const struct timespec *timeout, volatile int *uaddr2, int val3) {
   return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
 }
 #endif  // ART_USE_FUTEXES
 
-class ScopedContentionRecorder {
- public:
-  ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid)
-      : mutex_(kLogLockContentions ? mutex : NULL),
-        blocked_tid_(kLogLockContentions ? blocked_tid : 0),
-        owner_tid_(kLogLockContentions ? owner_tid : 0),
-        start_nano_time_(kLogLockContentions ? NanoTime() : 0) {
-    if (ATRACE_ENABLED()) {
-      std::string msg = StringPrintf("Lock contention on %s (owner tid: %" PRIu64 ")",
-                                     mutex->GetName(), owner_tid);
-      ATRACE_BEGIN(msg.c_str());
-    }
-  }
-
-  ~ScopedContentionRecorder() {
-    ATRACE_END();
-    if (kLogLockContentions) {
-      uint64_t end_nano_time = NanoTime();
-      mutex_->RecordContention(blocked_tid_, owner_tid_, end_nano_time - start_nano_time_);
-    }
-  }
-
- private:
-  BaseMutex* const mutex_;
-  const uint64_t blocked_tid_;
-  const uint64_t owner_tid_;
-  const uint64_t start_nano_time_;
-};
-
 static inline uint64_t SafeGetTid(const Thread* self) {
   if (self != NULL) {
     return static_cast<uint64_t>(self->GetTid());
@@ -158,15 +129,7 @@
       // Add as an extra reader.
       done = state_.CompareExchangeWeakAcquire(cur_state, cur_state + 1);
     } else {
-      // Owner holds it exclusively, hang up.
-      ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self));
-      ++num_pending_readers_;
-      if (futex(state_.Address(), FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) {
-        if (errno != EAGAIN) {
-          PLOG(FATAL) << "futex wait failed for " << name_;
-        }
-      }
-      --num_pending_readers_;
+      HandleSharedLockContention(self, cur_state);
     }
   } while (!done);
 #else
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 495ea5c..423ea77 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -19,8 +19,12 @@
 #include <errno.h>
 #include <sys/time.h>
 
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include "cutils/trace.h"
+
 #include "atomic.h"
 #include "base/logging.h"
+#include "base/value_object.h"
 #include "mutex-inl.h"
 #include "runtime.h"
 #include "scoped_thread_state_change.h"
@@ -39,6 +43,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;
@@ -82,22 +87,59 @@
 }
 #endif
 
-class ScopedAllMutexesLock {
+class ScopedAllMutexesLock FINAL {
  public:
   explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) {
     while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakAcquire(0, mutex)) {
       NanoSleep(100);
     }
   }
+
   ~ScopedAllMutexesLock() {
+#if !defined(__clang__)
+    // TODO: remove this workaround target GCC/libc++/bionic bug "invalid failure memory model".
+    while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakSequentiallyConsistent(mutex_, 0)) {
+#else
     while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakRelease(mutex_, 0)) {
+#endif
       NanoSleep(100);
     }
   }
+
  private:
   const BaseMutex* const mutex_;
 };
 
+// Scoped class that generates events at the beginning and end of lock contention.
+class ScopedContentionRecorder FINAL : public ValueObject {
+ public:
+  ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid)
+      : mutex_(kLogLockContentions ? mutex : NULL),
+        blocked_tid_(kLogLockContentions ? blocked_tid : 0),
+        owner_tid_(kLogLockContentions ? owner_tid : 0),
+        start_nano_time_(kLogLockContentions ? NanoTime() : 0) {
+    if (ATRACE_ENABLED()) {
+      std::string msg = StringPrintf("Lock contention on %s (owner tid: %" PRIu64 ")",
+                                     mutex->GetName(), owner_tid);
+      ATRACE_BEGIN(msg.c_str());
+    }
+  }
+
+  ~ScopedContentionRecorder() {
+    ATRACE_END();
+    if (kLogLockContentions) {
+      uint64_t end_nano_time = NanoTime();
+      mutex_->RecordContention(blocked_tid_, owner_tid_, end_nano_time - start_nano_time_);
+    }
+  }
+
+ private:
+  BaseMutex* const mutex_;
+  const uint64_t blocked_tid_;
+  const uint64_t owner_tid_;
+  const uint64_t start_nano_time_;
+};
+
 BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) {
   if (kLogLockContentions) {
     ScopedAllMutexesLock mu(this);
@@ -420,9 +462,9 @@
         if (this != Locks::logging_lock_) {
           LOG(FATAL) << "Unexpected state_ in unlock " << cur_state << " for " << name_;
         } else {
-          LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1);
-          LogMessage::LogLine(data, StringPrintf("Unexpected state_ %d in unlock for %s",
-                                                 cur_state, name_).c_str());
+          LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL,
+                              StringPrintf("Unexpected state_ %d in unlock for %s",
+                                           cur_state, name_).c_str());
           _exit(1);
         }
       }
@@ -604,6 +646,20 @@
 }
 #endif
 
+#if ART_USE_FUTEXES
+void ReaderWriterMutex::HandleSharedLockContention(Thread* self, int32_t cur_state) {
+  // Owner holds it exclusively, hang up.
+  ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self));
+  ++num_pending_readers_;
+  if (futex(state_.Address(), FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) {
+    if (errno != EAGAIN) {
+      PLOG(FATAL) << "futex wait failed for " << name_;
+    }
+  }
+  --num_pending_readers_;
+}
+#endif
+
 bool ReaderWriterMutex::SharedTryLock(Thread* self) {
   DCHECK(self == NULL || self == Thread::Current());
 #if ART_USE_FUTEXES
@@ -672,7 +728,7 @@
   CHECK_MUTEX_CALL(pthread_condattr_init, (&cond_attrs));
 #if !defined(__APPLE__)
   // Apple doesn't have CLOCK_MONOTONIC or pthread_condattr_setclock.
-  CHECK_MUTEX_CALL(pthread_condattr_setclock(&cond_attrs, CLOCK_MONOTONIC));
+  CHECK_MUTEX_CALL(pthread_condattr_setclock, (&cond_attrs, CLOCK_MONOTONIC));
 #endif
   CHECK_MUTEX_CALL(pthread_cond_init, (&cond_, &cond_attrs));
 #endif
@@ -785,8 +841,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_;
@@ -802,6 +859,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 {
@@ -826,13 +884,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() {
@@ -852,6 +913,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);
@@ -912,6 +974,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..d589eb6 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,
@@ -361,6 +361,9 @@
 
  private:
 #if ART_USE_FUTEXES
+  // Out-of-inline path for handling contention for a SharedLock.
+  void HandleSharedLockContention(Thread* self, int32_t cur_state);
+
   // -1 implies held exclusive, +ve shared held by state_ many owners.
   AtomicInteger state_;
   // Exclusive owner. Modification guarded by this mutex.
@@ -388,7 +391,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.
@@ -432,7 +435,7 @@
   DISALLOW_COPY_AND_ASSIGN(MutexLock);
 };
 // Catch bug where variable name is omitted. "MutexLock (lock);" instead of "MutexLock mu(lock)".
-#define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_declaration_missing_variable_name)
+#define MutexLock(x) static_assert(0, "MutexLock declaration missing variable name")
 
 // Scoped locker/unlocker for a ReaderWriterMutex that acquires read access to mu upon
 // construction and releases it upon destruction.
@@ -454,7 +457,7 @@
 };
 // Catch bug where variable name is omitted. "ReaderMutexLock (lock);" instead of
 // "ReaderMutexLock mu(lock)".
-#define ReaderMutexLock(x) COMPILE_ASSERT(0, reader_mutex_lock_declaration_missing_variable_name)
+#define ReaderMutexLock(x) static_assert(0, "ReaderMutexLock declaration missing variable name")
 
 // Scoped locker/unlocker for a ReaderWriterMutex that acquires write access to mu upon
 // construction and releases it upon destruction.
@@ -476,7 +479,7 @@
 };
 // Catch bug where variable name is omitted. "WriterMutexLock (lock);" instead of
 // "WriterMutexLock mu(lock)".
-#define WriterMutexLock(x) COMPILE_ASSERT(0, writer_mutex_lock_declaration_missing_variable_name)
+#define WriterMutexLock(x) static_assert(0, "WriterMutexLock declaration missing variable name")
 
 // Global mutexes corresponding to the levels above.
 class Locks {
@@ -579,8 +582,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..2570bad 100644
--- a/runtime/base/stringpiece.cc
+++ b/runtime/base/stringpiece.cc
@@ -16,29 +16,37 @@
 
 #include "stringpiece.h"
 
-#include <iostream>
+#include <ostream>
 #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..d793bb6 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) {
-    ptr_ = data;
+  void set(const char* data_in, size_type len) {
+    ptr_ = data_in;
     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) {
-    ptr_ = reinterpret_cast<const char*>(data);
+  void set(const void* data_in, size_type len) {
+    ptr_ = reinterpret_cast<const char*>(data_in);
     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/base/to_str.h b/runtime/base/to_str.h
new file mode 100644
index 0000000..6b1c84c
--- /dev/null
+++ b/runtime/base/to_str.h
@@ -0,0 +1,50 @@
+/*
+ * 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_BASE_TO_STR_H_
+#define ART_RUNTIME_BASE_TO_STR_H_
+
+#include <sstream>
+
+namespace art {
+
+// Helps you use operator<< in a const char*-like context such as our various 'F' methods with
+// format strings.
+template<typename T>
+class ToStr {
+ public:
+  explicit ToStr(const T& value) {
+    std::ostringstream os;
+    os << value;
+    s_ = os.str();
+  }
+
+  const char* c_str() const {
+    return s_.c_str();
+  }
+
+  const std::string& str() const {
+    return s_;
+  }
+
+ private:
+  std::string s_;
+  DISALLOW_COPY_AND_ASSIGN(ToStr);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_TO_STR_H_
diff --git a/runtime/base/type_static_if.h b/runtime/base/type_static_if.h
new file mode 100644
index 0000000..a74d79a
--- /dev/null
+++ b/runtime/base/type_static_if.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_RUNTIME_BASE_TYPE_STATIC_IF_H_
+#define ART_RUNTIME_BASE_TYPE_STATIC_IF_H_
+
+// A static if which determines whether to return type A or B based on the condition boolean.
+template <bool condition, typename A, typename B>
+struct TypeStaticIf {
+  typedef A type;
+};
+
+// Specialization to handle the false case.
+template <typename A, typename B>
+struct TypeStaticIf<false, A,  B> {
+  typedef B type;
+};
+
+#endif  // ART_RUNTIME_BASE_TYPE_STATIC_IF_H_
diff --git a/runtime/base/unix_file/mapped_file.cc b/runtime/base/unix_file/mapped_file.cc
deleted file mode 100644
index 63927b1..0000000
--- a/runtime/base/unix_file/mapped_file.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2008 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 "base/logging.h"
-#include "base/unix_file/mapped_file.h"
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <algorithm>
-#include <string>
-
-namespace unix_file {
-
-MappedFile::~MappedFile() {
-}
-
-int MappedFile::Close() {
-  if (IsMapped()) {
-    Unmap();
-  }
-  return FdFile::Close();
-}
-
-bool MappedFile::MapReadOnly() {
-  CHECK(IsOpened());
-  CHECK(!IsMapped());
-  struct stat st;
-  int result = TEMP_FAILURE_RETRY(fstat(Fd(), &st));
-  if (result == -1) {
-    PLOG(WARNING) << "Failed to stat file '" << GetPath() << "'";
-    return false;
-  }
-  file_size_ = st.st_size;
-  do {
-    mapped_file_ = mmap(NULL, file_size_, PROT_READ, MAP_PRIVATE, Fd(), 0);
-  } while (mapped_file_ == MAP_FAILED && errno == EINTR);
-  if (mapped_file_ == MAP_FAILED) {
-    PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size "
-                  << file_size_ << " bytes to memory";
-    return false;
-  }
-  map_mode_ = kMapReadOnly;
-  return true;
-}
-
-bool MappedFile::MapReadWrite(int64_t file_size) {
-  CHECK(IsOpened());
-  CHECK(!IsMapped());
-#ifdef __linux__
-  int result = TEMP_FAILURE_RETRY(ftruncate64(Fd(), file_size));
-#else
-  int result = TEMP_FAILURE_RETRY(ftruncate(Fd(), file_size));
-#endif
-  if (result == -1) {
-    PLOG(ERROR) << "Failed to truncate file '" << GetPath()
-                << "' to size " << file_size;
-    return false;
-  }
-  file_size_ = file_size;
-  do {
-    mapped_file_ =
-        mmap(NULL, file_size_, PROT_READ | PROT_WRITE, MAP_SHARED, Fd(), 0);
-  } while (mapped_file_ == MAP_FAILED && errno == EINTR);
-  if (mapped_file_ == MAP_FAILED) {
-    PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size "
-                  << file_size_ << " bytes to memory";
-    return false;
-  }
-  map_mode_ = kMapReadWrite;
-  return true;
-}
-
-bool MappedFile::Unmap() {
-  CHECK(IsMapped());
-  int result = TEMP_FAILURE_RETRY(munmap(mapped_file_, file_size_));
-  if (result == -1) {
-    PLOG(WARNING) << "Failed unmap file '" << GetPath() << "' of size "
-                  << file_size_;
-    return false;
-  } else {
-    mapped_file_ = NULL;
-    file_size_ = -1;
-    return true;
-  }
-}
-
-int64_t MappedFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
-  if (IsMapped()) {
-    if (offset < 0) {
-      errno = EINVAL;
-      return -errno;
-    }
-    int64_t read_size = std::max(static_cast<int64_t>(0),
-                                 std::min(byte_count, file_size_ - offset));
-    if (read_size > 0) {
-      memcpy(buf, data() + offset, read_size);
-    }
-    return read_size;
-  } else {
-    return FdFile::Read(buf, byte_count, offset);
-  }
-}
-
-int MappedFile::SetLength(int64_t new_length) {
-  CHECK(!IsMapped());
-  return FdFile::SetLength(new_length);
-}
-
-int64_t MappedFile::GetLength() const {
-  if (IsMapped()) {
-    return file_size_;
-  } else {
-    return FdFile::GetLength();
-  }
-}
-
-int MappedFile::Flush() {
-  int rc = IsMapped() ? TEMP_FAILURE_RETRY(msync(mapped_file_, file_size_, 0)) : FdFile::Flush();
-  return rc == -1 ? -errno : 0;
-}
-
-int64_t MappedFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
-  if (IsMapped()) {
-    CHECK_EQ(kMapReadWrite, map_mode_);
-    if (offset < 0) {
-      errno = EINVAL;
-      return -errno;
-    }
-    int64_t write_size = std::max(static_cast<int64_t>(0),
-                                  std::min(byte_count, file_size_ - offset));
-    if (write_size > 0) {
-      memcpy(data() + offset, buf, write_size);
-    }
-    return write_size;
-  } else {
-    return FdFile::Write(buf, byte_count, offset);
-  }
-}
-
-int64_t MappedFile::size() const {
-  return GetLength();
-}
-
-bool MappedFile::IsMapped() const {
-  return mapped_file_ != NULL && mapped_file_ != MAP_FAILED;
-}
-
-char* MappedFile::data() const {
-  CHECK(IsMapped());
-  return static_cast<char*>(mapped_file_);
-}
-
-}  // namespace unix_file
diff --git a/runtime/base/unix_file/mapped_file.h b/runtime/base/unix_file/mapped_file.h
deleted file mode 100644
index 73056e9..0000000
--- a/runtime/base/unix_file/mapped_file.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2008 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_UNIX_FILE_MAPPED_FILE_H_
-#define ART_RUNTIME_BASE_UNIX_FILE_MAPPED_FILE_H_
-
-#include <fcntl.h>
-#include <string>
-#include "base/unix_file/fd_file.h"
-
-namespace unix_file {
-
-// Random access file which handles an mmap(2), munmap(2) pair in C++
-// RAII style. When a file is mmapped, the random access file
-// interface accesses the mmapped memory directly; otherwise, the
-// standard file I/O is used. Whenever a function fails, it returns
-// false and errno is set to the corresponding error code.
-class MappedFile : public FdFile {
- public:
-  // File modes used in Open().
-  enum FileMode {
-#ifdef __linux__
-    kReadOnlyMode = O_RDONLY | O_LARGEFILE,
-    kReadWriteMode = O_CREAT | O_RDWR | O_LARGEFILE,
-#else
-    kReadOnlyMode = O_RDONLY,
-    kReadWriteMode = O_CREAT | O_RDWR,
-#endif
-  };
-
-  MappedFile() : FdFile(), file_size_(-1), mapped_file_(NULL) {
-  }
-  // Creates a MappedFile using the given file descriptor. Takes ownership of
-  // the file descriptor.
-  explicit MappedFile(int fd) : FdFile(fd), file_size_(-1), mapped_file_(NULL) {
-  }
-
-  // Unmaps and closes the file if needed.
-  virtual ~MappedFile();
-
-  // Maps an opened file to memory in the read-only mode.
-  bool MapReadOnly();
-
-  // Maps an opened file to memory in the read-write mode. Before the
-  // file is mapped, it is truncated to 'file_size' bytes.
-  bool MapReadWrite(int64_t file_size);
-
-  // Unmaps a mapped file so that, e.g., SetLength() may be invoked.
-  bool Unmap();
-
-  // RandomAccessFile API.
-  // The functions below require that the file is open, but it doesn't
-  // have to be mapped.
-  virtual int Close();
-  virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
-  // SetLength() requires that the file is not mmapped.
-  virtual int SetLength(int64_t new_length);
-  virtual int64_t GetLength() const;
-  virtual int Flush();
-  // Write() requires that, if the file is mmapped, it is mmapped in
-  // the read-write mode. Writes past the end of file are discarded.
-  virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
-
-  // A convenience method equivalent to GetLength().
-  int64_t size() const;
-
-  // Returns true if the file has been mmapped.
-  bool IsMapped() const;
-
-  // Returns a pointer to the start of the memory mapping once the
-  // file is successfully mapped; crashes otherwise.
-  char* data() const;
-
- private:
-  enum MapMode {
-    kMapReadOnly = 1,
-    kMapReadWrite = 2,
-  };
-
-  mutable int64_t file_size_;  // May be updated in GetLength().
-  void* mapped_file_;
-  MapMode map_mode_;
-
-  DISALLOW_COPY_AND_ASSIGN(MappedFile);
-};
-
-}  // namespace unix_file
-
-#endif  // ART_RUNTIME_BASE_UNIX_FILE_MAPPED_FILE_H_
diff --git a/runtime/base/unix_file/mapped_file_test.cc b/runtime/base/unix_file/mapped_file_test.cc
deleted file mode 100644
index 59334d4..0000000
--- a/runtime/base/unix_file/mapped_file_test.cc
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2008 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 "base/unix_file/mapped_file.h"
-#include "base/logging.h"
-#include "base/unix_file/fd_file.h"
-#include "base/unix_file/random_access_file_test.h"
-#include "base/unix_file/random_access_file_utils.h"
-#include "base/unix_file/string_file.h"
-#include "gtest/gtest.h"
-
-namespace unix_file {
-
-class MappedFileTest : public RandomAccessFileTest {
- protected:
-  MappedFileTest() : kContent("some content") {
-  }
-
-  void SetUp() {
-    RandomAccessFileTest::SetUp();
-
-    good_path_ = GetTmpPath("some-file.txt");
-    int fd = TEMP_FAILURE_RETRY(open(good_path_.c_str(), O_CREAT|O_RDWR, 0666));
-    FdFile dst(fd);
-
-    StringFile src;
-    src.Assign(kContent);
-
-    ASSERT_TRUE(CopyFile(src, &dst));
-  }
-
-  void TearDown() {
-    ASSERT_EQ(unlink(good_path_.c_str()), 0);
-
-    RandomAccessFileTest::TearDown();
-  }
-
-  virtual RandomAccessFile* MakeTestFile() {
-    TEMP_FAILURE_RETRY(truncate(good_path_.c_str(), 0));
-    MappedFile* f = new MappedFile;
-    CHECK(f->Open(good_path_, MappedFile::kReadWriteMode));
-    return f;
-  }
-
-  const std::string kContent;
-  std::string good_path_;
-};
-
-TEST_F(MappedFileTest, OkayToNotUse) {
-  MappedFile file;
-  EXPECT_EQ(-1, file.Fd());
-  EXPECT_FALSE(file.IsOpened());
-  EXPECT_FALSE(file.IsMapped());
-}
-
-TEST_F(MappedFileTest, OpenClose) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
-  EXPECT_GE(file.Fd(), 0);
-  EXPECT_TRUE(file.IsOpened());
-  EXPECT_EQ(kContent.size(), static_cast<uint64_t>(file.size()));
-  EXPECT_EQ(0, file.Close());
-  EXPECT_EQ(-1, file.Fd());
-  EXPECT_FALSE(file.IsOpened());
-}
-
-TEST_F(MappedFileTest, OpenFdClose) {
-  FILE* f = tmpfile();
-  ASSERT_TRUE(f != NULL);
-  MappedFile file(fileno(f));
-  EXPECT_GE(file.Fd(), 0);
-  EXPECT_TRUE(file.IsOpened());
-  EXPECT_EQ(0, file.Close());
-}
-
-TEST_F(MappedFileTest, CanUseAfterMapReadOnly) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
-  EXPECT_FALSE(file.IsMapped());
-  EXPECT_TRUE(file.MapReadOnly());
-  EXPECT_TRUE(file.IsMapped());
-  EXPECT_EQ(kContent.size(), static_cast<uint64_t>(file.size()));
-  ASSERT_TRUE(file.data());
-  EXPECT_EQ(0, memcmp(kContent.c_str(), file.data(), file.size()));
-  EXPECT_EQ(0, file.Flush());
-}
-
-TEST_F(MappedFileTest, CanUseAfterMapReadWrite) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
-  EXPECT_FALSE(file.IsMapped());
-  EXPECT_TRUE(file.MapReadWrite(1));
-  EXPECT_TRUE(file.IsMapped());
-  EXPECT_EQ(1, file.size());
-  ASSERT_TRUE(file.data());
-  EXPECT_EQ(kContent[0], *file.data());
-  EXPECT_EQ(0, file.Flush());
-}
-
-TEST_F(MappedFileTest, CanWriteNewData) {
-  const std::string new_path(GetTmpPath("new-file.txt"));
-  ASSERT_EQ(-1, unlink(new_path.c_str()));
-  ASSERT_EQ(ENOENT, errno);
-
-  MappedFile file;
-  ASSERT_TRUE(file.Open(new_path, MappedFile::kReadWriteMode));
-  EXPECT_TRUE(file.MapReadWrite(kContent.size()));
-  EXPECT_TRUE(file.IsMapped());
-  EXPECT_EQ(kContent.size(), static_cast<uint64_t>(file.size()));
-  ASSERT_TRUE(file.data());
-  memcpy(file.data(), kContent.c_str(), kContent.size());
-  EXPECT_EQ(0, file.Close());
-  EXPECT_FALSE(file.IsMapped());
-
-  FdFile new_file(TEMP_FAILURE_RETRY(open(new_path.c_str(), O_RDONLY)));
-  StringFile buffer;
-  ASSERT_TRUE(CopyFile(new_file, &buffer));
-  EXPECT_EQ(kContent, buffer.ToStringPiece());
-  EXPECT_EQ(0, unlink(new_path.c_str()));
-}
-
-TEST_F(MappedFileTest, FileMustExist) {
-  const std::string bad_path(GetTmpPath("does-not-exist.txt"));
-  MappedFile file;
-  EXPECT_FALSE(file.Open(bad_path, MappedFile::kReadOnlyMode));
-  EXPECT_EQ(-1, file.Fd());
-}
-
-TEST_F(MappedFileTest, FileMustBeWritable) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
-  EXPECT_FALSE(file.MapReadWrite(10));
-}
-
-TEST_F(MappedFileTest, RemappingAllowedUntilSuccess) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
-  EXPECT_FALSE(file.MapReadWrite(10));
-  EXPECT_FALSE(file.MapReadWrite(10));
-}
-
-TEST_F(MappedFileTest, ResizeMappedFile) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
-  ASSERT_TRUE(file.MapReadWrite(10));
-  EXPECT_EQ(10, file.GetLength());
-  EXPECT_TRUE(file.Unmap());
-  EXPECT_TRUE(file.MapReadWrite(20));
-  EXPECT_EQ(20, file.GetLength());
-  EXPECT_EQ(0, file.Flush());
-  EXPECT_TRUE(file.Unmap());
-  EXPECT_EQ(0, file.Flush());
-  EXPECT_EQ(0, file.SetLength(5));
-  EXPECT_TRUE(file.MapReadOnly());
-  EXPECT_EQ(5, file.GetLength());
-}
-
-TEST_F(MappedFileTest, ReadNotMapped) {
-  TestRead();
-}
-
-TEST_F(MappedFileTest, SetLengthNotMapped) {
-  TestSetLength();
-}
-
-TEST_F(MappedFileTest, WriteNotMapped) {
-  TestWrite();
-}
-
-TEST_F(MappedFileTest, ReadMappedReadOnly) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
-  ASSERT_TRUE(file.MapReadOnly());
-  TestReadContent(kContent, &file);
-}
-
-TEST_F(MappedFileTest, ReadMappedReadWrite) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
-  ASSERT_TRUE(file.MapReadWrite(kContent.size()));
-  TestReadContent(kContent, &file);
-}
-
-TEST_F(MappedFileTest, WriteMappedReadWrite) {
-  TEMP_FAILURE_RETRY(unlink(good_path_.c_str()));
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
-  ASSERT_TRUE(file.MapReadWrite(kContent.size()));
-
-  // Can't write to a negative offset.
-  EXPECT_EQ(-EINVAL, file.Write(kContent.c_str(), 0, -123));
-
-  // A zero-length write is a no-op.
-  EXPECT_EQ(0, file.Write(kContent.c_str(), 0, 0));
-  // But the file size is as given when mapped.
-  EXPECT_EQ(kContent.size(), static_cast<uint64_t>(file.GetLength()));
-
-  // Data written past the end are discarded.
-  EXPECT_EQ(kContent.size() - 1,
-            static_cast<uint64_t>(file.Write(kContent.c_str(), kContent.size(), 1)));
-  EXPECT_EQ(0, memcmp(kContent.c_str(), file.data() + 1, kContent.size() - 1));
-
-  // Data can be overwritten.
-  EXPECT_EQ(kContent.size(),
-            static_cast<uint64_t>(file.Write(kContent.c_str(), kContent.size(), 0)));
-  EXPECT_EQ(0, memcmp(kContent.c_str(), file.data(), kContent.size()));
-}
-
-#if 0  // death tests don't work on android yet
-
-class MappedFileDeathTest : public MappedFileTest {};
-
-TEST_F(MappedFileDeathTest, MustMapBeforeUse) {
-  MappedFile file;
-  EXPECT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
-  EXPECT_DEATH(file.data(), "mapped_");
-}
-
-TEST_F(MappedFileDeathTest, RemappingNotAllowedReadOnly) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
-  ASSERT_TRUE(file.MapReadOnly());
-  EXPECT_DEATH(file.MapReadOnly(), "mapped_");
-}
-
-TEST_F(MappedFileDeathTest, RemappingNotAllowedReadWrite) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
-  ASSERT_TRUE(file.MapReadWrite(10));
-  EXPECT_DEATH(file.MapReadWrite(10), "mapped_");
-}
-
-TEST_F(MappedFileDeathTest, SetLengthMappedReadWrite) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
-  ASSERT_TRUE(file.MapReadWrite(10));
-  EXPECT_EQ(10, file.GetLength());
-  EXPECT_DEATH(file.SetLength(0), ".*");
-}
-
-TEST_F(MappedFileDeathTest, SetLengthMappedReadOnly) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
-  ASSERT_TRUE(file.MapReadOnly());
-  EXPECT_EQ(kContent.size(), file.GetLength());
-  EXPECT_DEATH(file.SetLength(0), ".*");
-}
-
-TEST_F(MappedFileDeathTest, WriteMappedReadOnly) {
-  MappedFile file;
-  ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
-  ASSERT_TRUE(file.MapReadOnly());
-  char buf[10];
-  EXPECT_DEATH(file.Write(buf, 0, 0), ".*");
-}
-
-#endif
-
-}  // namespace unix_file
diff --git a/runtime/base/unix_file/null_file.cc b/runtime/base/unix_file/null_file.cc
index 050decb..322c25a 100644
--- a/runtime/base/unix_file/null_file.cc
+++ b/runtime/base/unix_file/null_file.cc
@@ -33,7 +33,8 @@
   return 0;
 }
 
-int64_t NullFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
+int64_t NullFile::Read(char* buf ATTRIBUTE_UNUSED, int64_t byte_count ATTRIBUTE_UNUSED,
+                       int64_t offset) const {
   if (offset < 0) {
     return -EINVAL;
   }
@@ -51,7 +52,8 @@
   return 0;
 }
 
-int64_t NullFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
+int64_t NullFile::Write(const char* buf ATTRIBUTE_UNUSED, int64_t byte_count ATTRIBUTE_UNUSED,
+                        int64_t offset) {
   if (offset < 0) {
     return -EINVAL;
   }
diff --git a/runtime/log_severity.h b/runtime/base/value_object.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/base/value_object.h
index 31682df..8c752a9 100644
--- a/runtime/log_severity.h
+++ b/runtime/base/value_object.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_BASE_VALUE_OBJECT_H_
+#define ART_RUNTIME_BASE_VALUE_OBJECT_H_
 
-typedef int LogSeverity;
+#include "base/macros.h"
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+class ValueObject {
+ private:
+  DISALLOW_ALLOCATION();
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_VALUE_OBJECT_H_
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 9036e3d..fe5b765 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -14,17 +14,20 @@
  * limitations under the License.
  */
 
-#include "jni_internal.h"
+#include "check_jni.h"
 
 #include <sys/mman.h>
 #include <zlib.h>
 
 #include "base/logging.h"
+#include "base/to_str.h"
 #include "class_linker.h"
 #include "class_linker-inl.h"
 #include "dex_file-inl.h"
 #include "field_helper.h"
 #include "gc/space/space.h"
+#include "java_vm_ext.h"
+#include "jni_internal.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
@@ -35,62 +38,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 +66,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 +155,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 +246,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 +269,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 +290,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 +343,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 +356,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 +383,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, 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 +619,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 +643,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 +696,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 +1029,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 +1054,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) {
@@ -867,7 +1130,7 @@
           *errorKind = "continuation";
           return utf8;
         }
-        // Fall through to take care of the final byte.
+        FALLTHROUGH_INTENDED;  // Fall-through to take care of the final byte.
       case 0x0c:
       case 0x0d:
         // Bit pattern 110x, so there is one additional byte.
@@ -882,92 +1145,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 +1268,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 +1286,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 +1416,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.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.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_name);
+    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 +3610,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/log_severity.h b/runtime/check_jni.h
similarity index 69%
rename from runtime/log_severity.h
rename to runtime/check_jni.h
index 31682df..f41abf8 100644
--- a/runtime/log_severity.h
+++ b/runtime/check_jni.h
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_CHECK_JNI_H_
+#define ART_RUNTIME_CHECK_JNI_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+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..9d2d59c
--- /dev/null
+++ b/runtime/check_reference_map_visitor.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_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 == nullptr || 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::kInFpuRegister:
+          // In Fpu register, should not be a reference.
+          CHECK(false);
+          break;
+        case DexRegisterMap::kConstant:
+          CHECK_EQ(dex_register_map.GetValue(reg), 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..ead3fa5 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -105,8 +105,7 @@
 }
 
 inline mirror::ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx,
-                                                         mirror::ArtMethod* referrer,
-                                                         InvokeType type) {
+                                                         mirror::ArtMethod* referrer) {
   mirror::ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx);
   if (resolved_method == nullptr || resolved_method->IsRuntimeMethod()) {
     return nullptr;
@@ -117,7 +116,7 @@
 inline mirror::ArtMethod* ClassLinker::ResolveMethod(Thread* self, uint32_t method_idx,
                                                      mirror::ArtMethod** referrer,
                                                      InvokeType type) {
-  mirror::ArtMethod* resolved_method = GetResolvedMethod(method_idx, *referrer, type);
+  mirror::ArtMethod* resolved_method = GetResolvedMethod(method_idx, *referrer);
   if (LIKELY(resolved_method != nullptr)) {
     return resolved_method;
   }
@@ -155,6 +154,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 900bc9d..fb90b91 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -16,11 +16,10 @@
 
 #include "class_linker.h"
 
-#include <fcntl.h>
-#include <sys/file.h>
-#include <sys/stat.h>
 #include <deque>
+#include <iostream>
 #include <memory>
+#include <queue>
 #include <string>
 #include <utility>
 #include <vector>
@@ -34,6 +33,7 @@
 #include "compiler_callbacks.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc_root-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/heap_bitmap.h"
@@ -66,7 +66,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,25 +91,43 @@
   // 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());
+    }
   }
 }
 
-static void WrapExceptionInInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static void VlogClassInitializationFailure(Handle<mirror::Class> klass)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (VLOG_IS_ON(class_linker)) {
+    std::string temp;
+    LOG(INFO) << "Failed to initialize class " << klass->GetDescriptor(&temp) << " from "
+              << klass->GetLocation() << "\n" << Thread::Current()->GetException(nullptr)->Dump();
+  }
+}
+
+static void WrapExceptionInInitializer(Handle<mirror::Class> klass)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   Thread* self = Thread::Current();
   JNIEnv* env = self->GetJniEnv();
 
@@ -126,45 +144,90 @@
     self->ThrowNewWrappedException(throw_location, "Ljava/lang/ExceptionInInitializerError;",
                                    nullptr);
   }
+  VlogClassInitializationFailure(klass);
 }
 
-const char* ClassLinker::class_roots_descriptors_[] = {
-  "Ljava/lang/Class;",
-  "Ljava/lang/Object;",
-  "[Ljava/lang/Class;",
-  "[Ljava/lang/Object;",
-  "Ljava/lang/String;",
-  "Ljava/lang/DexCache;",
-  "Ljava/lang/ref/Reference;",
-  "Ljava/lang/reflect/ArtField;",
-  "Ljava/lang/reflect/ArtMethod;",
-  "Ljava/lang/reflect/Proxy;",
-  "[Ljava/lang/String;",
-  "[Ljava/lang/reflect/ArtField;",
-  "[Ljava/lang/reflect/ArtMethod;",
-  "Ljava/lang/ClassLoader;",
-  "Ljava/lang/Throwable;",
-  "Ljava/lang/ClassNotFoundException;",
-  "Ljava/lang/StackTraceElement;",
-  "Z",
-  "B",
-  "C",
-  "D",
-  "F",
-  "I",
-  "J",
-  "S",
-  "V",
-  "[Z",
-  "[B",
-  "[C",
-  "[D",
-  "[F",
-  "[I",
-  "[J",
-  "[S",
-  "[Ljava/lang/StackTraceElement;",
+// 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.
+static 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);
+  }
+}
 
 ClassLinker::ClassLinker(InternTable* intern_table)
     // dex_lock_ is recursive as it may be used in stack dumping.
@@ -184,16 +247,9 @@
       quick_imt_conflict_trampoline_(nullptr),
       quick_generic_jni_trampoline_(nullptr),
       quick_to_interpreter_bridge_trampoline_(nullptr) {
-  CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
   memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
 }
 
-// To set a value for generic JNI. May be necessary in compiler tests.
-extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
-extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
-
 void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class_path) {
   VLOG(startup) << "ClassLinker::Init";
   CHECK(!Runtime::Current()->GetHeap()->HasImageSpace()) << "Runtime has image. We should use it.";
@@ -217,6 +273,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
 
@@ -241,6 +298,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(
@@ -350,7 +413,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
@@ -368,12 +431,12 @@
 
   // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that
   // we do not need friend classes or a publicly exposed setter.
-  quick_generic_jni_trampoline_ = reinterpret_cast<void*>(art_quick_generic_jni_trampoline);
+  quick_generic_jni_trampoline_ = GetQuickGenericJniStub();
   if (!runtime->IsCompiler()) {
     // We need to set up the generic trampolines since we don't have an image.
-    quick_resolution_trampoline_ = reinterpret_cast<void*>(art_quick_resolution_trampoline);
-    quick_imt_conflict_trampoline_ = reinterpret_cast<void*>(art_quick_imt_conflict_trampoline);
-    quick_to_interpreter_bridge_trampoline_ = reinterpret_cast<void*>(art_quick_to_interpreter_bridge);
+    quick_resolution_trampoline_ = GetQuickResolutionStub();
+    quick_imt_conflict_trampoline_ = GetQuickImtConflictStub();
+    quick_to_interpreter_bridge_trampoline_ = GetQuickToInterpreterBridge();
   }
 
   // Object, String and DexCache need to be rerun through FindSystemClass to finish init
@@ -457,15 +520,15 @@
   CHECK_EQ(java_lang_reflect_ArtField.Get(), Art_field_class);
 
   mirror::Class* String_array_class =
-      FindSystemClass(self, class_roots_descriptors_[kJavaLangStringArrayClass]);
+      FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass));
   CHECK_EQ(object_array_string.Get(), String_array_class);
 
   mirror::Class* Art_method_array_class =
-      FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]);
+      FindSystemClass(self, GetClassRootDescriptor(kJavaLangReflectArtMethodArrayClass));
   CHECK_EQ(object_array_art_method.Get(), Art_method_array_class);
 
   mirror::Class* Art_field_array_class =
-      FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]);
+      FindSystemClass(self, GetClassRootDescriptor(kJavaLangReflectArtFieldArrayClass));
   CHECK_EQ(object_array_art_field.Get(), Art_field_array_class);
 
   // End of special init trickery, subsequent classes may be loaded via FindSystemClass.
@@ -579,7 +642,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();
     }
   }
@@ -745,7 +808,6 @@
 
     if (oat_dex_file == nullptr) {
       if (i == 0 && generated) {
-        std::string error_msg;
         error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out "
                                  " file'%s'", dex_location, next_location_checksum,
                                  oat_file->GetLocation().c_str());
@@ -1121,22 +1183,16 @@
   if (oat_dex_file == nullptr) {
     *error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x",
                               oat_file->GetLocation().c_str(), dex_location, dex_location_checksum);
-    for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
+    for (const OatFile::OatDexFile* oat_dex_file_in : oat_file->GetOatDexFiles()) {
       *error_msg  += StringPrintf("\noat file '%s' contains contents for '%s' with checksum 0x%x",
                                   oat_file->GetLocation().c_str(),
-                                  oat_dex_file->GetDexFileLocation().c_str(),
-                                  oat_dex_file->GetDexFileLocationChecksum());
+                                  oat_dex_file_in->GetDexFileLocation().c_str(),
+                                  oat_dex_file_in->GetDexFileLocationChecksum());
     }
     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;
 }
 
@@ -1291,7 +1347,6 @@
     if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, 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) {
@@ -1315,7 +1370,6 @@
     if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, 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
@@ -1477,16 +1531,15 @@
 bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
                                bool* checksum_verified,
                                std::string* error_msg) {
-  std::string compound_msg("Oat file failed to verify: ");
-  uint32_t real_image_checksum;
-  void* real_image_oat_offset;
-  int32_t real_patch_delta;
-  const gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
+  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();
@@ -1500,31 +1553,34 @@
   }
 
   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);
   }
 
   bool offset_verified;
   bool patch_delta_verified;
 
   if (!oat_file->IsPic()) {
+    // If an oat file is not PIC, we need to check that the image is at the expected location and
+    // patched in the same way.
     void* oat_image_oat_offset =
         reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
     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();
     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);
     }
   } else {
     // If an oat file is PIC, we ignore offset and patching delta.
@@ -1533,8 +1589,8 @@
   }
 
   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;
 }
@@ -1560,12 +1616,10 @@
   if (obj->IsArtMethod()) {
     mirror::ArtMethod* method = obj->AsArtMethod();
     if (!method->IsNative()) {
-      method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
+      method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
       if (method != Runtime::Current()->GetResolutionMethod()) {
         method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-#if defined(ART_USE_PORTABLE_COMPILER)
         method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
-#endif
       }
     }
   }
@@ -1609,8 +1663,8 @@
   CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(),
            static_cast<uint32_t>(dex_caches->GetLength()));
   for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
-    StackHandleScope<1> hs(self);
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_caches->Get(i)));
+    StackHandleScope<1> hs2(self);
+    Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i)));
     const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
     const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(),
                                                                      nullptr);
@@ -1793,7 +1847,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;
@@ -1803,7 +1857,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() + pre_zygote_class_table_.Size();
       }
       mirror::Class* class_type = mirror::Class::GetJavaLangClass();
@@ -1947,7 +2001,7 @@
     }
     CHECK(h_class->IsRetired());
     // Get the updated class from class table.
-    klass = LookupClass(descriptor, ComputeModifiedUtf8Hash(descriptor),
+    klass = LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor),
                         h_class.Get()->GetClassLoader());
   }
 
@@ -2005,7 +2059,7 @@
   ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
   // Check if this would be found in the parent boot class loader.
   if (pair.second != nullptr) {
-    mirror::Class* klass = LookupClass(descriptor, hash, nullptr);
+    mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr);
     if (klass != nullptr) {
       return EnsureResolved(self, descriptor, klass);
     }
@@ -2025,7 +2079,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());
@@ -2056,12 +2110,12 @@
               LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
               break;
             }
-            for (const DexFile* dex_file : *dex_files) {
-              const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash);
+            for (const DexFile* cp_dex_file : *dex_files) {
+              const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash);
               if (dex_class_def != nullptr) {
-                RegisterDexFile(*dex_file);
-                mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader, *dex_file,
-                                                   *dex_class_def);
+                RegisterDexFile(*cp_dex_file);
+                mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader,
+                                                   *cp_dex_file, *dex_class_def);
                 if (klass == nullptr) {
                   CHECK(self->IsExceptionPending()) << descriptor;
                   self->ClearException();
@@ -2090,7 +2144,7 @@
   }
   const size_t hash = ComputeModifiedUtf8Hash(descriptor);
   // Find the class in the loaded classes table.
-  mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get());
+  mirror::Class* klass = LookupClass(self, descriptor, hash, class_loader.Get());
   if (klass != nullptr) {
     return EnsureResolved(self, descriptor, klass);
   }
@@ -2114,7 +2168,7 @@
   } else if (Runtime::Current()->UseCompileTimeClassPath()) {
     // First try with the bootstrap class loader.
     if (class_loader.Get() != nullptr) {
-      klass = LookupClass(descriptor, hash, nullptr);
+      klass = LookupClass(self, descriptor, hash, nullptr);
       if (klass != nullptr) {
         return EnsureResolved(self, descriptor, klass);
       }
@@ -2137,12 +2191,18 @@
     pair = FindInClassPath(descriptor, hash, *class_path);
     if (pair.second != nullptr) {
       return DefineClass(self, descriptor, hash, 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);
-    mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader);
-    if (klass != nullptr) {
-      return klass;
+    mirror::Class* cp_klass = FindClassInPathClassLoader(soa, self, descriptor, hash,
+                                                         class_loader);
+    if (cp_klass != nullptr) {
+      return cp_klass;
     }
     ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                 soa.AddLocalReference<jobject>(class_loader.Get()));
@@ -2174,9 +2234,7 @@
       return soa.Decode<mirror::Class*>(result.get());
     }
   }
-
-  ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
-  return nullptr;
+  UNREACHABLE();
 }
 
 mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
@@ -2218,7 +2276,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
@@ -2285,8 +2343,10 @@
 
 uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file,
                                                        const DexFile::ClassDef& dex_class_def) {
-  const byte* class_data = dex_file.GetClassData(dex_class_def);
+  const uint8_t* 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) {
@@ -2294,35 +2354,51 @@
       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,
                                                  uint32_t method_idx) {
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx);
-  const byte* class_data = dex_file.GetClassData(class_def);
+  const uint8_t* class_data = dex_file.GetClassData(class_def);
   CHECK(class_data != nullptr);
   ClassDataItemIterator it(dex_file, class_data);
   // Skip fields
@@ -2353,8 +2429,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();
@@ -2367,31 +2442,30 @@
     // by search for its position in the declared virtual methods.
     oat_method_index = declaring_class->NumDirectMethods();
     size_t end = declaring_class->NumVirtualMethods();
-    bool found = false;
+    bool found_virtual = false;
     for (size_t i = 0; i < end; i++) {
       // Check method index instead of identity in case of duplicate method definitions.
       if (method->GetDexMethodIndex() ==
           declaring_class->GetVirtualMethod(i)->GetDexMethodIndex()) {
-        found = true;
+        found_virtual = true;
         break;
       }
       oat_method_index++;
     }
-    CHECK(found) << "Didn't find oat method index for virtual method: " << PrettyMethod(method);
+    CHECK(found_virtual) << "Didn't find oat method index for virtual method: "
+                         << PrettyMethod(method);
   }
   DCHECK_EQ(oat_method_index,
             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;
+  return oat_class.GetOatMethod(oat_method_index);
 }
 
 // Special case to get oat code without overwriting a trampoline.
@@ -2400,21 +2474,20 @@
   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();
   }
 
   if (result == nullptr) {
     if (method->IsNative()) {
       // No code and native? Use generic trampoline.
-      result = GetQuickGenericJniTrampoline();
-#if defined(ART_USE_PORTABLE_COMPILER)
+      result = GetQuickGenericJniStub();
     } 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();
@@ -2423,7 +2496,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);
@@ -2431,10 +2503,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();
   }
@@ -2452,29 +2525,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(
@@ -2514,7 +2604,7 @@
   const DexFile& dex_file = klass->GetDexFile();
   const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
   CHECK(dex_class_def != nullptr);
-  const byte* class_data = dex_file.GetClassData(*dex_class_def);
+  const uint8_t* class_data = dex_file.GetClassData(*dex_class_def);
   // There should always be class data if there were direct methods.
   CHECK(class_data != nullptr) << PrettyDescriptor(klass);
   ClassDataItemIterator it(dex_file, class_data);
@@ -2525,8 +2615,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);
@@ -2547,18 +2638,13 @@
       // Use interpreter entry point.
       // 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)
+        quick_code = GetQuickGenericJniStub();
         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 {
@@ -2567,11 +2653,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);
@@ -2579,44 +2660,37 @@
   // Ignore virtual methods on the iterator.
 }
 
-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()) {
+void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method,
+                           const OatFile::OatClass* oat_class,
+                           uint32_t class_def_method_index) {
+  Runtime* runtime = Runtime::Current();
+  if (runtime->IsCompiler()) {
     // The following code only applies to a non-compiler runtime.
     return;
   }
   // 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.
-    const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
+    const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);
     oat_method.LinkMethod(method.Get());
   }
 
   // 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);
+    method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
   } else {
     method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
   }
 
   if (method->IsAbstract()) {
     method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-#if defined(ART_USE_PORTABLE_COMPILER)
     method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
-#endif
     return;
   }
 
@@ -2625,61 +2699,49 @@
     // For static methods excluding the class initializer, install the trampoline.
     // 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
+    method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
+    method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionStub());
   } 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->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
       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()) {
     // Unregistering restores the dlsym lookup stub.
-    method->UnregisterNative(Thread::Current());
+    method->UnregisterNative();
 
     if (enter_interpreter) {
-      // We have a native method here without code. Then it should have either the GenericJni
-      // trampoline as entrypoint (non-static), or the Resolution trampoline (static).
-      DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickResolutionTrampoline()
-          || method->GetEntryPointFromQuickCompiledCode() == GetQuickGenericJniTrampoline());
+      // We have a native method here without code. Then it should have either the generic JNI
+      // trampoline as entrypoint (non-static), or the resolution trampoline (static).
+      // TODO: this doesn't handle all the cases where trampolines may be installed.
+      const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
+      DCHECK(IsQuickGenericJniStub(entry_point) || IsQuickResolutionStub(entry_point));
     }
   }
 
   // Allow instrumentation its chance to hijack code.
-  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) {
@@ -2703,29 +2765,31 @@
   klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
   klass->SetDexTypeIndex(dex_class_def.class_idx_);
 
-  const byte* class_data = dex_file.GetClassData(dex_class_def);
+  const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
   if (class_data == nullptr) {
     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, &oat_class);
+    }
+  }
+  if (!has_oat_class) {
+    LoadClassMembers(self, dex_file, class_data, klass, nullptr);
   }
 }
 
-void ClassLinker::LoadClassMembers(const DexFile& dex_file,
-                                   const byte* class_data,
+void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,
+                                   const uint8_t* 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)) {
@@ -2744,6 +2808,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)) {
@@ -2754,6 +2819,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)) {
@@ -2789,6 +2855,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)) {
@@ -2796,7 +2863,7 @@
       return;
     }
     klass->SetDirectMethod(i, method.Get());
-    LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
+    LinkCode(method, oat_class, class_def_method_index);
     uint32_t it_method_index = it.GetMemberIndex();
     if (last_dex_method_index == it_method_index) {
       // duplicate case
@@ -2809,6 +2876,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)) {
@@ -2817,14 +2885,15 @@
     }
     klass->SetVirtualMethod(i, method.Get());
     DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
-    LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
+    LinkCode(method, oat_class, class_def_method_index);
     class_def_method_index++;
   }
   DCHECK(!it.HasNext());
 }
 
 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());
@@ -2845,7 +2914,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());
@@ -2892,12 +2961,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 "
@@ -3050,12 +3117,13 @@
   // 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.
     const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1);
-    component_type.Assign(LookupClass(descriptor + 1, component_hash, class_loader.Get()));
+    component_type.Assign(LookupClass(self, descriptor + 1, component_hash, class_loader.Get()));
     if (component_type.Get() == nullptr) {
       DCHECK(self->IsExceptionPending());
       return nullptr;
@@ -3085,7 +3153,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, hash, component_type->GetClassLoader());
+    mirror::Class* new_class = LookupClass(self, descriptor, hash, component_type->GetClassLoader());
     if (new_class != nullptr) {
       return new_class;
     }
@@ -3106,13 +3174,13 @@
       new_class.Assign(GetClassRoot(kClassArrayClass));
     } else if (strcmp(descriptor, "[Ljava/lang/Object;") == 0) {
       new_class.Assign(GetClassRoot(kObjectArrayClass));
-    } else if (strcmp(descriptor, class_roots_descriptors_[kJavaLangStringArrayClass]) == 0) {
+    } else if (strcmp(descriptor, GetClassRootDescriptor(kJavaLangStringArrayClass)) == 0) {
       new_class.Assign(GetClassRoot(kJavaLangStringArrayClass));
     } else if (strcmp(descriptor,
-                      class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]) == 0) {
+                      GetClassRootDescriptor(kJavaLangReflectArtMethodArrayClass)) == 0) {
       new_class.Assign(GetClassRoot(kJavaLangReflectArtMethodArrayClass));
     } else if (strcmp(descriptor,
-                      class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]) == 0) {
+                      GetClassRootDescriptor(kJavaLangReflectArtFieldArrayClass)) == 0) {
       new_class.Assign(GetClassRoot(kJavaLangReflectArtFieldArrayClass));
     } else if (strcmp(descriptor, "[C") == 0) {
       new_class.Assign(GetClassRoot(kCharArrayClass));
@@ -3136,9 +3204,9 @@
   new_class->SetClassLoader(component_type->GetClassLoader());
   new_class->SetStatus(mirror::Class::kStatusLoaded, self);
   {
-    StackHandleScope<mirror::Class::kImtSize> hs(self,
-                                                 Runtime::Current()->GetImtUnimplementedMethod());
-    new_class->PopulateEmbeddedImtAndVTable(&hs);
+    StackHandleScope<mirror::Class::kImtSize> hs2(self,
+                                                  Runtime::Current()->GetImtUnimplementedMethod());
+    new_class->PopulateEmbeddedImtAndVTable(&hs2);
   }
   new_class->SetStatus(mirror::Class::kStatusInitialized, self);
   // don't need to set new_class->SetObjectSize(..)
@@ -3300,10 +3368,10 @@
   return false;
 }
 
-mirror::Class* ClassLinker::LookupClass(const char* descriptor, size_t hash,
+mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash,
                                         mirror::ClassLoader* class_loader) {
   {
-    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;
@@ -3357,8 +3425,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++) {
@@ -3384,7 +3451,6 @@
     }
   }
   dex_cache_image_class_lookup_required_ = false;
-  self->EndAssertNoThreadSuspension(old_no_suspend_cause);
 }
 
 void ClassLinker::MoveClassTableToPreZygote() {
@@ -3395,9 +3461,7 @@
 }
 
 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);
@@ -3411,13 +3475,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;
 }
 
@@ -3455,9 +3517,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.
@@ -3497,10 +3558,10 @@
   Handle<mirror::Class> super(hs.NewHandle(klass->GetSuperClass()));
   if (super.Get() != nullptr) {
     // Acquire lock to prevent races on verifying the super class.
-    ObjectLock<mirror::Class> lock(self, super);
+    ObjectLock<mirror::Class> super_lock(self, super);
 
     if (!super->IsVerified() && !super->IsErroneous()) {
-      VerifyClass(super);
+      VerifyClass(self, super);
     }
     if (!super->IsCompileTimeVerified()) {
       std::string error_msg(
@@ -3541,7 +3602,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);
   }
@@ -3576,7 +3637,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 {
@@ -3599,9 +3660,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();
   }
 }
 
@@ -3696,7 +3757,7 @@
   if (code_item->tries_size_ == 0) {
     return;  // nothing to process
   }
-  const byte* handlers_ptr = DexFile::GetCatchHandlerData(*code_item, 0);
+  const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item, 0);
   uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
   for (uint32_t idx = 0; idx < handlers_size; idx++) {
@@ -3725,7 +3786,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.
@@ -3802,10 +3863,10 @@
     klass->SetVirtualMethods(virtuals);
   }
   for (size_t i = 0; i < num_virtual_methods; ++i) {
-    StackHandleScope<1> hs(self);
+    StackHandleScope<1> hs2(self);
     mirror::ObjectArray<mirror::ArtMethod>* decoded_methods =
         soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods);
-    Handle<mirror::ArtMethod> prototype(hs.NewHandle(decoded_methods->Get(i)));
+    Handle<mirror::ArtMethod> prototype(hs2.NewHandle(decoded_methods->Get(i)));
     mirror::ArtMethod* clone = CreateProxyMethod(self, klass, prototype);
     if (UNLIKELY(clone == nullptr)) {
       CHECK(self->IsExceptionPending());  // OOME.
@@ -3854,11 +3915,11 @@
     CHECK(klass->GetIFields() == nullptr);
     CheckProxyConstructor(klass->GetDirectMethod(0));
     for (size_t i = 0; i < num_virtual_methods; ++i) {
-      StackHandleScope<2> hs(self);
+      StackHandleScope<2> hs2(self);
       mirror::ObjectArray<mirror::ArtMethod>* decoded_methods =
           soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods);
-      Handle<mirror::ArtMethod> prototype(hs.NewHandle(decoded_methods->Get(i)));
-      Handle<mirror::ArtMethod> virtual_method(hs.NewHandle(klass->GetVirtualMethod(i)));
+      Handle<mirror::ArtMethod> prototype(hs2.NewHandle(decoded_methods->Get(i)));
+      Handle<mirror::ArtMethod> virtual_method(hs2.NewHandle(klass->GetVirtualMethod(i)));
       CheckProxyMethod(virtual_method, prototype);
     }
 
@@ -3970,15 +4031,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());
@@ -3992,12 +4052,10 @@
   CHECK(prototype->HasSameDexCacheResolvedTypes(method.Get()));
   CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex());
 
-  MethodHelper mh(method);
-  MethodHelper mh2(prototype);
   CHECK_STREQ(method->GetName(), prototype->GetName());
   CHECK_STREQ(method->GetShorty(), prototype->GetShorty());
   // More complex sanity - via dex cache
-  CHECK_EQ(mh.GetReturnType(), mh2.GetReturnType());
+  CHECK_EQ(method->GetInterfaceMethodIfProxy()->GetReturnType(), prototype->GetReturnType());
 }
 
 static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
@@ -4034,12 +4092,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?
@@ -4054,7 +4108,7 @@
     return false;
   }
 
-  Thread* self = Thread::Current();
+  self->AllowThreadSuspension();
   uint64_t t0;
   {
     ObjectLock<mirror::Class> lock(self, klass);
@@ -4067,18 +4121,20 @@
     // Was the class already found to be erroneous? Done under the lock to match the JLS.
     if (klass->IsErroneous()) {
       ThrowEarlierClassFailure(klass.Get());
+      VlogClassInitializationFailure(klass);
       return false;
     }
 
     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.
         if (klass->IsErroneous()) {
           CHECK(self->IsExceptionPending());
+          VlogClassInitializationFailure(klass);
         } else {
           CHECK(Runtime::Current()->IsCompiler());
           CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
@@ -4097,6 +4153,7 @@
     if (klass->GetStatus() == mirror::Class::kStatusInitializing) {
       // Could have got an exception during verification.
       if (self->IsExceptionPending()) {
+        VlogClassInitializationFailure(klass);
         return false;
       }
       // We caught somebody else in the act; was it us?
@@ -4112,6 +4169,7 @@
       klass->SetStatus(mirror::Class::kStatusError, self);
       return false;
     }
+    self->AllowThreadSuspension();
 
     CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << PrettyClass(klass.Get());
 
@@ -4131,7 +4189,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.
@@ -4154,7 +4212,7 @@
     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()));
 
@@ -4171,20 +4229,23 @@
       }
     }
 
-    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 uint8_t* 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> hs2(self);
+        Handle<mirror::ArtField> field(hs2.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());
       }
     }
   }
@@ -4196,6 +4257,7 @@
     clinit->Invoke(self, nullptr, 0, &result, "V");
   }
 
+  self->AllowThreadSuspension();
   uint64_t t1 = NanoTime();
 
   bool success = true;
@@ -4203,7 +4265,7 @@
     ObjectLock<mirror::Class> lock(self, klass);
 
     if (self->IsExceptionPending()) {
-      WrapExceptionInInitializer();
+      WrapExceptionInInitializer(klass);
       klass->SetStatus(mirror::Class::kStatusError, self);
       success = false;
     } else {
@@ -4237,9 +4299,9 @@
 
     // When we wake up, repeat the test for init-in-progress.  If
     // there's an exception pending (only possible if
-    // "interruptShouldThrow" was set), bail out.
+    // we were not using WaitIgnoringInterrupts), bail out.
     if (self->IsExceptionPending()) {
-      WrapExceptionInInitializer();
+      WrapExceptionInInitializer(klass);
       klass->SetStatus(mirror::Class::kStatusError, self);
       return false;
     }
@@ -4256,6 +4318,7 @@
       // different thread.  Synthesize one here.
       ThrowNoClassDefFoundError("<clinit> failed for class %s; see exception in other thread",
                                 PrettyDescriptor(klass.Get()).c_str());
+      VlogClassInitializationFailure(klass);
       return false;
     }
     if (klass->IsInitialized()) {
@@ -4264,7 +4327,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) {
@@ -4272,16 +4335,17 @@
     return true;
   }
   // 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));
+  Thread* self = Thread::Current();
+  StackHandleScope<2> hs(self);
+  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) {
       mh.ChangeMethod(klass->GetVTableEntry(i));
       super_mh.ChangeMethod(klass->GetSuperClass()->GetVTableEntry(i));
       if (mh.GetMethod() != super_mh.GetMethod() &&
-          !mh.HasSameSignatureWithDifferentClassLoaders(&super_mh)) {
+          !mh.HasSameSignatureWithDifferentClassLoaders(self, &super_mh)) {
         ThrowLinkageError(klass.Get(),
                           "Class %s method %s resolves differently in superclass %s",
                           PrettyDescriptor(klass.Get()).c_str(),
@@ -4298,7 +4362,7 @@
         mh.ChangeMethod(klass->GetIfTable()->GetMethodArray(i)->GetWithoutChecks(j));
         super_mh.ChangeMethod(klass->GetIfTable()->GetInterface(i)->GetVirtualMethod(j));
         if (mh.GetMethod() != super_mh.GetMethod() &&
-            !mh.HasSameSignatureWithDifferentClassLoaders(&super_mh)) {
+            !mh.HasSameSignatureWithDifferentClassLoaders(self, &super_mh)) {
           ThrowLinkageError(klass.Get(),
                             "Class %s method %s resolves differently in interface %s",
                             PrettyDescriptor(klass.Get()).c_str(),
@@ -4312,15 +4376,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());
@@ -4331,20 +4394,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) {
@@ -4396,15 +4445,14 @@
   if (!LinkMethods(self, klass, interfaces, &imt_handle_scope)) {
     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)) {
@@ -4560,6 +4608,7 @@
 bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass,
                               Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                               StackHandleScope<mirror::Class::kImtSize>* out_imt) {
+  self->AllowThreadSuspension();
   if (klass->IsInterface()) {
     // No vtable.
     size_t count = klass->NumVirtualMethods();
@@ -4579,7 +4628,7 @@
 // Comparator for name and signature of a method, used in finding overriding methods. Implementation
 // avoids the use of handles, if it didn't then rather than compare dex files we could compare dex
 // caches in the implementation below.
-class MethodNameAndSignatureComparator FINAL {
+class MethodNameAndSignatureComparator FINAL : public ValueObject {
  public:
   explicit MethodNameAndSignatureComparator(mirror::ArtMethod* method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
@@ -4692,7 +4741,7 @@
     const size_t max_count = num_virtual_methods + super_vtable_length;
     StackHandleScope<2> 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)) {
@@ -4823,7 +4872,7 @@
 bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass,
                                        Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                                        StackHandleScope<mirror::Class::kImtSize>* out_imt) {
-  StackHandleScope<2> hs(self);
+  StackHandleScope<3> hs(self);
   Runtime* const runtime = Runtime::Current();
   const bool has_superclass = klass->HasSuperClass();
   const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
@@ -4866,7 +4915,7 @@
     }
     ifcount += interface->GetIfTableCount();
   }
-  Handle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
+  MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
   if (UNLIKELY(iftable.Get() == nullptr)) {
     CHECK(self->IsExceptionPending());  // OOME.
     return false;
@@ -4878,6 +4927,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++) {
@@ -4912,6 +4962,7 @@
       }
     }
   }
+  self->AllowThreadSuspension();
   // Shrink iftable in case duplicates were found
   if (idx < ifcount) {
     DCHECK_NE(num_interfaces, 0U);
@@ -4929,9 +4980,15 @@
   if (klass->IsInterface()) {
     return true;
   }
-  Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
+  size_t miranda_list_size = 0;
+  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();
+  }
+  MutableHandle<mirror::ObjectArray<mirror::ArtMethod>>
+      miranda_list(hs.NewHandle(AllocArtMethodArray(self, max_miranda_methods)));
+  MutableHandle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
       hs.NewHandle(klass->GetVTableDuringLinking()));
-  std::vector<mirror::ArtMethod*> miranda_list;
   // Copy the IMT from the super class if possible.
   bool extend_super_iftable = false;
   if (has_superclass) {
@@ -4973,9 +5030,10 @@
     }
   }
   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);
+      StackHandleScope<2> hs2(self);
       const bool is_super = i < super_ifcount;
       const bool super_interface = is_super && extend_super_iftable;
       Handle<mirror::ObjectArray<mirror::ArtMethod>> method_array;
@@ -4985,13 +5043,13 @@
         DCHECK(if_table != nullptr);
         DCHECK(if_table->GetMethodArray(i) != nullptr);
         // If we are working on a super interface, try extending the existing method array.
-        method_array = hs.NewHandle(if_table->GetMethodArray(i)->Clone(self)->
+        method_array = hs2.NewHandle(if_table->GetMethodArray(i)->Clone(self)->
             AsObjectArray<mirror::ArtMethod>());
         // We are overwriting a super class interface, try to only virtual methods instead of the
         // whole vtable.
-        input_array = hs.NewHandle(klass->GetVirtualMethods());
+        input_array = hs2.NewHandle(klass->GetVirtualMethods());
       } else {
-        method_array = hs.NewHandle(AllocArtMethodArray(self, num_methods));
+        method_array = hs2.NewHandle(AllocArtMethodArray(self, num_methods));
         // A new interface, we need the whole vtable incase a new interface method is implemented
         // in the whole superclass.
         input_array = vtable;
@@ -5057,31 +5115,31 @@
         }
         if (k < 0 && !super_interface) {
           mirror::ArtMethod* miranda_method = nullptr;
-          for (mirror::ArtMethod* mir_method : miranda_list) {
-            if (interface_name_comparator.HasSameNameAndSignature(
-                mir_method->GetInterfaceMethodIfProxy())) {
+          for (size_t l = 0; l < miranda_list_size; ++l) {
+            mirror::ArtMethod* mir_method = miranda_list->Get(l);
+            if (interface_name_comparator.HasSameNameAndSignature(mir_method)) {
               miranda_method = mir_method;
               break;
             }
           }
           if (miranda_method == nullptr) {
             // Point the interface table at a phantom slot.
-            miranda_method = down_cast<mirror::ArtMethod*>(interface_method->Clone(self));
+            miranda_method = interface_method->Clone(self)->AsArtMethod();
             if (UNLIKELY(miranda_method == 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);
+            DCHECK_LT(miranda_list_size, max_miranda_methods);
+            miranda_list->Set<false>(miranda_list_size++, miranda_method);
           }
           method_array->SetWithoutChecks<false>(j, miranda_method);
         }
       }
     }
   }
-  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);
@@ -5095,14 +5153,14 @@
     klass->SetVirtualMethods(virtuals);
 
     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));
@@ -5114,23 +5172,24 @@
   }
 
   if (kIsDebugBuild) {
-    mirror::ObjectArray<mirror::ArtMethod>* vtable = klass->GetVTableDuringLinking();
-    for (int i = 0; i < vtable->GetLength(); ++i) {
-      CHECK(vtable->GetWithoutChecks(i) != nullptr);
+    mirror::ObjectArray<mirror::ArtMethod>* check_vtable = klass->GetVTableDuringLinking();
+    for (int i = 0; i < check_vtable->GetLength(); ++i) {
+      CHECK(check_vtable->GetWithoutChecks(i) != nullptr);
     }
   }
 
+  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 {
@@ -5139,20 +5198,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.
@@ -5160,7 +5219,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();
 
@@ -5174,7 +5235,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 {
@@ -5191,6 +5252,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());
@@ -5202,6 +5265,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();
@@ -5209,51 +5274,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;")) {
@@ -5270,12 +5315,12 @@
     bool seen_non_ref = false;
     for (size_t i = 0; i < num_fields; i++) {
       mirror::ArtField* field = fields->Get(i);
-      if (false) {  // enable to debug field layout
+      if ((false)) {  // enable to debug field layout
         LOG(INFO) << "LinkFields: " << (is_static ? "static" : "instance")
                     << " class=" << PrettyClass(klass.Get())
                     << " field=" << PrettyField(field)
                     << " offset="
-                    << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()));
+                    << field->GetField32(mirror::ArtField::OffsetOffset());
       }
       Primitive::Type type = field->GetTypeAsPrimitiveType();
       bool is_primitive = type != Primitive::kPrimNot;
@@ -5323,51 +5368,34 @@
 void ClassLinker::CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) {
   uint32_t reference_offsets = 0;
   mirror::Class* super_class = klass->GetSuperClass();
+  // Leave the reference offsets as 0 for mirror::Object (the class field is handled specially).
   if (super_class != nullptr) {
     reference_offsets = super_class->GetReferenceInstanceOffsets();
-    // If our superclass overflowed, we don't stand a chance.
-    if (reference_offsets == CLASS_WALK_SUPER) {
-      klass->SetReferenceInstanceOffsets(reference_offsets);
-      return;
+    // Compute reference offsets unless our superclass overflowed.
+    if (reference_offsets != mirror::Class::kClassWalkSuper) {
+      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) {
+        // Note that byte_offset is the offset from the beginning of
+        // object, not the offset into instance data
+        mirror::ArtField* field = fields->Get(i);
+        MemberOffset byte_offset = field->GetOffsetDuringLinking();
+        uint32_t displaced_bitmap_position =
+            (byte_offset.Uint32Value() - mirror::kObjectHeaderSize) /
+            sizeof(mirror::HeapReference<mirror::Object>);
+        if (displaced_bitmap_position >= 32) {
+          // Can't encode offset so fall back on slow-path.
+          reference_offsets = mirror::Class::kClassWalkSuper;
+          break;
+        } else {
+          reference_offsets |= (1 << displaced_bitmap_position);
+        }
+      }
     }
   }
-  CreateReferenceOffsets(klass, false, reference_offsets);
-}
-
-void ClassLinker::CreateReferenceStaticOffsets(Handle<mirror::Class> klass) {
-  CreateReferenceOffsets(klass, true, 0);
-}
-
-void ClassLinker::CreateReferenceOffsets(Handle<mirror::Class> klass, bool is_static,
-                                         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();
-  // 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) {
-    // Note that byte_offset is the offset from the beginning of
-    // object, not the offset into instance data
-    mirror::ArtField* field = fields->Get(i);
-    MemberOffset byte_offset = field->GetOffsetDuringLinking();
-    CHECK_EQ(byte_offset.Uint32Value() & (CLASS_OFFSET_ALIGNMENT - 1), 0U);
-    if (CLASS_CAN_ENCODE_OFFSET(byte_offset.Uint32Value())) {
-      uint32_t new_bit = CLASS_BIT_FROM_OFFSET(byte_offset.Uint32Value());
-      CHECK_NE(new_bit, 0U);
-      reference_offsets |= new_bit;
-    } else {
-      reference_offsets = CLASS_WALK_SUPER;
-      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,
@@ -5460,6 +5488,7 @@
       break;
     default:
       LOG(FATAL) << "Unreachable - invocation type: " << type;
+      UNREACHABLE();
   }
   if (resolved == nullptr) {
     // Search by name, which works across dex files.
@@ -5679,11 +5708,91 @@
   }
 }
 
+static OatFile::OatMethod CreateOatMethod(const void* code, const uint8_t* gc_map,
+                                          bool is_portable) {
+  CHECK_EQ(kUsePortableCompiler, is_portable);
+  CHECK(code != nullptr);
+  const uint8_t* base;
+  uint32_t code_offset, gc_map_offset;
+  if (gc_map == nullptr) {
+    base = reinterpret_cast<const uint8_t*>(code);  // Base of data points at code.
+    base -= sizeof(void*);  // Move backward so that code_offset != 0.
+    code_offset = sizeof(void*);
+    gc_map_offset = 0;
+  } else {
+    // TODO: 64bit support.
+    base = nullptr;  // Base of data in oat file, ie 0.
+    code_offset = PointerToLowMemUInt32(code);
+    gc_map_offset = PointerToLowMemUInt32(gc_map);
+  }
+  return OatFile::OatMethod(base, code_offset, gc_map_offset);
+}
+
+bool ClassLinker::IsPortableResolutionStub(const void* entry_point) const {
+  return (entry_point == GetPortableResolutionStub()) ||
+      (portable_resolution_trampoline_ == entry_point);
+}
+
+bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
+  return (entry_point == GetQuickResolutionStub()) ||
+      (quick_resolution_trampoline_ == entry_point);
+}
+
+bool ClassLinker::IsPortableToInterpreterBridge(const void* entry_point) const {
+  return (entry_point == GetPortableToInterpreterBridge());
+  // TODO: portable_to_interpreter_bridge_trampoline_ == entry_point;
+}
+
+bool ClassLinker::IsQuickToInterpreterBridge(const void* entry_point) const {
+  return (entry_point == GetQuickToInterpreterBridge()) ||
+      (quick_to_interpreter_bridge_trampoline_ == entry_point);
+}
+
+bool ClassLinker::IsQuickGenericJniStub(const void* entry_point) const {
+  return (entry_point == GetQuickGenericJniStub()) ||
+      (quick_generic_jni_trampoline_ == entry_point);
+}
+
+const void* ClassLinker::GetRuntimeQuickGenericJniStub() const {
+  return GetQuickGenericJniStub();
+}
+
+void ClassLinker::SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code,
+                                               bool is_portable) const {
+  OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr, is_portable);
+  oat_method.LinkMethod(method);
+  method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
+  // Create bridges to transition between different kinds of compiled bridge.
+  if (method->GetEntryPointFromPortableCompiledCode() == nullptr) {
+    method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
+  } else {
+    CHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
+    method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
+    method->SetIsPortableCompiled();
+  }
+}
+
+void ClassLinker::SetEntryPointsToInterpreter(mirror::ArtMethod* method) const {
+  if (!method->IsNative()) {
+    method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
+    method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
+    method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
+  } else {
+    const void* quick_method_code = GetQuickGenericJniStub();
+    OatFile::OatMethod oat_method = CreateOatMethod(quick_method_code, nullptr, false);
+    oat_method.LinkMethod(method);
+    method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
+    method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
+  }
+}
+
 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 << "Zygote loaded classes=" << pre_zygote_class_table_.Size() << " post zygote classes="
      << class_table_.Size() << "\n";
 }
@@ -5717,6 +5826,52 @@
   class_roots->Set<false>(class_root, klass);
 }
 
+const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) {
+  static const char* class_roots_descriptors[] = {
+    "Ljava/lang/Class;",
+    "Ljava/lang/Object;",
+    "[Ljava/lang/Class;",
+    "[Ljava/lang/Object;",
+    "Ljava/lang/String;",
+    "Ljava/lang/DexCache;",
+    "Ljava/lang/ref/Reference;",
+    "Ljava/lang/reflect/ArtField;",
+    "Ljava/lang/reflect/ArtMethod;",
+    "Ljava/lang/reflect/Proxy;",
+    "[Ljava/lang/String;",
+    "[Ljava/lang/reflect/ArtField;",
+    "[Ljava/lang/reflect/ArtMethod;",
+    "Ljava/lang/ClassLoader;",
+    "Ljava/lang/Throwable;",
+    "Ljava/lang/ClassNotFoundException;",
+    "Ljava/lang/StackTraceElement;",
+    "Z",
+    "B",
+    "C",
+    "D",
+    "F",
+    "I",
+    "J",
+    "S",
+    "V",
+    "[Z",
+    "[B",
+    "[C",
+    "[D",
+    "[F",
+    "[I",
+    "[J",
+    "[S",
+    "[Ljava/lang/StackTraceElement;",
+  };
+  static_assert(arraysize(class_roots_descriptors) == size_t(kClassRootsMax),
+                "Mismatch between class descriptors and class-root enum");
+
+  const char* descriptor = class_roots_descriptors[class_root];
+  CHECK(descriptor != nullptr);
+  return descriptor;
+}
+
 std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& root)
     const {
   std::string temp;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 7fc394a..385f135 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>
@@ -27,7 +28,6 @@
 #include "base/mutex.h"
 #include "dex_file.h"
 #include "gc_root.h"
-#include "gtest/gtest.h"
 #include "jni.h"
 #include "oat_file.h"
 #include "object_callbacks.h"
@@ -48,6 +48,7 @@
   class StackTraceElement;
 }  // namespace mirror
 
+template<class T> class Handle;
 class InternTable;
 template<class T> class ObjectLock;
 class Runtime;
@@ -60,6 +61,46 @@
 
 class ClassLinker {
  public:
+  // Well known mirror::Class roots accessed via GetClassRoot.
+  enum ClassRoot {
+    kJavaLangClass,
+    kJavaLangObject,
+    kClassArrayClass,
+    kObjectArrayClass,
+    kJavaLangString,
+    kJavaLangDexCache,
+    kJavaLangRefReference,
+    kJavaLangReflectArtField,
+    kJavaLangReflectArtMethod,
+    kJavaLangReflectProxy,
+    kJavaLangStringArrayClass,
+    kJavaLangReflectArtFieldArrayClass,
+    kJavaLangReflectArtMethodArrayClass,
+    kJavaLangClassLoader,
+    kJavaLangThrowable,
+    kJavaLangClassNotFoundException,
+    kJavaLangStackTraceElement,
+    kPrimitiveBoolean,
+    kPrimitiveByte,
+    kPrimitiveChar,
+    kPrimitiveDouble,
+    kPrimitiveFloat,
+    kPrimitiveInt,
+    kPrimitiveLong,
+    kPrimitiveShort,
+    kPrimitiveVoid,
+    kBooleanArrayClass,
+    kByteArrayClass,
+    kCharArrayClass,
+    kDoubleArrayClass,
+    kFloatArrayClass,
+    kIntArrayClass,
+    kLongArrayClass,
+    kShortArrayClass,
+    kJavaLangStackTraceElementArrayClass,
+    kClassRootsMax,
+  };
+
   explicit ClassLinker(InternTable* intern_table);
   ~ClassLinker();
 
@@ -93,7 +134,9 @@
       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(Thread* self, const char* descriptor, size_t hash,
@@ -103,7 +146,7 @@
 
   // 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, size_t hash,
+  mirror::Class* LookupClass(Thread* self, const char* descriptor, size_t hash,
                              mirror::ClassLoader* class_loader)
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -126,8 +169,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_)
@@ -182,8 +224,7 @@
                                    InvokeType type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  mirror::ArtMethod* GetResolvedMethod(uint32_t method_idx, mirror::ArtMethod* referrer,
-                                       InvokeType type)
+  mirror::ArtMethod* GetResolvedMethod(uint32_t method_idx, mirror::ArtMethod* referrer)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   mirror::ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, mirror::ArtMethod** referrer,
                                    InvokeType type)
@@ -223,7 +264,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
@@ -298,6 +340,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)
@@ -322,7 +367,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_);
@@ -346,50 +392,58 @@
   // 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.
 
-  const void* GetPortableResolutionTrampoline() const {
-    return portable_resolution_trampoline_;
-  }
+  mirror::Class* GetClassRoot(ClassRoot class_root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  const void* GetQuickGenericJniTrampoline() const {
-    return quick_generic_jni_trampoline_;
-  }
+  static const char* GetClassRootDescriptor(ClassRoot class_root);
 
-  const void* GetQuickResolutionTrampoline() const {
-    return quick_resolution_trampoline_;
-  }
+  // Is the given entry point portable code to run the resolution stub?
+  bool IsPortableResolutionStub(const void* entry_point) const;
 
-  const void* GetPortableImtConflictTrampoline() const {
-    return portable_imt_conflict_trampoline_;
-  }
+  // Is the given entry point quick code to run the resolution stub?
+  bool IsQuickResolutionStub(const void* entry_point) const;
 
-  const void* GetQuickImtConflictTrampoline() const {
-    return quick_imt_conflict_trampoline_;
-  }
+  // Is the given entry point portable code to bridge into the interpreter?
+  bool IsPortableToInterpreterBridge(const void* entry_point) const;
 
-  const void* GetQuickToInterpreterBridgeTrampoline() const {
-    return quick_to_interpreter_bridge_trampoline_;
-  }
+  // Is the given entry point quick code to bridge into the interpreter?
+  bool IsQuickToInterpreterBridge(const void* entry_point) const;
+
+  // Is the given entry point quick code to run the generic JNI stub?
+  bool IsQuickGenericJniStub(const void* entry_point) const;
 
   InternTable* GetInternTable() const {
     return intern_table_;
   }
 
+  // Set the entrypoints up for method to the given code.
+  void SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code,
+                                    bool is_portable) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Set the entrypoints up for method to the enter the interpreter.
+  void SetEntryPointsToInterpreter(mirror::ArtMethod* method) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Attempts to insert a class into a class table.  Returns NULL if
   // the class was inserted, otherwise returns an existing class with
   // the same descriptor and ClassLoader.
@@ -418,7 +472,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  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)
@@ -450,30 +504,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,
-                        const OatFile::OatClass* oat_class)
+  void LoadClassMembers(Thread* self, const DexFile& dex_file, const uint8_t* class_data,
+                        Handle<mirror::Class> klass, const OatFile::OatClass* oat_class)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void LoadField(const DexFile& dex_file, const ClassDataItemIterator& it,
@@ -487,9 +532,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)
@@ -498,7 +543,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,
@@ -540,23 +585,17 @@
                             StackHandleScope<mirror::Class::kImtSize>* out_imt)
       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)
+                uint32_t class_def_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)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // For use by ImageWriter to find DexCaches for its roots
   ReaderWriterMutex* DexLock()
@@ -682,10 +721,16 @@
   void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void SetClassRoot(ClassRoot class_root, mirror::Class* klass)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Return the quick generic JNI stub for testing.
+  const void* GetRuntimeQuickGenericJniStub() const;
+
   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_);
 
@@ -733,62 +778,9 @@
   // the classes into the class_table_ to avoid dex cache based searches.
   Atomic<uint32_t> failed_dex_cache_class_lookups_;
 
-  // indexes into class_roots_.
-  // needs to be kept in sync with class_roots_descriptors_.
-  enum ClassRoot {
-    kJavaLangClass,
-    kJavaLangObject,
-    kClassArrayClass,
-    kObjectArrayClass,
-    kJavaLangString,
-    kJavaLangDexCache,
-    kJavaLangRefReference,
-    kJavaLangReflectArtField,
-    kJavaLangReflectArtMethod,
-    kJavaLangReflectProxy,
-    kJavaLangStringArrayClass,
-    kJavaLangReflectArtFieldArrayClass,
-    kJavaLangReflectArtMethodArrayClass,
-    kJavaLangClassLoader,
-    kJavaLangThrowable,
-    kJavaLangClassNotFoundException,
-    kJavaLangStackTraceElement,
-    kPrimitiveBoolean,
-    kPrimitiveByte,
-    kPrimitiveChar,
-    kPrimitiveDouble,
-    kPrimitiveFloat,
-    kPrimitiveInt,
-    kPrimitiveLong,
-    kPrimitiveShort,
-    kPrimitiveVoid,
-    kBooleanArrayClass,
-    kByteArrayClass,
-    kCharArrayClass,
-    kDoubleArrayClass,
-    kFloatArrayClass,
-    kIntArrayClass,
-    kLongArrayClass,
-    kShortArrayClass,
-    kJavaLangStackTraceElementArrayClass,
-    kClassRootsMax,
-  };
+  // Well known mirror::Class roots.
   GcRoot<mirror::ObjectArray<mirror::Class>> class_roots_;
 
-  ALWAYS_INLINE mirror::Class* GetClassRoot(ClassRoot class_root)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  void SetClassRoot(ClassRoot class_root, mirror::Class* klass)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  static const char* class_roots_descriptors_[];
-
-  const char* GetClassRootDescriptor(ClassRoot class_root) {
-    const char* descriptor = class_roots_descriptors_[class_root];
-    CHECK(descriptor != NULL);
-    return descriptor;
-  }
-
   // The interface table used by all arrays.
   GcRoot<mirror::IfTable> array_iftable_;
 
@@ -816,12 +808,11 @@
   friend class ImageWriter;  // for GetClassRoots
   friend class ImageDumper;  // for FindOpenedOatFileFromOatLocation
   friend class ElfPatcher;  // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
+  friend class JniCompilerTest;  // for GetRuntimeQuickGenericJniStub
   friend class NoDex2OatTest;  // for FindOpenedOatFileForDexFile
   friend class NoPatchoatTest;  // for FindOpenedOatFileForDexFile
-  FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
-  FRIEND_TEST(mirror::DexCacheTest, Open);
-  FRIEND_TEST(ExceptionTest, FindExceptionHandler);
-  FRIEND_TEST(ObjectTest, AllocObjectArray);
+  ART_FRIEND_TEST(mirror::DexCacheTest, Open);  // for AllocDexCache
+
   DISALLOW_COPY_AND_ASSIGN(ClassLinker);
 };
 
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 2d45d03..b257343 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -47,9 +47,9 @@
   void AssertNonExistentClass(const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     Thread* self = Thread::Current();
-    EXPECT_TRUE(class_linker_->FindSystemClass(self, descriptor.c_str()) == NULL);
+    EXPECT_TRUE(class_linker_->FindSystemClass(self, descriptor.c_str()) == nullptr);
     EXPECT_TRUE(self->IsExceptionPending());
-    mirror::Object* exception = self->GetException(NULL);
+    mirror::Object* exception = self->GetException(nullptr);
     self->ClearException();
     mirror::Class* exception_class =
         class_linker_->FindSystemClass(self, "Ljava/lang/NoClassDefFoundError;");
@@ -64,15 +64,15 @@
 
   void AssertPrimitiveClass(const std::string& descriptor, mirror::Class* primitive)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    ASSERT_TRUE(primitive != NULL);
-    ASSERT_TRUE(primitive->GetClass() != NULL);
+    ASSERT_TRUE(primitive != nullptr);
+    ASSERT_TRUE(primitive->GetClass() != nullptr);
     ASSERT_EQ(primitive->GetClass(), primitive->GetClass()->GetClass());
-    EXPECT_TRUE(primitive->GetClass()->GetSuperClass() != NULL);
+    EXPECT_TRUE(primitive->GetClass()->GetSuperClass() != nullptr);
     std::string temp;
     ASSERT_STREQ(descriptor.c_str(), primitive->GetDescriptor(&temp));
-    EXPECT_TRUE(primitive->GetSuperClass() == NULL);
+    EXPECT_TRUE(primitive->GetSuperClass() == nullptr);
     EXPECT_FALSE(primitive->HasSuperClass());
-    EXPECT_TRUE(primitive->GetClassLoader() == NULL);
+    EXPECT_TRUE(primitive->GetClassLoader() == nullptr);
     EXPECT_EQ(mirror::Class::kStatusInitialized, primitive->GetStatus());
     EXPECT_FALSE(primitive->IsErroneous());
     EXPECT_TRUE(primitive->IsLoaded());
@@ -81,7 +81,7 @@
     EXPECT_TRUE(primitive->IsInitialized());
     EXPECT_FALSE(primitive->IsArrayInstance());
     EXPECT_FALSE(primitive->IsArrayClass());
-    EXPECT_TRUE(primitive->GetComponentType() == NULL);
+    EXPECT_TRUE(primitive->GetComponentType() == nullptr);
     EXPECT_FALSE(primitive->IsInterface());
     EXPECT_TRUE(primitive->IsPublic());
     EXPECT_TRUE(primitive->IsFinal());
@@ -94,7 +94,7 @@
     EXPECT_EQ(0U, primitive->NumDirectInterfaces());
     EXPECT_FALSE(primitive->HasVTable());
     EXPECT_EQ(0, primitive->GetIfTableCount());
-    EXPECT_TRUE(primitive->GetIfTable() == NULL);
+    EXPECT_TRUE(primitive->GetIfTable() == nullptr);
     EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags());
   }
 
@@ -116,17 +116,17 @@
 
   void AssertArrayClass(const std::string& array_descriptor, Handle<mirror::Class> array)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    ASSERT_TRUE(array.Get() != NULL);
-    ASSERT_TRUE(array->GetClass() != NULL);
+    ASSERT_TRUE(array.Get() != nullptr);
+    ASSERT_TRUE(array->GetClass() != nullptr);
     ASSERT_EQ(array->GetClass(), array->GetClass()->GetClass());
-    EXPECT_TRUE(array->GetClass()->GetSuperClass() != NULL);
+    EXPECT_TRUE(array->GetClass()->GetSuperClass() != nullptr);
     std::string temp;
     ASSERT_STREQ(array_descriptor.c_str(), array->GetDescriptor(&temp));
-    EXPECT_TRUE(array->GetSuperClass() != NULL);
+    EXPECT_TRUE(array->GetSuperClass() != nullptr);
     Thread* self = Thread::Current();
     EXPECT_EQ(class_linker_->FindSystemClass(self, "Ljava/lang/Object;"), array->GetSuperClass());
     EXPECT_TRUE(array->HasSuperClass());
-    ASSERT_TRUE(array->GetComponentType() != NULL);
+    ASSERT_TRUE(array->GetComponentType() != nullptr);
     ASSERT_GT(strlen(array->GetComponentType()->GetDescriptor(&temp)), 0U);
     EXPECT_EQ(mirror::Class::kStatusInitialized, array->GetStatus());
     EXPECT_FALSE(array->IsErroneous());
@@ -148,7 +148,7 @@
     EXPECT_EQ(2U, array->NumDirectInterfaces());
     EXPECT_TRUE(array->ShouldHaveEmbeddedImtAndVTable());
     EXPECT_EQ(2, array->GetIfTableCount());
-    ASSERT_TRUE(array->GetIfTable() != NULL);
+    ASSERT_TRUE(array->GetIfTable() != nullptr);
     mirror::Class* direct_interface0 = mirror::Class::GetDirectInterface(self, array, 0);
     EXPECT_TRUE(direct_interface0 != nullptr);
     EXPECT_STREQ(direct_interface0->GetDescriptor(&temp), "Ljava/lang/Cloneable;");
@@ -177,13 +177,13 @@
 
   void AssertField(mirror::Class* klass, mirror::ArtField* field)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    EXPECT_TRUE(field != NULL);
-    EXPECT_TRUE(field->GetClass() != NULL);
+    EXPECT_TRUE(field != nullptr);
+    EXPECT_TRUE(field->GetClass() != nullptr);
     EXPECT_EQ(klass, field->GetDeclaringClass());
-    EXPECT_TRUE(field->GetName() != NULL);
+    EXPECT_TRUE(field->GetName() != nullptr);
     StackHandleScope<1> hs(Thread::Current());
     FieldHelper fh(hs.NewHandle(field));
-    EXPECT_TRUE(fh.GetType() != NULL);
+    EXPECT_TRUE(fh.GetType() != nullptr);
   }
 
   void AssertClass(const std::string& descriptor, Handle<mirror::Class> klass)
@@ -194,16 +194,16 @@
       EXPECT_FALSE(klass->HasSuperClass());
     } else {
       EXPECT_TRUE(klass->HasSuperClass());
-      EXPECT_TRUE(klass->GetSuperClass() != NULL);
+      EXPECT_TRUE(klass->GetSuperClass() != nullptr);
     }
-    EXPECT_TRUE(klass->GetClass() != NULL);
+    EXPECT_TRUE(klass->GetClass() != nullptr);
     EXPECT_EQ(klass->GetClass(), klass->GetClass()->GetClass());
-    EXPECT_TRUE(klass->GetDexCache() != NULL);
+    EXPECT_TRUE(klass->GetDexCache() != nullptr);
     EXPECT_TRUE(klass->IsLoaded());
     EXPECT_TRUE(klass->IsResolved());
     EXPECT_FALSE(klass->IsErroneous());
     EXPECT_FALSE(klass->IsArrayClass());
-    EXPECT_TRUE(klass->GetComponentType() == NULL);
+    EXPECT_TRUE(klass->GetComponentType() == nullptr);
     EXPECT_TRUE(klass->IsInSamePackage(klass.Get()));
     std::string temp2;
     EXPECT_TRUE(mirror::Class::IsInSamePackage(klass->GetDescriptor(&temp),
@@ -225,7 +225,7 @@
     mirror::IfTable* iftable = klass->GetIfTable();
     for (int i = 0; i < klass->GetIfTableCount(); i++) {
       mirror::Class* interface = iftable->GetInterface(i);
-      ASSERT_TRUE(interface != NULL);
+      ASSERT_TRUE(interface != nullptr);
       if (klass->IsInterface()) {
         EXPECT_EQ(0U, iftable->GetMethodArrayCount(i));
       } else {
@@ -278,20 +278,22 @@
     // 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);
+      ASSERT_TRUE(field_type != nullptr);
       ASSERT_TRUE(!field_type->IsPrimitive());
     }
     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);
+      ASSERT_TRUE(field_type != nullptr);
       if (!fh.GetField()->IsPrimitiveType() || !field_type->IsPrimitive()) {
         // While Reference.referent is not primitive, the ClassLinker
         // treats it as such so that the garbage collector won't scan it.
@@ -299,18 +301,24 @@
       }
     }
 
-    size_t total_num_reference_instance_fields = 0;
+    uint32_t total_num_reference_instance_fields = 0;
     mirror::Class* k = klass.Get();
-    while (k != NULL) {
+    while (k != nullptr) {
       total_num_reference_instance_fields += k->NumReferenceInstanceFields();
       k = k->GetSuperClass();
     }
-    EXPECT_EQ(klass->GetReferenceInstanceOffsets() == 0, total_num_reference_instance_fields == 0);
+    EXPECT_GE(total_num_reference_instance_fields, 1U);  // Should always have Object's class.
+    if (klass->GetReferenceInstanceOffsets() != mirror::Class::kClassWalkSuper) {
+      // The reference instance offsets have a bit set for each reference offset.
+      // +1 for Object's class.
+      EXPECT_EQ(static_cast<uint32_t>(POPCOUNT(klass->GetReferenceInstanceOffsets())) + 1,
+                total_num_reference_instance_fields);
+    }
   }
 
   void AssertDexFileClass(mirror::ClassLoader* class_loader, const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    ASSERT_TRUE(descriptor != NULL);
+    ASSERT_TRUE(descriptor != nullptr);
     Thread* self = Thread::Current();
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> klass(
@@ -330,7 +338,7 @@
 
   void AssertDexFile(const DexFile* dex, mirror::ClassLoader* class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    ASSERT_TRUE(dex != NULL);
+    ASSERT_TRUE(dex != nullptr);
 
     // Verify all the classes defined in this file
     for (size_t i = 0; i < dex->NumClassDefs(); i++) {
@@ -344,17 +352,17 @@
       const char* descriptor = dex->GetTypeDescriptor(type_id);
       AssertDexFileClass(class_loader, descriptor);
     }
-    class_linker_->VisitRoots(TestRootVisitor, NULL, kVisitRootFlagAllRoots);
+    class_linker_->VisitRoots(TestRootVisitor, nullptr, kVisitRootFlagAllRoots);
     // Verify the dex cache has resolution methods in all resolved method slots
     mirror::DexCache* dex_cache = class_linker_->FindDexCache(*dex);
     mirror::ObjectArray<mirror::ArtMethod>* resolved_methods = dex_cache->GetResolvedMethods();
     for (size_t i = 0; i < static_cast<size_t>(resolved_methods->GetLength()); i++) {
-      EXPECT_TRUE(resolved_methods->Get(i) != NULL) << dex->GetLocation() << " i=" << i;
+      EXPECT_TRUE(resolved_methods->Get(i) != nullptr) << dex->GetLocation() << " i=" << i;
     }
   }
 
   static void TestRootVisitor(mirror::Object** root, void*, uint32_t, RootType) {
-    EXPECT_TRUE(*root != NULL);
+    EXPECT_TRUE(*root != nullptr);
   }
 };
 
@@ -366,8 +374,8 @@
 
 template <typename T>
 struct CheckOffsets {
-  CheckOffsets(bool is_static, const char* class_descriptor)
-      : is_static(is_static), class_descriptor(class_descriptor) {}
+  CheckOffsets(bool is_static_in, const char* class_descriptor_in)
+      : is_static(is_static_in), class_descriptor(class_descriptor_in) {}
   bool is_static;
   std::string class_descriptor;
   std::vector<CheckOffset> offsets;
@@ -376,7 +384,7 @@
     Thread* self = Thread::Current();
     mirror::Class* klass =
         Runtime::Current()->GetClassLinker()->FindSystemClass(self, class_descriptor.c_str());
-    CHECK(klass != NULL) << class_descriptor;
+    CHECK(klass != nullptr) << class_descriptor;
 
     bool error = false;
 
@@ -488,9 +496,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"));
 
@@ -529,7 +535,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"));
   };
 };
@@ -647,12 +652,12 @@
       hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Nested"))));
 
   mirror::Class* outer = class_linker_->FindClass(soa.Self(), "LNested;", class_loader);
-  ASSERT_TRUE(outer != NULL);
+  ASSERT_TRUE(outer != nullptr);
   EXPECT_EQ(0U, outer->NumVirtualMethods());
   EXPECT_EQ(1U, outer->NumDirectMethods());
 
   mirror::Class* inner = class_linker_->FindClass(soa.Self(), "LNested$Inner;", class_loader);
-  ASSERT_TRUE(inner != NULL);
+  ASSERT_TRUE(inner != nullptr);
   EXPECT_EQ(0U, inner->NumVirtualMethods());
   EXPECT_EQ(1U, inner->NumDirectMethods());
 }
@@ -674,15 +679,15 @@
 TEST_F(ClassLinkerTest, FindClass) {
   ScopedObjectAccess soa(Thread::Current());
   mirror::Class* JavaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
-  ASSERT_TRUE(JavaLangObject != NULL);
-  ASSERT_TRUE(JavaLangObject->GetClass() != NULL);
+  ASSERT_TRUE(JavaLangObject != nullptr);
+  ASSERT_TRUE(JavaLangObject->GetClass() != nullptr);
   ASSERT_EQ(JavaLangObject->GetClass(), JavaLangObject->GetClass()->GetClass());
   EXPECT_EQ(JavaLangObject, JavaLangObject->GetClass()->GetSuperClass());
   std::string temp;
   ASSERT_STREQ(JavaLangObject->GetDescriptor(&temp), "Ljava/lang/Object;");
-  EXPECT_TRUE(JavaLangObject->GetSuperClass() == NULL);
+  EXPECT_TRUE(JavaLangObject->GetSuperClass() == nullptr);
   EXPECT_FALSE(JavaLangObject->HasSuperClass());
-  EXPECT_TRUE(JavaLangObject->GetClassLoader() == NULL);
+  EXPECT_TRUE(JavaLangObject->GetClassLoader() == nullptr);
   EXPECT_EQ(mirror::Class::kStatusInitialized, JavaLangObject->GetStatus());
   EXPECT_FALSE(JavaLangObject->IsErroneous());
   EXPECT_TRUE(JavaLangObject->IsLoaded());
@@ -691,7 +696,7 @@
   EXPECT_TRUE(JavaLangObject->IsInitialized());
   EXPECT_FALSE(JavaLangObject->IsArrayInstance());
   EXPECT_FALSE(JavaLangObject->IsArrayClass());
-  EXPECT_TRUE(JavaLangObject->GetComponentType() == NULL);
+  EXPECT_TRUE(JavaLangObject->GetComponentType() == nullptr);
   EXPECT_FALSE(JavaLangObject->IsInterface());
   EXPECT_TRUE(JavaLangObject->IsPublic());
   EXPECT_FALSE(JavaLangObject->IsFinal());
@@ -719,8 +724,8 @@
       hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass"))));
   AssertNonExistentClass("LMyClass;");
   mirror::Class* MyClass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader);
-  ASSERT_TRUE(MyClass != NULL);
-  ASSERT_TRUE(MyClass->GetClass() != NULL);
+  ASSERT_TRUE(MyClass != nullptr);
+  ASSERT_TRUE(MyClass->GetClass() != nullptr);
   ASSERT_EQ(MyClass->GetClass(), MyClass->GetClass()->GetClass());
   EXPECT_EQ(JavaLangObject, MyClass->GetClass()->GetSuperClass());
   ASSERT_STREQ(MyClass->GetDescriptor(&temp), "LMyClass;");
@@ -735,7 +740,7 @@
   EXPECT_FALSE(MyClass->IsInitialized());
   EXPECT_FALSE(MyClass->IsArrayInstance());
   EXPECT_FALSE(MyClass->IsArrayClass());
-  EXPECT_TRUE(MyClass->GetComponentType() == NULL);
+  EXPECT_TRUE(MyClass->GetComponentType() == nullptr);
   EXPECT_FALSE(MyClass->IsInterface());
   EXPECT_FALSE(MyClass->IsPublic());
   EXPECT_FALSE(MyClass->IsFinal());
@@ -750,10 +755,10 @@
   EXPECT_EQ(JavaLangObject->GetClass()->GetClass(), MyClass->GetClass()->GetClass());
 
   // created by class_linker
-  AssertArrayClass("[C", "C", NULL);
-  AssertArrayClass("[Ljava/lang/Object;", "Ljava/lang/Object;", NULL);
+  AssertArrayClass("[C", "C", nullptr);
+  AssertArrayClass("[Ljava/lang/Object;", "Ljava/lang/Object;", nullptr);
   // synthesized on the fly
-  AssertArrayClass("[[C", "[C", NULL);
+  AssertArrayClass("[[C", "[C", nullptr);
   AssertArrayClass("[[[LMyClass;", "[[LMyClass;", class_loader.Get());
   // or not available at all
   AssertNonExistentClass("[[[[LNonExistentClass;");
@@ -761,7 +766,7 @@
 
 TEST_F(ClassLinkerTest, LibCore) {
   ScopedObjectAccess soa(Thread::Current());
-  AssertDexFile(java_lang_dex_file_, NULL);
+  AssertDexFile(java_lang_dex_file_, nullptr);
 }
 
 // The first reference array element must be a multiple of 4 bytes from the
@@ -857,13 +862,13 @@
       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.
   // So <clinit> should be null.
   mirror::ArtMethod* clinit = statics->FindDirectMethod("<clinit>", "()V");
-  EXPECT_TRUE(clinit == NULL);
+  EXPECT_TRUE(clinit == nullptr);
 
   EXPECT_EQ(9U, statics->NumStaticFields());
 
@@ -901,12 +906,12 @@
 
   mirror::ArtField* s6 = mirror::Class::FindStaticField(soa.Self(), statics, "s6", "F");
   EXPECT_EQ(s6->GetTypeAsPrimitiveType(), Primitive::kPrimFloat);
-  EXPECT_EQ(0.5, s6->GetFloat(statics.Get()));
+  EXPECT_DOUBLE_EQ(0.5, s6->GetFloat(statics.Get()));
   s6->SetFloat<false>(statics.Get(), 0.75);
 
   mirror::ArtField* s7 = mirror::Class::FindStaticField(soa.Self(), statics, "s7", "D");
   EXPECT_EQ(s7->GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
-  EXPECT_EQ(16777217, s7->GetDouble(statics.Get()));
+  EXPECT_DOUBLE_EQ(16777217.0, s7->GetDouble(statics.Get()));
   s7->SetDouble<false>(statics.Get(), 16777219);
 
   mirror::ArtField* s8 = mirror::Class::FindStaticField(soa.Self(), statics, "s8",
@@ -924,8 +929,8 @@
   EXPECT_EQ(-535, s3->GetShort(statics.Get()));
   EXPECT_EQ(2000000001, s4->GetInt(statics.Get()));
   EXPECT_EQ(INT64_C(0x34567890abcdef12), s5->GetLong(statics.Get()));
-  EXPECT_EQ(0.75, s6->GetFloat(statics.Get()));
-  EXPECT_EQ(16777219, s7->GetDouble(statics.Get()));
+  EXPECT_FLOAT_EQ(0.75, s6->GetFloat(statics.Get()));
+  EXPECT_DOUBLE_EQ(16777219.0, s7->GetDouble(statics.Get()));
   EXPECT_TRUE(s8->GetObject(statics.Get())->AsString()->Equals("robot"));
 }
 
@@ -960,15 +965,15 @@
   mirror::ArtMethod* Ai = A->FindVirtualMethod("i", void_sig);
   mirror::ArtMethod* Aj1 = A->FindVirtualMethod("j1", void_sig);
   mirror::ArtMethod* Aj2 = A->FindVirtualMethod("j2", void_sig);
-  ASSERT_TRUE(Ii != NULL);
-  ASSERT_TRUE(Jj1 != NULL);
-  ASSERT_TRUE(Jj2 != NULL);
-  ASSERT_TRUE(Kj1 != NULL);
-  ASSERT_TRUE(Kj2 != NULL);
-  ASSERT_TRUE(Kk != NULL);
-  ASSERT_TRUE(Ai != NULL);
-  ASSERT_TRUE(Aj1 != NULL);
-  ASSERT_TRUE(Aj2 != NULL);
+  ASSERT_TRUE(Ii != nullptr);
+  ASSERT_TRUE(Jj1 != nullptr);
+  ASSERT_TRUE(Jj2 != nullptr);
+  ASSERT_TRUE(Kj1 != nullptr);
+  ASSERT_TRUE(Kj2 != nullptr);
+  ASSERT_TRUE(Kk != nullptr);
+  ASSERT_TRUE(Ai != nullptr);
+  ASSERT_TRUE(Aj1 != nullptr);
+  ASSERT_TRUE(Aj2 != nullptr);
   EXPECT_NE(Ii, Ai);
   EXPECT_NE(Jj1, Aj1);
   EXPECT_NE(Jj2, Aj2);
@@ -989,7 +994,7 @@
                                                           "Ljava/lang/String;");
   mirror::ArtField* Kfoo = mirror::Class::FindStaticField(soa.Self(), K, "foo",
                                                           "Ljava/lang/String;");
-  ASSERT_TRUE(Afoo != NULL);
+  ASSERT_TRUE(Afoo != nullptr);
   EXPECT_EQ(Afoo, Bfoo);
   EXPECT_EQ(Afoo, Jfoo);
   EXPECT_EQ(Afoo, Kfoo);
@@ -1007,20 +1012,20 @@
   Handle<mirror::ClassLoader> class_loader(
       hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
   const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(jclass_loader)[0];
-  CHECK(dex_file != NULL);
+  CHECK(dex_file != nullptr);
   mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader);
   mirror::ArtMethod* clinit = klass->FindClassInitializer();
   mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;");
   const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;");
-  ASSERT_TRUE(string_id != NULL);
+  ASSERT_TRUE(string_id != nullptr);
   const DexFile::TypeId* type_id = dex_file->FindTypeId(dex_file->GetIndexForStringId(*string_id));
-  ASSERT_TRUE(type_id != NULL);
+  ASSERT_TRUE(type_id != nullptr);
   uint32_t type_idx = dex_file->GetIndexForTypeId(*type_id);
   mirror::Class* uninit = ResolveVerifyAndClinit(type_idx, clinit, Thread::Current(), true, false);
-  EXPECT_TRUE(uninit != NULL);
+  EXPECT_TRUE(uninit != nullptr);
   EXPECT_FALSE(uninit->IsInitialized());
   mirror::Class* init = ResolveVerifyAndClinit(type_idx, getS0, Thread::Current(), true, false);
-  EXPECT_TRUE(init != NULL);
+  EXPECT_TRUE(init != nullptr);
   EXPECT_TRUE(init->IsInitialized());
 }
 
@@ -1117,7 +1122,7 @@
   ScopedObjectAccess soa(Thread::Current());
 
   mirror::Class* JavaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
-  ASSERT_TRUE(JavaLangObject != NULL);
+  ASSERT_TRUE(JavaLangObject != nullptr);
   EXPECT_TRUE(JavaLangObject->IsInitialized()) << "Not testing already initialized class from the "
                                                   "core";
   CheckPreverified(JavaLangObject, true);
@@ -1135,7 +1140,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);
 }
 
@@ -1150,7 +1155,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 e9e11f6..6e3ebc2 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -44,7 +44,7 @@
 
 int main(int argc, char **argv) {
   art::InitLogging(argv);
-  LOG(INFO) << "Running main() from common_runtime_test.cc...";
+  LOG(::art::INFO) << "Running main() from common_runtime_test.cc...";
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
 }
@@ -343,23 +343,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..bd0dbaa 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -85,7 +85,7 @@
   virtual void SetUp();
 
   // Allow subclases such as CommonCompilerTest to add extra options.
-  virtual void SetUpRuntimeOptions(RuntimeOptions* options) {}
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options ATTRIBUTE_UNUSED) {}
 
   void ClearDirectory(const char* dirpath);
 
@@ -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 1f52c46..584743b 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 {
@@ -231,7 +231,7 @@
   virtual ~DebugInstrumentationListener() {}
 
   void MethodEntered(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method,
-                     uint32_t dex_pc)
+                     uint32_t dex_pc ATTRIBUTE_UNUSED)
       OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (method->IsNative()) {
       // TODO: post location events is a suspension point and native method entry stubs aren't.
@@ -254,6 +254,7 @@
                     uint32_t dex_pc)
       OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // We're not recorded to listen to this kind of event, so complain.
+    UNUSED(thread, this_object, method, dex_pc);
     LOG(ERROR) << "Unexpected method unwind event in debugger " << PrettyMethod(method)
                << " " << dex_pc;
   }
@@ -267,16 +268,18 @@
   void FieldRead(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method,
                  uint32_t dex_pc, mirror::ArtField* field)
       OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    UNUSED(thread);
     Dbg::PostFieldAccessEvent(method, dex_pc, this_object, field);
   }
 
-  void FieldWritten(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method,
-                    uint32_t dex_pc, mirror::ArtField* field, const JValue& field_value)
+  void FieldWritten(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object,
+                    mirror::ArtMethod* method, uint32_t dex_pc, mirror::ArtField* field,
+                    const JValue& field_value)
       OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     Dbg::PostFieldModificationEvent(method, dex_pc, this_object, field, &field_value);
   }
 
-  void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location,
+  void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED, const ThrowLocation& throw_location,
                        mirror::ArtMethod* catch_method, uint32_t catch_dex_pc,
                        mirror::Throwable* exception_object)
       OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -297,7 +300,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 +402,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 +473,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 +515,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) {
@@ -615,7 +620,7 @@
   VLOG(jdwp) << "ParseJdwpOptions: " << options;
 
   std::vector<std::string> pairs;
-  Split(options, ',', pairs);
+  Split(options, ',', &pairs);
 
   for (size_t i = 0; i < pairs.size(); ++i) {
     std::string::size_type equals = pairs[i].find('=');
@@ -651,7 +656,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 +714,7 @@
 }
 
 Thread* Dbg::GetDebugThread() {
-  return (gJdwpState != NULL) ? gJdwpState->GetDebugThread() : NULL;
+  return (gJdwpState != nullptr) ? gJdwpState->GetDebugThread() : nullptr;
 }
 
 void Dbg::ClearWaitForEventThread() {
@@ -827,12 +832,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 +855,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 +893,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 +913,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 +931,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 +945,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 +973,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 +991,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 +1015,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 +1129,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 +1141,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_in) : classes(classes_in) {
     }
 
     static bool Visit(mirror::Class* c, void* arg) {
@@ -1148,12 +1157,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 +1172,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 +1190,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 +1223,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 +1289,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 +1346,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 +1385,10 @@
   } 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();
+      mirror::Object* o = gRegistry->Get<mirror::Object*>(id, &error);
+      if (error != JDWP::ERR_NONE) {
+        return error;
       }
       oa->Set<false>(offset + i, o);
     }
@@ -1391,13 +1401,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 +1416,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 +1454,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 +1473,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 +1489,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 +1507,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 +1515,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 +1582,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 +1609,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 +1635,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 +1651,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 +1690,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 +1748,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 +1770,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 +1780,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 +1797,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 +1827,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 +1856,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 +1879,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 +1899,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 +1933,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 +1975,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) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id);
-  if (thread_object == ObjectRegistry::kInvalidObject) {
+  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.
@@ -1999,22 +2019,25 @@
   } else if (error == JDWP::ERR_NONE) {
     mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
     CHECK(c != nullptr);
-    mirror::ArtField* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
+    mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_Thread_group);
     CHECK(f != nullptr);
     mirror::Object* group = f->GetObject(thread_object);
     CHECK(group != nullptr);
     JDWP::ObjectId thread_group_id = gRegistry->Add(group);
     expandBufAddObjectId(pReply, thread_group_id);
   }
-  soa.Self()->EndAssertNoThreadSuspension(old_cause);
   return error;
 }
 
 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);
-  if (thread_group == nullptr || thread_group == ObjectRegistry::kInvalidObject) {
+  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;
   }
@@ -2036,12 +2059,10 @@
   if (error != JDWP::ERR_NONE) {
     return error;
   }
-  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupName");
-  mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
-  mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;");
-  CHECK(f != NULL);
+  ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroupName");
+  mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_name);
+  CHECK(f != nullptr);
   mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group));
-  soa.Self()->EndAssertNoThreadSuspension(old_cause);
 
   std::string thread_group_name(s->ToModifiedUtf8());
   expandBufAddUtf8String(pReply, thread_group_name);
@@ -2055,14 +2076,13 @@
   if (error != JDWP::ERR_NONE) {
     return error;
   }
-  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("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 != NULL);
-  mirror::Object* parent = f->GetObject(thread_group);
-  soa.Self()->EndAssertNoThreadSuspension(old_cause);
-
+  mirror::Object* parent;
+  {
+    ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroupParent");
+    mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_parent);
+    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;
@@ -2074,12 +2094,20 @@
   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::ArtField* groups_field = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_groups);
   mirror::Object* groups_array_list = groups_field->GetObject(thread_group);
+  {
+    // The "groups" field is declared as a java.util.List: check it really is
+    // an instance of java.util.ArrayList.
+    CHECK(groups_array_list != nullptr);
+    mirror::Class* java_util_ArrayList_class =
+        soa.Decode<mirror::Class*>(WellKnownClasses::java_util_ArrayList);
+    CHECK(groups_array_list->InstanceOf(java_util_ArrayList_class));
+  }
 
   // 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::ArtField* array_field = soa.DecodeField(WellKnownClasses::java_util_ArrayList_array);
+  mirror::ArtField* size_field = soa.DecodeField(WellKnownClasses::java_util_ArrayList_size);
   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);
@@ -2171,8 +2199,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;
@@ -2192,8 +2220,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;
   }
@@ -2205,8 +2233,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;
   }
@@ -2262,8 +2290,8 @@
 
 static int GetStackDepth(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   struct CountStackDepthVisitor : public StackVisitor {
-    explicit CountStackDepthVisitor(Thread* thread)
-        : StackVisitor(thread, NULL), depth(0) {}
+    explicit CountStackDepthVisitor(Thread* thread_in)
+        : StackVisitor(thread_in, nullptr), depth(0) {}
 
     // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
     // annotalysis.
@@ -2281,18 +2309,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;
 }
 
@@ -2300,10 +2329,11 @@
                                      size_t frame_count, JDWP::ExpandBuf* buf) {
   class GetFrameVisitor : public StackVisitor {
    public:
-    GetFrameVisitor(Thread* thread, size_t start_frame, size_t frame_count, JDWP::ExpandBuf* buf)
+    GetFrameVisitor(Thread* thread, size_t start_frame_in, size_t frame_count_in,
+                    JDWP::ExpandBuf* buf_in)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, NULL), depth_(0),
-          start_frame_(start_frame), frame_count_(frame_count), buf_(buf) {
+        : StackVisitor(thread, nullptr), depth_(0),
+          start_frame_(start_frame_in), frame_count_(frame_count_in), buf_(buf_in) {
       expandBufAdd4BE(buf_, frame_count_);
     }
 
@@ -2337,8 +2367,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;
   }
@@ -2369,12 +2399,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
@@ -2384,7 +2415,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;
@@ -2395,13 +2426,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;
   }
@@ -2420,9 +2453,9 @@
 }
 
 struct GetThisVisitor : public StackVisitor {
-  GetThisVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id)
+  GetThisVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id_in)
       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_in) {}
 
   // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
   // annotalysis.
@@ -2445,7 +2478,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;
     }
@@ -2501,7 +2535,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;
     }
@@ -2667,7 +2702,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;
     }
@@ -2747,8 +2783,10 @@
     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) {
+      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)),
@@ -3137,12 +3175,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);
@@ -3274,7 +3313,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()) {
@@ -3340,10 +3379,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;
     }
 
@@ -3353,11 +3392,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());
           }
@@ -3382,15 +3421,15 @@
   //
 
   struct DebugCallbackContext {
-    explicit DebugCallbackContext(SingleStepControl* single_step_control, int32_t line_number,
+    explicit DebugCallbackContext(SingleStepControl* single_step_control_cb, int32_t line_number_cb,
                                   const DexFile::CodeItem* code_item)
-      : single_step_control_(single_step_control), line_number_(line_number), code_item_(code_item),
-        last_pc_valid(false), last_pc(0) {
+      : single_step_control_(single_step_control_cb), line_number_(line_number_cb),
+        code_item_(code_item), last_pc_valid(false), last_pc(0) {
     }
 
-    static bool Callback(void* raw_context, uint32_t address, uint32_t line_number) {
+    static bool Callback(void* raw_context, uint32_t address, uint32_t line_number_cb) {
       DebugCallbackContext* context = reinterpret_cast<DebugCallbackContext*>(raw_context);
-      if (static_cast<int32_t>(line_number) == context->line_number_) {
+      if (static_cast<int32_t>(line_number_cb) == context->line_number_) {
         if (!context->last_pc_valid) {
           // Everything from this address until the next line change is ours.
           context->last_pc = address;
@@ -3430,7 +3469,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);
   }
 
   //
@@ -3460,8 +3499,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);
@@ -3473,6 +3512,7 @@
   switch (tag) {
     default:
       LOG(FATAL) << "unknown JDWP tag: " << PrintableChar(tag);
+      UNREACHABLE();
 
     // Primitives.
     case JDWP::JT_BYTE:    return 'B';
@@ -3505,13 +3545,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;
@@ -3546,25 +3587,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()) {
@@ -3598,11 +3638,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;
           }
 
@@ -3712,8 +3752,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);
@@ -3730,7 +3770,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());
@@ -3758,7 +3798,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());
@@ -3777,23 +3817,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());
@@ -3813,7 +3854,7 @@
     return false;
   }
 
-  if (chunk.get() == NULL) {
+  if (chunk.get() == nullptr) {
     return false;
   }
 
@@ -3835,13 +3876,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;
   }
@@ -3906,8 +3947,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());
@@ -3957,7 +3998,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;
@@ -3969,7 +4010,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);
@@ -4083,7 +4124,6 @@
   HeapChunkContext(bool merge, bool native)
       : buf_(16384 - 16),
         type_(0),
-        merge_(merge),
         chunk_overhead_(0) {
     Reset();
     if (native) {
@@ -4126,7 +4166,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;
@@ -4154,7 +4194,7 @@
     ResetStartOfNextChunk();
     totalAllocationUnits_ = 0;
     needHeader_ = true;
-    pieceLenField_ = NULL;
+    pieceLenField_ = nullptr;
   }
 
   void HeapChunkCallback(void* start, void* /*end*/, size_t used_bytes)
@@ -4163,9 +4203,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
@@ -4180,7 +4220,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.
@@ -4196,7 +4236,7 @@
             }
         }
         if (flush) {
-            startOfNextMemoryChunk_ = NULL;
+            startOfNextMemoryChunk_ = nullptr;
             Flush();
         }
     }
@@ -4242,7 +4282,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);
     }
 
@@ -4259,7 +4299,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);
     }
@@ -4294,7 +4334,6 @@
   void* startOfNextMemoryChunk_;
   size_t totalAllocationUnits_;
   uint32_t type_;
-  bool merge_;
   bool needHeader_;
   size_t chunk_overhead_;
 
@@ -4337,7 +4376,7 @@
   // Send a series of heap segment chunks.
   HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
   if (native) {
-#ifdef USE_DLMALLOC
+#if defined(HAVE_ANDROID_OS) && defined(USE_DLMALLOC)
     dlmalloc_inspect_all(HeapChunkContext::HeapChunkCallback, &context);
 #else
     UNIMPLEMENTED(WARNING) << "Native heap inspection is only supported with dlmalloc";
@@ -4412,7 +4451,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();
@@ -4422,19 +4461,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();
@@ -4445,9 +4484,9 @@
 }
 
 struct AllocRecordStackVisitor : public StackVisitor {
-  AllocRecordStackVisitor(Thread* thread, AllocRecord* record)
+  AllocRecordStackVisitor(Thread* thread, AllocRecord* record_in)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : StackVisitor(thread, NULL), record(record), depth(0) {}
+      : StackVisitor(thread, nullptr), record(record_in), depth(0) {}
 
   // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
   // annotalysis.
@@ -4476,12 +4515,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;
   }
@@ -4522,7 +4558,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;
   }
@@ -4543,7 +4579,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();
@@ -4648,7 +4684,7 @@
  * between the contents of these tables.
  */
 jbyteArray Dbg::GetRecentAllocations() {
-  if (false) {
+  if ((false)) {
     DumpRecentAllocations();
   }
 
@@ -4672,7 +4708,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));
@@ -4755,7 +4791,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 131de2c..488ba7f 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -189,6 +189,7 @@
   // Method for selective deoptimization.
   jmethodID method_;
 };
+std::ostream& operator<<(std::ostream& os, const DeoptimizationRequest::Kind& rhs);
 
 class Dbg {
  public:
@@ -257,9 +258,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_);
@@ -267,38 +268,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_);
 
   //
@@ -327,12 +328,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_);
 
@@ -340,19 +341,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_);
@@ -384,7 +385,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)
@@ -413,7 +414,7 @@
   /*
    * 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)
@@ -448,7 +449,7 @@
       LOCKS_EXCLUDED(Locks::thread_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  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)
@@ -493,7 +494,7 @@
   /*
    * Debugger notification
    */
-  enum {
+  enum EventFlag {
     kBreakpoint     = 0x01,
     kSingleStep     = 0x02,
     kMethodEntry    = 0x04,
@@ -574,7 +575,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)
@@ -590,7 +591,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_);
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index e095c48..c68fdca 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -26,14 +26,14 @@
 namespace art {
 
 inline int32_t DexFile::GetStringLength(const StringId& string_id) const {
-  const byte* ptr = begin_ + string_id.string_data_off_;
+  const uint8_t* ptr = begin_ + string_id.string_data_off_;
   return DecodeUnsignedLeb128(&ptr);
 }
 
 inline const char* DexFile::GetStringDataAndUtf16Length(const StringId& string_id,
                                                         uint32_t* utf16_length) const {
   DCHECK(utf16_length != NULL) << GetLocation();
-  const byte* ptr = begin_ + string_id.string_data_off_;
+  const uint8_t* ptr = begin_ + string_id.string_data_off_;
   *utf16_length = DecodeUnsignedLeb128(&ptr);
   return reinterpret_cast<const char*>(ptr);
 }
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 31d79bf..3d4184b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -23,7 +23,9 @@
 #include <string.h>
 #include <sys/file.h>
 #include <sys/stat.h>
+
 #include <memory>
+#include <sstream>
 
 #include "base/logging.h"
 #include "base/stringprintf.h"
@@ -37,7 +39,6 @@
 #include "mirror/string.h"
 #include "os.h"
 #include "safe_map.h"
-#include "ScopedFd.h"
 #include "handle_scope-inl.h"
 #include "thread.h"
 #include "utf-inl.h"
@@ -45,10 +46,15 @@
 #include "well_known_classes.h"
 #include "zip_archive.h"
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#include "ScopedFd.h"
+#pragma GCC diagnostic pop
+
 namespace art {
 
-const byte DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
-const byte DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' };
+const uint8_t DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
+const uint8_t DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' };
 
 static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
   CHECK(magic != NULL);
@@ -205,19 +211,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";
@@ -322,7 +329,7 @@
 }
 
 
-const DexFile* DexFile::OpenMemory(const byte* base,
+const DexFile* DexFile::OpenMemory(const uint8_t* base,
                                    size_t size,
                                    const std::string& location,
                                    uint32_t location_checksum,
@@ -336,7 +343,7 @@
   }
 }
 
-DexFile::DexFile(const byte* base, size_t size,
+DexFile::DexFile(const uint8_t* base, size_t size,
                  const std::string& location,
                  uint32_t location_checksum,
                  MemMap* mem_map)
@@ -353,8 +360,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();
 }
@@ -399,12 +405,12 @@
   return true;
 }
 
-bool DexFile::IsMagicValid(const byte* magic) {
+bool DexFile::IsMagicValid(const uint8_t* magic) {
   return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
 }
 
-bool DexFile::IsVersionValid(const byte* magic) {
-  const byte* version = &magic[sizeof(kDexMagic)];
+bool DexFile::IsVersionValid(const uint8_t* magic) {
+  const uint8_t* version = &magic[sizeof(kDexMagic)];
   return (memcmp(version, kDexMagicVersion, sizeof(kDexMagicVersion)) == 0);
 }
 
@@ -444,20 +450,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;
-      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();
+    for (uint32_t i = 0; i < num_class_defs;  ++i) {
+      const ClassDef& class_def = GetClassDef(i);
+      const char* class_descriptor = GetClassDescriptor(class_def);
+      index->Insert(std::make_pair(class_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;
 }
@@ -754,7 +761,7 @@
 
 void DexFile::DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32_t method_idx,
                                DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb,
-                               void* context, const byte* stream, LocalInfo* local_in_reg) const {
+                               void* context, const uint8_t* stream, LocalInfo* local_in_reg) const {
   uint32_t line = DecodeUnsignedLeb128(&stream);
   uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
   uint16_t arg_reg = code_item->registers_size_ - code_item->ins_size_;
@@ -919,7 +926,7 @@
                               DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb,
                               void* context) const {
   DCHECK(code_item != nullptr);
-  const byte* stream = GetDebugInfoStream(code_item);
+  const uint8_t* stream = GetDebugInfoStream(code_item);
   std::unique_ptr<LocalInfo[]> local_in_reg(local_cb != NULL ?
                                       new LocalInfo[code_item->registers_size_] :
                                       NULL);
@@ -1059,7 +1066,7 @@
 }
 
 // Read a signed integer.  "zwidth" is the zero-based byte count.
-static int32_t ReadSignedInt(const byte* ptr, int zwidth) {
+static int32_t ReadSignedInt(const uint8_t* ptr, int zwidth) {
   int32_t val = 0;
   for (int i = zwidth; i >= 0; --i) {
     val = ((uint32_t)val >> 8) | (((int32_t)*ptr++) << 24);
@@ -1070,7 +1077,7 @@
 
 // Read an unsigned integer.  "zwidth" is the zero-based byte count,
 // "fill_on_right" indicates which side we want to zero-fill from.
-static uint32_t ReadUnsignedInt(const byte* ptr, int zwidth, bool fill_on_right) {
+static uint32_t ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right) {
   uint32_t val = 0;
   if (!fill_on_right) {
     for (int i = zwidth; i >= 0; --i) {
@@ -1086,7 +1093,7 @@
 }
 
 // Read a signed long.  "zwidth" is the zero-based byte count.
-static int64_t ReadSignedLong(const byte* ptr, int zwidth) {
+static int64_t ReadSignedLong(const uint8_t* ptr, int zwidth) {
   int64_t val = 0;
   for (int i = zwidth; i >= 0; --i) {
     val = ((uint64_t)val >> 8) | (((int64_t)*ptr++) << 56);
@@ -1097,7 +1104,7 @@
 
 // Read an unsigned long.  "zwidth" is the zero-based byte count,
 // "fill_on_right" indicates which side we want to zero-fill from.
-static uint64_t ReadUnsignedLong(const byte* ptr, int zwidth, bool fill_on_right) {
+static uint64_t ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right) {
   uint64_t val = 0;
   if (!fill_on_right) {
     for (int i = zwidth; i >= 0; --i) {
@@ -1137,8 +1144,8 @@
   if (pos_ >= array_size_) {
     return;
   }
-  byte value_type = *ptr_++;
-  byte value_arg = value_type >> kEncodedValueArgShift;
+  uint8_t value_type = *ptr_++;
+  uint8_t value_arg = value_type >> kEncodedValueArgShift;
   size_t width = value_arg + 1;  // assume and correct later
   type_ = static_cast<ValueType>(value_type & kEncodedValueTypeMask);
   switch (type_) {
@@ -1180,19 +1187,20 @@
   case kArray:
   case kAnnotation:
     UNIMPLEMENTED(FATAL) << ": type " << type_;
-    break;
+    UNREACHABLE();
   case kNull:
     jval_.l = NULL;
     width = 0;
     break;
   default:
     LOG(FATAL) << "Unreached";
+    UNREACHABLE();
   }
   ptr_ += width;
 }
 
 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;
@@ -1219,8 +1227,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;
@@ -1266,7 +1274,7 @@
   }
 }
 
-void CatchHandlerIterator::Init(const byte* handler_data) {
+void CatchHandlerIterator::Init(const uint8_t* handler_data) {
   current_data_ = handler_data;
   remaining_count_ = DecodeSignedLeb128(&current_data_);
 
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 54c4b09..a71ca42 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -25,6 +25,7 @@
 #include "base/hash_map.h"
 #include "base/logging.h"
 #include "base/mutex.h"  // For Locks::mutator_lock_.
+#include "base/value_object.h"
 #include "globals.h"
 #include "invoke_type.h"
 #include "jni.h"
@@ -51,10 +52,10 @@
 // TODO: move all of the macro functionality into the DexCache class.
 class DexFile {
  public:
-  static const byte kDexMagic[];
-  static const byte kDexMagicVersion[];
-  static const size_t kSha1DigestSize = 20;
-  static const uint32_t kDexEndianConstant = 0x12345678;
+  static const uint8_t kDexMagic[];
+  static const uint8_t kDexMagicVersion[];
+  static constexpr size_t kSha1DigestSize = 20;
+  static constexpr uint32_t kDexEndianConstant = 0x12345678;
 
   // name of the DexFile entry within a zip archive
   static const char* kClassesDex;
@@ -206,10 +207,10 @@
     // (class or interface). These are all in the lower 16b and do not contain runtime flags.
     uint32_t GetJavaAccessFlags() const {
       // Make sure that none of our runtime-only flags are set.
-      COMPILE_ASSERT((kAccValidClassFlags & kAccJavaFlagsMask) == kAccValidClassFlags,
-                     valid_class_flags_not_subset_of_java_flags);
-      COMPILE_ASSERT((kAccValidInterfaceFlags & kAccJavaFlagsMask) == kAccValidInterfaceFlags,
-                     valid_interface_flags_not_subset_of_java_flags);
+      static_assert((kAccValidClassFlags & kAccJavaFlagsMask) == kAccValidClassFlags,
+                    "Valid class flags not a subset of Java flags");
+      static_assert((kAccValidInterfaceFlags & kAccJavaFlagsMask) == kAccValidInterfaceFlags,
+                    "Valid interface flags not a subset of Java flags");
 
       if ((access_flags_ & kAccInterface) != 0) {
         // Interface.
@@ -441,10 +442,10 @@
   uint32_t GetVersion() const;
 
   // Returns true if the byte string points to the magic value.
-  static bool IsMagicValid(const byte* magic);
+  static bool IsMagicValid(const uint8_t* magic);
 
   // Returns true if the byte string after the magic is the correct value.
-  static bool IsVersionValid(const byte* magic);
+  static bool IsVersionValid(const uint8_t* magic);
 
   // Returns the number of string identifiers in the .dex file.
   size_t NumStringIds() const {
@@ -660,13 +661,13 @@
     if (class_def.interfaces_off_ == 0) {
         return NULL;
     } else {
-      const byte* addr = begin_ + class_def.interfaces_off_;
+      const uint8_t* addr = begin_ + class_def.interfaces_off_;
       return reinterpret_cast<const TypeList*>(addr);
     }
   }
 
   // Returns a pointer to the raw memory mapped class_data_item
-  const byte* GetClassData(const ClassDef& class_def) const {
+  const uint8_t* GetClassData(const ClassDef& class_def) const {
     if (class_def.class_data_off_ == 0) {
       return NULL;
     } else {
@@ -679,7 +680,7 @@
     if (code_off == 0) {
       return NULL;  // native or abstract method
     } else {
-      const byte* addr = begin_ + code_off;
+      const uint8_t* addr = begin_ + code_off;
       return reinterpret_cast<const CodeItem*>(addr);
     }
   }
@@ -732,12 +733,12 @@
     if (proto_id.parameters_off_ == 0) {
       return NULL;
     } else {
-      const byte* addr = begin_ + proto_id.parameters_off_;
+      const uint8_t* addr = begin_ + proto_id.parameters_off_;
       return reinterpret_cast<const TypeList*>(addr);
     }
   }
 
-  const byte* GetEncodedStaticFieldValuesArray(const ClassDef& class_def) const {
+  const uint8_t* GetEncodedStaticFieldValuesArray(const ClassDef& class_def) const {
     if (class_def.static_values_off_ == 0) {
       return 0;
     } else {
@@ -748,9 +749,9 @@
   static const TryItem* GetTryItems(const CodeItem& code_item, uint32_t offset);
 
   // Get the base of the encoded data for the given DexCode.
-  static const byte* GetCatchHandlerData(const CodeItem& code_item, uint32_t offset) {
-    const byte* handler_data =
-        reinterpret_cast<const byte*>(GetTryItems(code_item, code_item.tries_size_));
+  static const uint8_t* GetCatchHandlerData(const CodeItem& code_item, uint32_t offset) {
+    const uint8_t* handler_data =
+        reinterpret_cast<const uint8_t*>(GetTryItems(code_item, code_item.tries_size_));
     return handler_data + offset;
   }
 
@@ -761,7 +762,7 @@
   static int32_t FindCatchHandlerOffset(const CodeItem &code_item, uint32_t address);
 
   // Get the pointer to the start of the debugging data
-  const byte* GetDebugInfoStream(const CodeItem* code_item) const {
+  const uint8_t* GetDebugInfoStream(const CodeItem* code_item) const {
     if (code_item->debug_info_off_ == 0) {
       return NULL;
     } else {
@@ -864,7 +865,7 @@
 
   bool DisableWrite() const;
 
-  const byte* Begin() const {
+  const uint8_t* Begin() const {
     return begin_;
   }
 
@@ -919,14 +920,14 @@
                                    std::string* error_msg);
 
   // Opens a .dex file at the given address, optionally backed by a MemMap
-  static const DexFile* OpenMemory(const byte* dex_file,
+  static const DexFile* OpenMemory(const uint8_t* dex_file,
                                    size_t size,
                                    const std::string& location,
                                    uint32_t location_checksum,
                                    MemMap* mem_map,
                                    std::string* error_msg);
 
-  DexFile(const byte* base, size_t size,
+  DexFile(const uint8_t* base, size_t size,
           const std::string& location,
           uint32_t location_checksum,
           MemMap* mem_map);
@@ -939,7 +940,7 @@
 
   void DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32_t method_idx,
       DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb,
-      void* context, const byte* stream, LocalInfo* local_in_reg) const;
+      void* context, const uint8_t* stream, LocalInfo* local_in_reg) const;
 
   // Check whether a location denotes a multidex dex file. This is a very simple check: returns
   // whether the string contains the separator character.
@@ -947,7 +948,7 @@
 
 
   // The base address of the memory mapping.
-  const byte* const begin_;
+  const uint8_t* const begin_;
 
   // The size of the underlying memory allocation in bytes.
   const size_t size_;
@@ -1012,7 +1013,6 @@
   };
   typedef HashMap<const char*, const ClassDef*, UTF16EmptyFn, 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);
 
@@ -1043,7 +1043,7 @@
 };
 
 // Abstract the signature of a method.
-class Signature {
+class Signature : public ValueObject {
  public:
   std::string ToString() const;
 
@@ -1075,7 +1075,7 @@
 // Iterate and decode class_data_item
 class ClassDataItemIterator {
  public:
-  ClassDataItemIterator(const DexFile& dex_file, const byte* raw_class_data_item)
+  ClassDataItemIterator(const DexFile& dex_file, const uint8_t* raw_class_data_item)
       : dex_file_(dex_file), pos_(0), ptr_pos_(raw_class_data_item), last_idx_(0) {
     ReadClassDataHeader();
     if (EndOfInstanceFieldsPos() > 0) {
@@ -1190,7 +1190,7 @@
   uint32_t GetMethodCodeItemOffset() const {
     return method_.code_off_;
   }
-  const byte* EndDataPointer() const {
+  const uint8_t* EndDataPointer() const {
     CHECK(!HasNext());
     return ptr_pos_;
   }
@@ -1252,7 +1252,7 @@
 
   const DexFile& dex_file_;
   size_t pos_;  // integral number of items passed
-  const byte* ptr_pos_;  // pointer into stream of class_data_item
+  const uint8_t* ptr_pos_;  // pointer into stream of class_data_item
   uint32_t last_idx_;  // last read field or method index to apply delta to
   DISALLOW_IMPLICIT_CONSTRUCTORS(ClassDataItemIterator);
 };
@@ -1265,9 +1265,9 @@
       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_; }
+  bool HasNext() const { return pos_ < array_size_; }
 
   void Next();
 
@@ -1291,8 +1291,8 @@
   };
 
  private:
-  static const byte kEncodedValueTypeMask = 0x1f;  // 0b11111
-  static const byte kEncodedValueArgShift = 5;
+  static constexpr uint8_t kEncodedValueTypeMask = 0x1f;  // 0b11111
+  static constexpr uint8_t kEncodedValueArgShift = 5;
 
   const DexFile& dex_file_;
   Handle<mirror::DexCache>* const dex_cache_;  // Dex cache to resolve literal objects.
@@ -1300,7 +1300,7 @@
   ClassLinker* linker_;  // Linker to resolve literal objects.
   size_t array_size_;  // Size of array.
   size_t pos_;  // Current position.
-  const byte* ptr_;  // Pointer into encoded data array.
+  const uint8_t* ptr_;  // Pointer into encoded data array.
   ValueType type_;  // Type of current encoded value.
   jvalue jval_;  // Value of current encoded value.
   DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedStaticFieldValueIterator);
@@ -1314,7 +1314,7 @@
     CatchHandlerIterator(const DexFile::CodeItem& code_item,
                          const DexFile::TryItem& try_item);
 
-    explicit CatchHandlerIterator(const byte* handler_data) {
+    explicit CatchHandlerIterator(const uint8_t* handler_data) {
       Init(handler_data);
     }
 
@@ -1329,20 +1329,20 @@
       return remaining_count_ != -1 || catch_all_;
     }
     // End of this set of catch blocks, convenience method to locate next set of catch blocks
-    const byte* EndDataPointer() const {
+    const uint8_t* EndDataPointer() const {
       CHECK(!HasNext());
       return current_data_;
     }
 
   private:
     void Init(const DexFile::CodeItem& code_item, int32_t offset);
-    void Init(const byte* handler_data);
+    void Init(const uint8_t* handler_data);
 
     struct CatchHandlerItem {
       uint16_t type_idx_;  // type index of the caught exception type
       uint32_t address_;  // handler address
     } handler_;
-    const byte *current_data_;  // the current handler in dex file.
+    const uint8_t* current_data_;  // the current handler in dex file.
     int32_t remaining_count_;   // number of handlers not read.
     bool catch_all_;            // is there a handler that will catch all exceptions in case
                                 // that all typed handler does not match.
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index d0c5603..134e284 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -35,7 +35,7 @@
   ASSERT_TRUE(dex != NULL);
 }
 
-static const byte kBase64Map[256] = {
+static const uint8_t kBase64Map[256] = {
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
@@ -60,12 +60,12 @@
   255, 255, 255, 255
 };
 
-static inline byte* DecodeBase64(const char* src, size_t* dst_size) {
-  std::vector<byte> tmp;
+static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+  std::vector<uint8_t> tmp;
   uint32_t t = 0, y = 0;
   int g = 3;
   for (size_t i = 0; src[i] != '\0'; ++i) {
-    byte c = kBase64Map[src[i] & 0xFF];
+    uint8_t c = kBase64Map[src[i] & 0xFF];
     if (c == 255) continue;
     // the final = symbols are read and used to trim the remaining bytes
     if (c == 254) {
@@ -96,7 +96,7 @@
     *dst_size = 0;
     return nullptr;
   }
-  std::unique_ptr<byte[]> dst(new byte[tmp.size()]);
+  std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
   if (dst_size != nullptr) {
     *dst_size = tmp.size();
   } else {
@@ -137,7 +137,7 @@
   // decode base64
   CHECK(base64 != NULL);
   size_t length;
-  std::unique_ptr<byte[]> dex_bytes(DecodeBase64(base64, &length));
+  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(base64, &length));
   CHECK(dex_bytes.get() != NULL);
 
   // write to provided file
@@ -229,7 +229,7 @@
   const DexFile::ClassDef& class_def = raw->GetClassDef(0);
   ASSERT_STREQ("LGetMethodSignature;", raw->GetClassDescriptor(class_def));
 
-  const byte* class_data = raw->GetClassData(class_def);
+  const uint8_t* class_data = raw->GetClassData(class_def);
   ASSERT_TRUE(class_data != NULL);
   ClassDataItemIterator it(*raw, class_data);
 
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 9eba92f..a3f3de8 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -124,7 +124,7 @@
     error_stmt;                                             \
   }
 
-bool DexFileVerifier::Verify(const DexFile* dex_file, const byte* begin, size_t size,
+bool DexFileVerifier::Verify(const DexFile* dex_file, const uint8_t* begin, size_t size,
                              const char* location, std::string* error_msg) {
   std::unique_ptr<DexFileVerifier> verifier(new DexFileVerifier(dex_file, begin, size, location));
   if (!verifier->Verify()) {
@@ -142,7 +142,7 @@
         ErrorStringPrintf("Invalid use of void");
         return false;
       }
-      // Intentional fallthrough.
+      FALLTHROUGH_INTENDED;
     case 'B':
     case 'C':
     case 'D':
@@ -175,8 +175,8 @@
   // Check that size is not 0.
   CHECK_NE(elem_size, 0U);
 
-  const byte* range_start = reinterpret_cast<const byte*>(start);
-  const byte* file_start = reinterpret_cast<const byte*>(begin_);
+  const uint8_t* range_start = reinterpret_cast<const uint8_t*>(start);
+  const uint8_t* file_start = reinterpret_cast<const uint8_t*>(begin_);
 
   // Check for overflow.
   uintptr_t max = 0 - 1;
@@ -189,8 +189,8 @@
     return false;
   }
 
-  const byte* range_end = range_start + count * elem_size;
-  const byte* file_end = file_start + size_;
+  const uint8_t* range_end = range_start + count * elem_size;
+  const uint8_t* file_end = file_start + size_;
   if (UNLIKELY((range_start < file_start) || (range_end > file_end))) {
     // Note: these two tests are enough as we make sure above that there's no overflow.
     ErrorStringPrintf("Bad range for %s: %zx to %zx", label,
@@ -201,7 +201,7 @@
   return true;
 }
 
-bool DexFileVerifier::CheckList(size_t element_size, const char* label, const byte* *ptr) {
+bool DexFileVerifier::CheckList(size_t element_size, const char* label, const uint8_t* *ptr) {
   // Check that the list is available. The first 4B are the count.
   if (!CheckListSize(*ptr, 1, 4U, label)) {
     return false;
@@ -251,7 +251,7 @@
   // Compute and verify the checksum in the header.
   uint32_t adler_checksum = adler32(0L, Z_NULL, 0);
   const uint32_t non_sum = sizeof(header_->magic_) + sizeof(header_->checksum_);
-  const byte* non_sum_ptr = reinterpret_cast<const byte*>(header_) + non_sum;
+  const uint8_t* non_sum_ptr = reinterpret_cast<const uint8_t*>(header_) + non_sum;
   adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
   if (adler_checksum != header_->checksum_) {
     ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
@@ -388,7 +388,7 @@
 
 uint32_t DexFileVerifier::ReadUnsignedLittleEndian(uint32_t size) {
   uint32_t result = 0;
-  if (LIKELY(CheckListSize(ptr_, size, sizeof(byte), "encoded_value"))) {
+  if (LIKELY(CheckListSize(ptr_, size, sizeof(uint8_t), "encoded_value"))) {
     for (uint32_t i = 0; i < size; i++) {
       result |= ((uint32_t) *(ptr_++)) << (i * 8);
     }
@@ -398,7 +398,7 @@
 
 bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
                                                 uint32_t* handler_offsets, uint32_t handlers_size) {
-  const byte* handlers_base = DexFile::GetCatchHandlerData(*code_item, 0);
+  const uint8_t* handlers_base = DexFile::GetCatchHandlerData(*code_item, 0);
 
   for (uint32_t i = 0; i < handlers_size; i++) {
     bool catch_all;
@@ -503,7 +503,7 @@
 
 bool DexFileVerifier::CheckPadding(size_t offset, uint32_t aligned_offset) {
   if (offset < aligned_offset) {
-    if (!CheckListSize(begin_ + offset, aligned_offset - offset, sizeof(byte), "section")) {
+    if (!CheckListSize(begin_ + offset, aligned_offset - offset, sizeof(uint8_t), "section")) {
       return false;
     }
     while (offset < aligned_offset) {
@@ -519,7 +519,7 @@
 }
 
 bool DexFileVerifier::CheckEncodedValue() {
-  if (!CheckListSize(ptr_, 1, sizeof(byte), "encoded_value header")) {
+  if (!CheckListSize(ptr_, 1, sizeof(uint8_t), "encoded_value header")) {
     return false;
   }
 
@@ -746,7 +746,7 @@
   // Grab the end of the insns if there are no try_items.
   uint32_t try_items_size = code_item->tries_size_;
   if (try_items_size == 0) {
-    ptr_ = reinterpret_cast<const byte*>(&insns[insns_size]);
+    ptr_ = reinterpret_cast<const uint8_t*>(&insns[insns_size]);
     return true;
   }
 
@@ -812,7 +812,7 @@
 
 bool DexFileVerifier::CheckIntraStringDataItem() {
   uint32_t size = DecodeUnsignedLeb128(&ptr_);
-  const byte* file_end = begin_ + size_;
+  const uint8_t* file_end = begin_ + size_;
 
   for (uint32_t i = 0; i < size; i++) {
     CHECK_LT(i, size);  // b/15014252 Prevents hitting the impossible case below
@@ -1003,7 +1003,7 @@
 }
 
 bool DexFileVerifier::CheckIntraAnnotationItem() {
-  if (!CheckListSize(ptr_, 1, sizeof(byte), "annotation visibility")) {
+  if (!CheckListSize(ptr_, 1, sizeof(uint8_t), "annotation visibility")) {
     return false;
   }
 
@@ -1090,7 +1090,7 @@
   }
 
   // Return a pointer to the end of the annotations.
-  ptr_ = reinterpret_cast<const byte*>(parameter_item);
+  ptr_ = reinterpret_cast<const uint8_t*>(parameter_item);
   return true;
 }
 
@@ -1416,7 +1416,7 @@
   return true;
 }
 
-uint16_t DexFileVerifier::FindFirstClassDataDefiner(const byte* ptr, bool* success) {
+uint16_t DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bool* success) {
   ClassDataItemIterator it(*dex_file_, ptr);
   *success = true;
 
@@ -1435,7 +1435,7 @@
   return DexFile::kDexNoIndex16;
 }
 
-uint16_t DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const byte* ptr, bool* success) {
+uint16_t DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success) {
   const DexFile::AnnotationsDirectoryItem* item =
       reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr);
   *success = true;
@@ -1759,7 +1759,7 @@
 
   // Check that references in class_data_item are to the right class.
   if (item->class_data_off_ != 0) {
-    const byte* data = begin_ + item->class_data_off_;
+    const uint8_t* data = begin_ + item->class_data_off_;
     bool success;
     uint16_t data_definer = FindFirstClassDataDefiner(data, &success);
     if (!success) {
@@ -1773,7 +1773,7 @@
 
   // Check that references in annotations_directory_item are to right class.
   if (item->annotations_off_ != 0) {
-    const byte* data = begin_ + item->annotations_off_;
+    const uint8_t* data = begin_ + item->annotations_off_;
     bool success;
     uint16_t annotations_definer = FindFirstAnnotationsDirectoryDefiner(data, &success);
     if (!success) {
@@ -1804,7 +1804,7 @@
     item++;
   }
 
-  ptr_ = reinterpret_cast<const byte*>(item);
+  ptr_ = reinterpret_cast<const uint8_t*>(item);
   return true;
 }
 
@@ -1834,7 +1834,7 @@
     offsets++;
   }
 
-  ptr_ = reinterpret_cast<const byte*>(offsets);
+  ptr_ = reinterpret_cast<const uint8_t*>(offsets);
   return true;
 }
 
@@ -1935,7 +1935,7 @@
     parameter_item++;
   }
 
-  ptr_ = reinterpret_cast<const byte*>(parameter_item);
+  ptr_ = reinterpret_cast<const uint8_t*>(parameter_item);
   return true;
 }
 
@@ -1956,7 +1956,7 @@
   for (uint32_t i = 0; i < count; i++) {
     uint32_t new_offset = (offset + alignment_mask) & ~alignment_mask;
     ptr_ = begin_ + new_offset;
-    const byte* prev_ptr = ptr_;
+    const uint8_t* prev_ptr = ptr_;
 
     // Check depending on the section type.
     switch (type) {
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 606da54..18bf2e7 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -26,7 +26,7 @@
 
 class DexFileVerifier {
  public:
-  static bool Verify(const DexFile* dex_file, const byte* begin, size_t size,
+  static bool Verify(const DexFile* dex_file, const uint8_t* begin, size_t size,
                      const char* location, std::string* error_msg);
 
   const std::string& FailureReason() const {
@@ -34,7 +34,7 @@
   }
 
  private:
-  DexFileVerifier(const DexFile* dex_file, const byte* begin, size_t size, const char* location)
+  DexFileVerifier(const DexFile* dex_file, const uint8_t* begin, size_t size, const char* location)
       : dex_file_(dex_file), begin_(begin), size_(size), location_(location),
         header_(&dex_file->GetHeader()), ptr_(NULL), previous_item_(NULL)  {
   }
@@ -45,7 +45,7 @@
   bool CheckListSize(const void* start, size_t count, size_t element_size, const char* label);
   // Check a list. The head is assumed to be at *ptr, and elements to be of size element_size. If
   // successful, the ptr will be moved forward the amount covered by the list.
-  bool CheckList(size_t element_size, const char* label, const byte* *ptr);
+  bool CheckList(size_t element_size, const char* label, const uint8_t* *ptr);
   // Checks whether the offset is zero (when size is zero) or that the offset falls within the area
   // claimed by the file.
   bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, const char* label);
@@ -81,8 +81,8 @@
 
   // Note: as sometimes kDexNoIndex16, being 0xFFFF, is a valid return value, we need an
   // additional out parameter to signal any errors loading an index.
-  uint16_t FindFirstClassDataDefiner(const byte* ptr, bool* success);
-  uint16_t FindFirstAnnotationsDirectoryDefiner(const byte* ptr, bool* success);
+  uint16_t FindFirstClassDataDefiner(const uint8_t* ptr, bool* success);
+  uint16_t FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success);
 
   bool CheckInterStringIdItem();
   bool CheckInterTypeIdItem();
@@ -112,13 +112,13 @@
       __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
 
   const DexFile* const dex_file_;
-  const byte* const begin_;
+  const uint8_t* const begin_;
   const size_t size_;
   const char* const location_;
   const DexFile::Header* const header_;
 
   AllocationTrackingSafeMap<uint32_t, uint16_t, kAllocatorTagDexFileVerifier> offset_to_type_map_;
-  const byte* ptr_;
+  const uint8_t* ptr_;
   const void* previous_item_;
 
   std::string failure_reason_;
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index d475d42..addd948 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -30,7 +30,7 @@
 
 class DexFileVerifierTest : public CommonRuntimeTest {};
 
-static const byte kBase64Map[256] = {
+static const uint8_t kBase64Map[256] = {
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
@@ -55,12 +55,12 @@
   255, 255, 255, 255
 };
 
-static inline byte* DecodeBase64(const char* src, size_t* dst_size) {
-  std::vector<byte> tmp;
+static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+  std::vector<uint8_t> tmp;
   uint32_t t = 0, y = 0;
   int g = 3;
   for (size_t i = 0; src[i] != '\0'; ++i) {
-    byte c = kBase64Map[src[i] & 0xFF];
+    uint8_t c = kBase64Map[src[i] & 0xFF];
     if (c == 255) continue;
     // the final = symbols are read and used to trim the remaining bytes
     if (c == 254) {
@@ -91,7 +91,7 @@
     *dst_size = 0;
     return nullptr;
   }
-  std::unique_ptr<byte[]> dst(new byte[tmp.size()]);
+  std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
   if (dst_size != nullptr) {
     *dst_size = tmp.size();
   } else {
@@ -106,7 +106,7 @@
   // decode base64
   CHECK(base64 != NULL);
   size_t length;
-  std::unique_ptr<byte[]> dex_bytes(DecodeBase64(base64, &length));
+  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(base64, &length));
   CHECK(dex_bytes.get() != NULL);
 
   // write to provided file
@@ -153,17 +153,17 @@
   ASSERT_TRUE(raw.get() != nullptr) << error_msg;
 }
 
-static void FixUpChecksum(byte* dex_file) {
+static void FixUpChecksum(uint8_t* dex_file) {
   DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file);
   uint32_t expected_size = header->file_size_;
   uint32_t adler_checksum = adler32(0L, Z_NULL, 0);
   const uint32_t non_sum = sizeof(DexFile::Header::magic_) + sizeof(DexFile::Header::checksum_);
-  const byte* non_sum_ptr = dex_file + non_sum;
+  const uint8_t* non_sum_ptr = dex_file + non_sum;
   adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
   header->checksum_ = adler_checksum;
 }
 
-static const DexFile* FixChecksumAndOpen(byte* bytes, size_t length, const char* location,
+static const DexFile* FixChecksumAndOpen(uint8_t* bytes, size_t length, const char* location,
                                          std::string* error_msg) {
   // Check data.
   CHECK(bytes != nullptr);
@@ -196,7 +196,7 @@
                                     std::string* error_msg) {
   // Decode base64.
   size_t length;
-  std::unique_ptr<byte[]> dex_bytes(DecodeBase64(kGoodTestDex, &length));
+  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kGoodTestDex, &length));
   CHECK(dex_bytes.get() != NULL);
 
   // Make modifications.
diff --git a/runtime/dex_instruction-inl.h b/runtime/dex_instruction-inl.h
index ad9491f..dd65f2c 100644
--- a/runtime/dex_instruction-inl.h
+++ b/runtime/dex_instruction-inl.h
@@ -460,11 +460,21 @@
    * copies of those.) Note that cases 5..2 fall through.
    */
   switch (count) {
-    case 5: arg[4] = InstA(inst_data);
-    case 4: arg[3] = (regList >> 12) & 0x0f;
-    case 3: arg[2] = (regList >> 8) & 0x0f;
-    case 2: arg[1] = (regList >> 4) & 0x0f;
-    case 1: arg[0] = regList & 0x0f; break;
+    case 5:
+      arg[4] = InstA(inst_data);
+      FALLTHROUGH_INTENDED;
+    case 4:
+      arg[3] = (regList >> 12) & 0x0f;
+      FALLTHROUGH_INTENDED;
+    case 3:
+      arg[2] = (regList >> 8) & 0x0f;
+      FALLTHROUGH_INTENDED;
+    case 2:
+      arg[1] = (regList >> 4) & 0x0f;
+      FALLTHROUGH_INTENDED;
+    case 1:
+      arg[0] = regList & 0x0f;
+      break;
     default:  // case 0
       break;  // Valid, but no need to do anything.
   }
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 0a71d62..a802759 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 
 #include <iomanip>
+#include <sstream>
 
 #include "base/stringprintf.h"
 #include "dex_file-inl.h"
@@ -111,8 +112,8 @@
       if ((*insns & 0xFF) == 0) {
         return 1;  // NOP.
       } else {
-        LOG(FATAL) << "Unreachable: " << DumpString(NULL);
-        return 0;
+        LOG(FATAL) << "Unreachable: " << DumpString(nullptr);
+        UNREACHABLE();
       }
   }
 }
@@ -161,21 +162,23 @@
     case k21c: {
       switch (Opcode()) {
         case CONST_STRING:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t string_idx = VRegB_21c();
             os << StringPrintf("const-string v%d, %s // string@%d", VRegA_21c(),
                                PrintableString(file->StringDataByIdx(string_idx)).c_str(), string_idx);
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         case CHECK_CAST:
         case CONST_CLASS:
         case NEW_INSTANCE:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t type_idx = VRegB_21c();
             os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << PrettyType(type_idx, *file)
                << " // type@" << type_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         case SGET:
         case SGET_WIDE:
         case SGET_OBJECT:
@@ -183,12 +186,13 @@
         case SGET_BYTE:
         case SGET_CHAR:
         case SGET_SHORT:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t field_idx = VRegB_21c();
             os << opcode << "  v" << static_cast<int>(VRegA_21c()) << ", " << PrettyField(field_idx, *file, true)
                << " // field@" << field_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         case SPUT:
         case SPUT_WIDE:
         case SPUT_OBJECT:
@@ -196,12 +200,13 @@
         case SPUT_BYTE:
         case SPUT_CHAR:
         case SPUT_SHORT:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t field_idx = VRegB_21c();
             os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << PrettyField(field_idx, *file, true)
                << " // field@" << field_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         default:
           os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_21c(), VRegB_21c());
           break;
@@ -221,20 +226,22 @@
         case IGET_BYTE:
         case IGET_CHAR:
         case IGET_SHORT:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t field_idx = VRegC_22c();
             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
                << PrettyField(field_idx, *file, true) << " // field@" << field_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         case IGET_QUICK:
         case IGET_OBJECT_QUICK:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t field_idx = VRegC_22c();
             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
                << "// offset@" << field_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         case IPUT:
         case IPUT_WIDE:
         case IPUT_OBJECT:
@@ -242,34 +249,38 @@
         case IPUT_BYTE:
         case IPUT_CHAR:
         case IPUT_SHORT:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t field_idx = VRegC_22c();
             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
                << PrettyField(field_idx, *file, true) << " // field@" << field_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         case IPUT_QUICK:
         case IPUT_OBJECT_QUICK:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t field_idx = VRegC_22c();
             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
                << "// offset@" << field_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         case INSTANCE_OF:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t type_idx = VRegC_22c();
             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
                << PrettyType(type_idx, *file) << " // type@" << type_idx;
             break;
           }
+          FALLTHROUGH_INTENDED;
         case NEW_ARRAY:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t type_idx = VRegC_22c();
             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
                << PrettyType(type_idx, *file) << " // type@" << type_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         default:
           os << StringPrintf("%s v%d, v%d, thing@%d", opcode, VRegA_22c(), VRegB_22c(), VRegC_22c());
           break;
@@ -283,7 +294,7 @@
     case k31c:
       if (Opcode() == CONST_STRING_JUMBO) {
         uint32_t string_idx = VRegB_31c();
-        if (file != NULL) {
+        if (file != nullptr) {
           os << StringPrintf("%s v%d, %s // string@%d", opcode, VRegA_31c(),
                              PrintableString(file->StringDataByIdx(string_idx)).c_str(),
                              string_idx);
@@ -317,7 +328,7 @@
         case INVOKE_DIRECT:
         case INVOKE_STATIC:
         case INVOKE_INTERFACE:
-          if (file != NULL) {
+          if (file != nullptr) {
             os << opcode << " {";
             uint32_t method_idx = VRegB_35c();
             for (size_t i = 0; i < VRegA_35c(); ++i) {
@@ -328,9 +339,10 @@
             }
             os << "}, " << PrettyMethod(method_idx, *file) << " // method@" << method_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         case INVOKE_VIRTUAL_QUICK:
-          if (file != NULL) {
+          if (file != nullptr) {
             os << opcode << " {";
             uint32_t method_idx = VRegB_35c();
             for (size_t i = 0; i < VRegA_35c(); ++i) {
@@ -341,7 +353,8 @@
             }
             os << "},  // vtable@" << method_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         default:
           os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2]
                        << ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c();
@@ -356,19 +369,21 @@
         case INVOKE_DIRECT_RANGE:
         case INVOKE_STATIC_RANGE:
         case INVOKE_INTERFACE_RANGE:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t method_idx = VRegB_3rc();
             os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
                << PrettyMethod(method_idx, *file) << " // method@" << method_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         case INVOKE_VIRTUAL_RANGE_QUICK:
-          if (file != NULL) {
+          if (file != nullptr) {
             uint32_t method_idx = VRegB_3rc();
             os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
                << "// vtable@" << method_idx;
             break;
-          }  // else fall-through
+          }
+          FALLTHROUGH_INTENDED;
         default:
           os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(),
                              (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc());
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index b6810b0..af5d9d0 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -35,7 +35,7 @@
 class Instruction {
  public:
   // NOP-encoded switch-statement signatures.
-  enum {
+  enum Signatures {
     kPackedSwitchSignature = 0x0100,
     kSparseSwitchSignature = 0x0200,
     kArrayDataSignature = 0x0300,
@@ -79,15 +79,13 @@
     DISALLOW_COPY_AND_ASSIGN(ArrayDataPayload);
   };
 
-  // TODO: the code layout below is deliberate to avoid this enum being picked up by
-  //       generate-operator-out.py.
-  enum Code
-  {  // NOLINT(whitespace/braces)
+  enum Code {  // private marker to avoid generate-operator-out.py from processing.
 #define INSTRUCTION_ENUM(opcode, cname, p, f, r, i, a, v) cname = opcode,
 #include "dex_instruction_list.h"
     DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM)
 #undef DEX_INSTRUCTION_LIST
 #undef INSTRUCTION_ENUM
+    RSUB_INT_LIT16 = RSUB_INT,
   };
 
   enum Format {
@@ -190,7 +188,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/dex_instruction_visitor_test.cc b/runtime/dex_instruction_visitor_test.cc
index c5e63eb..5273084 100644
--- a/runtime/dex_instruction_visitor_test.cc
+++ b/runtime/dex_instruction_visitor_test.cc
@@ -16,7 +16,6 @@
 
 #include "dex_instruction_visitor.h"
 
-#include <iostream>
 #include <memory>
 
 #include "gtest/gtest.h"
diff --git a/runtime/dex_method_iterator.h b/runtime/dex_method_iterator.h
index 806266d..14e316f 100644
--- a/runtime/dex_method_iterator.h
+++ b/runtime/dex_method_iterator.h
@@ -139,7 +139,7 @@
   uint32_t dex_file_index_;
   uint32_t class_def_index_;
   const DexFile::ClassDef* class_def_;
-  const byte* class_data_;
+  const uint8_t* class_data_;
   std::unique_ptr<ClassDataItemIterator> it_;
   bool direct_method_;
 };
diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc
index b8f180b..c6f333f 100644
--- a/runtime/dex_method_iterator_test.cc
+++ b/runtime/dex_method_iterator_test.cc
@@ -38,8 +38,8 @@
     const DexFile& dex_file = it.GetDexFile();
     InvokeType invoke_type = it.GetInvokeType();
     uint32_t method_idx = it.GetMemberIndex();
-    if (false) {
-      LG << invoke_type << " " << PrettyMethod(method_idx, dex_file);
+    if ((false)) {
+      LOG(INFO) << invoke_type << " " << PrettyMethod(method_idx, dex_file);
     }
     it.Next();
   }
diff --git a/runtime/dwarf.h b/runtime/dwarf.h
index 370ad95..7daa5f1 100644
--- a/runtime/dwarf.h
+++ b/runtime/dwarf.h
@@ -364,38 +364,38 @@
   DW_OP_reg29 = 0x6d,
   DW_OP_reg30 = 0x6e,
   DW_OP_reg31 = 0x6f,
-  DW_OP_breg0 = 0x50,
-  DW_OP_breg1 = 0x51,
-  DW_OP_breg2 = 0x52,
-  DW_OP_breg3 = 0x53,
-  DW_OP_breg4 = 0x54,
-  DW_OP_breg5 = 0x55,
-  DW_OP_breg6 = 0x56,
-  DW_OP_breg7 = 0x57,
-  DW_OP_breg8 = 0x58,
-  DW_OP_breg9 = 0x59,
-  DW_OP_breg10 = 0x5a,
-  DW_OP_breg11 = 0x5b,
-  DW_OP_breg12 = 0x5c,
-  DW_OP_breg13 = 0x5d,
-  DW_OP_breg14 = 0x5e,
-  DW_OP_breg15 = 0x5f,
-  DW_OP_breg16 = 0x60,
-  DW_OP_breg17 = 0x61,
-  DW_OP_breg18 = 0x62,
-  DW_OP_breg19 = 0x63,
-  DW_OP_breg20 = 0x64,
-  DW_OP_breg21 = 0x65,
-  DW_OP_breg22 = 0x66,
-  DW_OP_breg23 = 0x67,
-  DW_OP_breg24 = 0x68,
-  DW_OP_breg25 = 0x69,
-  DW_OP_breg26 = 0x6a,
-  DW_OP_breg27 = 0x6b,
-  DW_OP_breg28 = 0x6c,
-  DW_OP_breg29 = 0x6d,
-  DW_OP_breg30 = 0x6e,
-  DW_OP_breg31 = 0x6f,
+  DW_OP_breg0 = 0x70,
+  DW_OP_breg1 = 0x71,
+  DW_OP_breg2 = 0x72,
+  DW_OP_breg3 = 0x73,
+  DW_OP_breg4 = 0x74,
+  DW_OP_breg5 = 0x75,
+  DW_OP_breg6 = 0x76,
+  DW_OP_breg7 = 0x77,
+  DW_OP_breg8 = 0x78,
+  DW_OP_breg9 = 0x79,
+  DW_OP_breg10 = 0x7a,
+  DW_OP_breg11 = 0x7b,
+  DW_OP_breg12 = 0x7c,
+  DW_OP_breg13 = 0x7d,
+  DW_OP_breg14 = 0x7e,
+  DW_OP_breg15 = 0x7f,
+  DW_OP_breg16 = 0x80,
+  DW_OP_breg17 = 0x81,
+  DW_OP_breg18 = 0x82,
+  DW_OP_breg19 = 0x83,
+  DW_OP_breg20 = 0x84,
+  DW_OP_breg21 = 0x85,
+  DW_OP_breg22 = 0x86,
+  DW_OP_breg23 = 0x87,
+  DW_OP_breg24 = 0x88,
+  DW_OP_breg25 = 0x89,
+  DW_OP_breg26 = 0x8a,
+  DW_OP_breg27 = 0x8b,
+  DW_OP_breg28 = 0x8c,
+  DW_OP_breg29 = 0x8d,
+  DW_OP_breg30 = 0x8e,
+  DW_OP_breg31 = 0x8f,
   DW_OP_regx = 0x90,
   DW_OP_fbreg = 0x91,
   DW_OP_bregx = 0x92,
diff --git a/runtime/elf.h b/runtime/elf.h
index 6e007a2..60b5248 100644
--- a/runtime/elf.h
+++ b/runtime/elf.h
@@ -1411,6 +1411,7 @@
 
 // BEGIN android-added for <elf.h> compat
 static inline unsigned char ELF32_ST_TYPE(unsigned char st_info) { return st_info & 0x0f; }
+static inline unsigned char ELF64_ST_TYPE(unsigned char st_info) { return st_info & 0x0f; }
 // END android-added for <elf.h> compat
 
 // Symbol table entries for ELF64.
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 6ec0712..b6cf921 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -16,13 +16,17 @@
 
 #include "elf_file.h"
 
+#include <inttypes.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include "base/logging.h"
 #include "base/stringprintf.h"
 #include "base/stl_util.h"
+#include "base/unix_file/fd_file.h"
 #include "dwarf.h"
+#include "elf_file_impl.h"
+#include "elf_utils.h"
 #include "leb128.h"
 #include "utils.h"
 #include "instruction_set.h"
@@ -42,7 +46,7 @@
   struct JITCodeEntry {
     JITCodeEntry* next_;
     JITCodeEntry* prev_;
-    const byte *symfile_addr_;
+    const uint8_t *symfile_addr_;
     uint64_t symfile_size_;
   };
 
@@ -56,6 +60,7 @@
   // GDB will place breakpoint into this function.
   // To prevent GCC from inlining or removing it we place noinline attribute
   // and inline assembler statement inside.
+  void __attribute__((noinline)) __jit_debug_register_code();
   void __attribute__((noinline)) __jit_debug_register_code() {
     __asm__("");
   }
@@ -67,7 +72,7 @@
 }
 
 
-static JITCodeEntry* CreateCodeEntry(const byte *symfile_addr,
+static JITCodeEntry* CreateCodeEntry(const uint8_t *symfile_addr,
                                      uintptr_t symfile_size) {
   JITCodeEntry* entry = new JITCodeEntry;
   entry->symfile_addr_ = symfile_addr;
@@ -106,7 +111,12 @@
   delete entry;
 }
 
-ElfFile::ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base)
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::ElfFileImpl(File* file, bool writable, bool program_header_only, uint8_t* requested_base)
   : file_(file),
     writable_(writable),
     program_header_only_(program_header_only),
@@ -129,10 +139,20 @@
   CHECK(file != nullptr);
 }
 
-ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only,
-                       std::string* error_msg, uint8_t* requested_base) {
-  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only,
-                                    requested_base));
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>*
+    ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::Open(File* file, bool writable, bool program_header_only,
+           std::string* error_msg, uint8_t* requested_base) {
+  std::unique_ptr<ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>>
+    elf_file(new ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+                 Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+                 (file, writable, program_header_only, requested_base));
   int prot;
   int flags;
   if (writable) {
@@ -148,16 +168,32 @@
   return elf_file.release();
 }
 
-ElfFile* ElfFile::Open(File* file, int prot, int flags, std::string* error_msg) {
-  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false,
-                                    /*requested_base*/nullptr));
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>*
+    ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::Open(File* file, int prot, int flags, std::string* error_msg) {
+  std::unique_ptr<ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>>
+    elf_file(new ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+                 Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+                 (file, (prot & PROT_WRITE) == PROT_WRITE, /*program_header_only*/false,
+                 /*requested_base*/nullptr));
   if (!elf_file->Setup(prot, flags, error_msg)) {
     return nullptr;
   }
   return elf_file.release();
 }
 
-bool ElfFile::Setup(int prot, int flags, std::string* error_msg) {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::Setup(int prot, int flags, std::string* error_msg) {
   int64_t temp_file_length = file_->GetLength();
   if (temp_file_length < 0) {
     errno = -temp_file_length;
@@ -166,16 +202,16 @@
     return false;
   }
   size_t file_length = static_cast<size_t>(temp_file_length);
-  if (file_length < sizeof(Elf32_Ehdr)) {
+  if (file_length < sizeof(Elf_Ehdr)) {
     *error_msg = StringPrintf("File size of %zd bytes not large enough to contain ELF header of "
-                              "%zd bytes: '%s'", file_length, sizeof(Elf32_Ehdr),
+                              "%zd bytes: '%s'", file_length, sizeof(Elf_Ehdr),
                               file_->GetPath().c_str());
     return false;
   }
 
   if (program_header_only_) {
     // first just map ELF header to get program header size information
-    size_t elf_header_size = sizeof(Elf32_Ehdr);
+    size_t elf_header_size = sizeof(Elf_Ehdr);
     if (!SetMap(MemMap::MapFile(elf_header_size, prot, flags, file_->Fd(), 0,
                                 file_->GetPath().c_str(), error_msg),
                 error_msg)) {
@@ -186,7 +222,7 @@
     if (file_length < program_header_size) {
       *error_msg = StringPrintf("File size of %zd bytes not large enough to contain ELF program "
                                 "header of %zd bytes: '%s'", file_length,
-                                sizeof(Elf32_Ehdr), file_->GetPath().c_str());
+                                sizeof(Elf_Ehdr), file_->GetPath().c_str());
       return false;
     }
     if (!SetMap(MemMap::MapFile(program_header_size, prot, flags, file_->Fd(), 0,
@@ -218,7 +254,7 @@
     }
 
     // Find shstrtab.
-    Elf32_Shdr* shstrtab_section_header = GetSectionNameStringSection();
+    Elf_Shdr* shstrtab_section_header = GetSectionNameStringSection();
     if (shstrtab_section_header == nullptr) {
       *error_msg = StringPrintf("Failed to find shstrtab section header in ELF file: '%s'",
                                 file_->GetPath().c_str());
@@ -234,13 +270,13 @@
     }
 
     if (!CheckAndSet(GetDynamicProgramHeader().p_offset, "dynamic section",
-                     reinterpret_cast<byte**>(&dynamic_section_start_), error_msg)) {
+                     reinterpret_cast<uint8_t**>(&dynamic_section_start_), error_msg)) {
       return false;
     }
 
     // Find other sections from section headers
-    for (Elf32_Word i = 0; i < GetSectionHeaderNum(); i++) {
-      Elf32_Shdr* section_header = GetSectionHeader(i);
+    for (Elf_Word i = 0; i < GetSectionHeaderNum(); i++) {
+      Elf_Shdr* section_header = GetSectionHeader(i);
       if (section_header == nullptr) {
         *error_msg = StringPrintf("Failed to find section header for section %d in ELF file: '%s'",
                                   i, file_->GetPath().c_str());
@@ -249,14 +285,14 @@
       switch (section_header->sh_type) {
         case SHT_SYMTAB: {
           if (!CheckAndSet(section_header->sh_offset, "symtab",
-                           reinterpret_cast<byte**>(&symtab_section_start_), error_msg)) {
+                           reinterpret_cast<uint8_t**>(&symtab_section_start_), error_msg)) {
             return false;
           }
           break;
         }
         case SHT_DYNSYM: {
           if (!CheckAndSet(section_header->sh_offset, "dynsym",
-                           reinterpret_cast<byte**>(&dynsym_section_start_), error_msg)) {
+                           reinterpret_cast<uint8_t**>(&dynsym_section_start_), error_msg)) {
             return false;
           }
           break;
@@ -268,7 +304,7 @@
             const char* header_name = GetString(*shstrtab_section_header, section_header->sh_name);
             if (strncmp(".dynstr", header_name, 8) == 0) {
               if (!CheckAndSet(section_header->sh_offset, "dynstr",
-                               reinterpret_cast<byte**>(&dynstr_section_start_), error_msg)) {
+                               reinterpret_cast<uint8_t**>(&dynstr_section_start_), error_msg)) {
                 return false;
               }
             }
@@ -277,7 +313,7 @@
             const char* header_name = GetString(*shstrtab_section_header, section_header->sh_name);
             if (strncmp(".strtab", header_name, 8) == 0) {
               if (!CheckAndSet(section_header->sh_offset, "strtab",
-                               reinterpret_cast<byte**>(&strtab_section_start_), error_msg)) {
+                               reinterpret_cast<uint8_t**>(&strtab_section_start_), error_msg)) {
                 return false;
               }
             }
@@ -285,7 +321,7 @@
           break;
         }
         case SHT_DYNAMIC: {
-          if (reinterpret_cast<byte*>(dynamic_section_start_) !=
+          if (reinterpret_cast<uint8_t*>(dynamic_section_start_) !=
               Begin() + section_header->sh_offset) {
             LOG(WARNING) << "Failed to find matching SHT_DYNAMIC for PT_DYNAMIC in "
                          << file_->GetPath() << ": " << std::hex
@@ -297,7 +333,7 @@
         }
         case SHT_HASH: {
           if (!CheckAndSet(section_header->sh_offset, "hash section",
-                           reinterpret_cast<byte**>(&hash_section_start_), error_msg)) {
+                           reinterpret_cast<uint8_t**>(&hash_section_start_), error_msg)) {
             return false;
           }
           break;
@@ -314,7 +350,12 @@
   return true;
 }
 
-ElfFile::~ElfFile() {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::~ElfFileImpl() {
   STLDeleteElements(&segments_);
   delete symtab_symbol_table_;
   delete dynsym_symbol_table_;
@@ -324,8 +365,13 @@
   }
 }
 
-bool ElfFile::CheckAndSet(Elf32_Off offset, const char* label,
-                          byte** target, std::string* error_msg) {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::CheckAndSet(Elf32_Off offset, const char* label,
+                  uint8_t** target, std::string* error_msg) {
   if (Begin() + offset >= End()) {
     *error_msg = StringPrintf("Offset %d is out of range for %s in ELF file: '%s'", offset, label,
                               file_->GetPath().c_str());
@@ -335,18 +381,23 @@
   return true;
 }
 
-bool ElfFile::CheckSectionsLinked(const byte* source, const byte* target) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::CheckSectionsLinked(const uint8_t* source, const uint8_t* target) const {
   // Only works in whole-program mode, as we need to iterate over the sections.
   // Note that we normally can't search by type, as duplicates are allowed for most section types.
   if (program_header_only_) {
     return true;
   }
 
-  Elf32_Shdr* source_section = nullptr;
-  Elf32_Word target_index = 0;
+  Elf_Shdr* source_section = nullptr;
+  Elf_Word target_index = 0;
   bool target_found = false;
-  for (Elf32_Word i = 0; i < GetSectionHeaderNum(); i++) {
-    Elf32_Shdr* section_header = GetSectionHeader(i);
+  for (Elf_Word i = 0; i < GetSectionHeaderNum(); i++) {
+    Elf_Shdr* section_header = GetSectionHeader(i);
 
     if (Begin() + section_header->sh_offset == source) {
       // Found the source.
@@ -366,7 +417,12 @@
   return target_found && source_section != nullptr && source_section->sh_link == target_index;
 }
 
-bool ElfFile::CheckSectionsExist(std::string* error_msg) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::CheckSectionsExist(std::string* error_msg) const {
   if (!program_header_only_) {
     // If in full mode, need section headers.
     if (section_headers_start_ == nullptr) {
@@ -399,8 +455,8 @@
     }
 
     // The symtab should link to the strtab.
-    if (!CheckSectionsLinked(reinterpret_cast<const byte*>(symtab_section_start_),
-                             reinterpret_cast<const byte*>(strtab_section_start_))) {
+    if (!CheckSectionsLinked(reinterpret_cast<const uint8_t*>(symtab_section_start_),
+                             reinterpret_cast<const uint8_t*>(strtab_section_start_))) {
       *error_msg = StringPrintf("Symtab is not linked to the strtab in ELF file: '%s'",
                                 file_->GetPath().c_str());
       return false;
@@ -425,8 +481,8 @@
   }
 
   // And the hash section should be linking to the dynsym.
-  if (!CheckSectionsLinked(reinterpret_cast<const byte*>(hash_section_start_),
-                           reinterpret_cast<const byte*>(dynsym_section_start_))) {
+  if (!CheckSectionsLinked(reinterpret_cast<const uint8_t*>(hash_section_start_),
+                           reinterpret_cast<const uint8_t*>(dynsym_section_start_))) {
     *error_msg = StringPrintf("Hash section is not linked to the dynstr in ELF file: '%s'",
                               file_->GetPath().c_str());
     return false;
@@ -435,7 +491,12 @@
   return true;
 }
 
-bool ElfFile::SetMap(MemMap* map, std::string* error_msg) {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::SetMap(MemMap* map, std::string* error_msg) {
   if (map == nullptr) {
     // MemMap::Open should have already set an error.
     DCHECK(!error_msg->empty());
@@ -445,7 +506,7 @@
   CHECK(map_.get() != nullptr) << file_->GetPath();
   CHECK(map_->Begin() != nullptr) << file_->GetPath();
 
-  header_ = reinterpret_cast<Elf32_Ehdr*>(map_->Begin());
+  header_ = reinterpret_cast<Elf_Ehdr*>(map_->Begin());
   if ((ELFMAG0 != header_->e_ident[EI_MAG0])
       || (ELFMAG1 != header_->e_ident[EI_MAG1])
       || (ELFMAG2 != header_->e_ident[EI_MAG2])
@@ -459,9 +520,10 @@
                               header_->e_ident[EI_MAG3]);
     return false;
   }
-  if (ELFCLASS32 != header_->e_ident[EI_CLASS]) {
+  uint8_t elf_class = (sizeof(Elf_Addr) == sizeof(Elf64_Addr)) ? ELFCLASS64 : ELFCLASS32;
+  if (elf_class != header_->e_ident[EI_CLASS]) {
     *error_msg = StringPrintf("Failed to find expected EI_CLASS value %d in %s, found %d",
-                              ELFCLASS32,
+                              elf_class,
                               file_->GetPath().c_str(),
                               header_->e_ident[EI_CLASS]);
     return false;
@@ -498,7 +560,7 @@
     *error_msg = StringPrintf("Failed to find expected e_entry value %d in %s, found %d",
                               0,
                               file_->GetPath().c_str(),
-                              header_->e_entry);
+                              static_cast<int32_t>(header_->e_entry));
     return false;
   }
   if (0 == header_->e_phoff) {
@@ -551,15 +613,15 @@
 
   if (!program_header_only_) {
     if (header_->e_phoff >= Size()) {
-      *error_msg = StringPrintf("Failed to find e_phoff value %d less than %zd in %s",
-                                header_->e_phoff,
+      *error_msg = StringPrintf("Failed to find e_phoff value %" PRIu64 " less than %zd in %s",
+                                static_cast<uint64_t>(header_->e_phoff),
                                 Size(),
                                 file_->GetPath().c_str());
       return false;
     }
     if (header_->e_shoff >= Size()) {
-      *error_msg = StringPrintf("Failed to find e_shoff value %d less than %zd in %s",
-                                header_->e_shoff,
+      *error_msg = StringPrintf("Failed to find e_shoff value %" PRIu64 " less than %zd in %s",
+                                static_cast<uint64_t>(header_->e_shoff),
                                 Size(),
                                 file_->GetPath().c_str());
       return false;
@@ -568,39 +630,64 @@
   return true;
 }
 
-
-Elf32_Ehdr& ElfFile::GetHeader() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Ehdr& ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetHeader() const {
   CHECK(header_ != nullptr);  // Header has been checked in SetMap. This is a sanity check.
   return *header_;
 }
 
-byte* ElfFile::GetProgramHeadersStart() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+uint8_t* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetProgramHeadersStart() const {
   CHECK(program_headers_start_ != nullptr);  // Header has been set in Setup. This is a sanity
                                              // check.
   return program_headers_start_;
 }
 
-byte* ElfFile::GetSectionHeadersStart() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+uint8_t* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetSectionHeadersStart() const {
   CHECK(!program_header_only_);              // Only used in "full" mode.
   CHECK(section_headers_start_ != nullptr);  // Is checked in CheckSectionsExist. Sanity check.
   return section_headers_start_;
 }
 
-Elf32_Phdr& ElfFile::GetDynamicProgramHeader() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Phdr& ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetDynamicProgramHeader() const {
   CHECK(dynamic_program_header_ != nullptr);  // Is checked in CheckSectionsExist. Sanity check.
   return *dynamic_program_header_;
 }
 
-Elf32_Dyn* ElfFile::GetDynamicSectionStart() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Dyn* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetDynamicSectionStart() const {
   CHECK(dynamic_section_start_ != nullptr);  // Is checked in CheckSectionsExist. Sanity check.
   return dynamic_section_start_;
 }
 
-static bool IsSymbolSectionType(Elf32_Word section_type) {
-  return ((section_type == SHT_SYMTAB) || (section_type == SHT_DYNSYM));
-}
-
-Elf32_Sym* ElfFile::GetSymbolSectionStart(Elf32_Word section_type) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Sym* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetSymbolSectionStart(Elf_Word section_type) const {
   CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type;
   switch (section_type) {
     case SHT_SYMTAB: {
@@ -618,7 +705,12 @@
   }
 }
 
-const char* ElfFile::GetStringSectionStart(Elf32_Word section_type) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+const char* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetStringSectionStart(Elf_Word section_type) const {
   CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type;
   switch (section_type) {
     case SHT_SYMTAB: {
@@ -634,7 +726,12 @@
   }
 }
 
-const char* ElfFile::GetString(Elf32_Word section_type, Elf32_Word i) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+const char* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetString(Elf_Word section_type, Elf_Word i) const {
   CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type;
   if (i == 0) {
     return nullptr;
@@ -649,19 +746,39 @@
 // WARNING: The following methods do not check for an error condition (non-existent hash section).
 //          It is the caller's job to do this.
 
-Elf32_Word* ElfFile::GetHashSectionStart() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetHashSectionStart() const {
   return hash_section_start_;
 }
 
-Elf32_Word ElfFile::GetHashBucketNum() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetHashBucketNum() const {
   return GetHashSectionStart()[0];
 }
 
-Elf32_Word ElfFile::GetHashChainNum() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetHashChainNum() const {
   return GetHashSectionStart()[1];
 }
 
-Elf32_Word ElfFile::GetHashBucket(size_t i, bool* ok) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetHashBucket(size_t i, bool* ok) const {
   if (i >= GetHashBucketNum()) {
     *ok = false;
     return 0;
@@ -671,7 +788,12 @@
   return GetHashSectionStart()[2 + i];
 }
 
-Elf32_Word ElfFile::GetHashChain(size_t i, bool* ok) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetHashChain(size_t i, bool* ok) const {
   if (i >= GetHashBucketNum()) {
     *ok = false;
     return 0;
@@ -681,22 +803,37 @@
   return GetHashSectionStart()[2 + GetHashBucketNum() + i];
 }
 
-Elf32_Word ElfFile::GetProgramHeaderNum() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetProgramHeaderNum() const {
   return GetHeader().e_phnum;
 }
 
-Elf32_Phdr* ElfFile::GetProgramHeader(Elf32_Word i) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Phdr* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetProgramHeader(Elf_Word i) const {
   CHECK_LT(i, GetProgramHeaderNum()) << file_->GetPath();  // Sanity check for caller.
-  byte* program_header = GetProgramHeadersStart() + (i * GetHeader().e_phentsize);
+  uint8_t* program_header = GetProgramHeadersStart() + (i * GetHeader().e_phentsize);
   if (program_header >= End()) {
     return nullptr;  // Failure condition.
   }
-  return reinterpret_cast<Elf32_Phdr*>(program_header);
+  return reinterpret_cast<Elf_Phdr*>(program_header);
 }
 
-Elf32_Phdr* ElfFile::FindProgamHeaderByType(Elf32_Word type) const {
-  for (Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) {
-    Elf32_Phdr* program_header = GetProgramHeader(i);
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Phdr* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FindProgamHeaderByType(Elf_Word type) const {
+  for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) {
+    Elf_Phdr* program_header = GetProgramHeader(i);
     if (program_header->p_type == type) {
       return program_header;
     }
@@ -704,30 +841,45 @@
   return nullptr;
 }
 
-Elf32_Word ElfFile::GetSectionHeaderNum() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetSectionHeaderNum() const {
   return GetHeader().e_shnum;
 }
 
-Elf32_Shdr* ElfFile::GetSectionHeader(Elf32_Word i) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Shdr* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetSectionHeader(Elf_Word i) const {
   // Can only access arbitrary sections when we have the whole file, not just program header.
   // Even if we Load(), it doesn't bring in all the sections.
   CHECK(!program_header_only_) << file_->GetPath();
   if (i >= GetSectionHeaderNum()) {
     return nullptr;  // Failure condition.
   }
-  byte* section_header = GetSectionHeadersStart() + (i * GetHeader().e_shentsize);
+  uint8_t* section_header = GetSectionHeadersStart() + (i * GetHeader().e_shentsize);
   if (section_header >= End()) {
     return nullptr;  // Failure condition.
   }
-  return reinterpret_cast<Elf32_Shdr*>(section_header);
+  return reinterpret_cast<Elf_Shdr*>(section_header);
 }
 
-Elf32_Shdr* ElfFile::FindSectionByType(Elf32_Word type) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Shdr* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FindSectionByType(Elf_Word type) const {
   // Can only access arbitrary sections when we have the whole file, not just program header.
   // We could change this to switch on known types if they were detected during loading.
   CHECK(!program_header_only_) << file_->GetPath();
-  for (Elf32_Word i = 0; i < GetSectionHeaderNum(); i++) {
-    Elf32_Shdr* section_header = GetSectionHeader(i);
+  for (Elf_Word i = 0; i < GetSectionHeaderNum(); i++) {
+    Elf_Shdr* section_header = GetSectionHeader(i);
     if (section_header->sh_type == type) {
       return section_header;
     }
@@ -749,16 +901,26 @@
   return h;
 }
 
-Elf32_Shdr* ElfFile::GetSectionNameStringSection() const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Shdr* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetSectionNameStringSection() const {
   return GetSectionHeader(GetHeader().e_shstrndx);
 }
 
-const byte* ElfFile::FindDynamicSymbolAddress(const std::string& symbol_name) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+const uint8_t* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FindDynamicSymbolAddress(const std::string& symbol_name) const {
   // Check that we have a hash section.
   if (GetHashSectionStart() == nullptr) {
     return nullptr;  // Failure condition.
   }
-  const Elf32_Sym* sym = FindDynamicSymbol(symbol_name);
+  const Elf_Sym* sym = FindDynamicSymbol(symbol_name);
   if (sym != nullptr) {
     // TODO: we need to change this to calculate base_address_ in ::Open,
     // otherwise it will be wrongly 0 if ::Load has not yet been called.
@@ -769,20 +931,25 @@
 }
 
 // WARNING: Only called from FindDynamicSymbolAddress. Elides check for hash section.
-const Elf32_Sym* ElfFile::FindDynamicSymbol(const std::string& symbol_name) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+const Elf_Sym* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FindDynamicSymbol(const std::string& symbol_name) const {
   if (GetHashBucketNum() == 0) {
     // No dynamic symbols at all.
     return nullptr;
   }
-  Elf32_Word hash = elfhash(symbol_name.c_str());
-  Elf32_Word bucket_index = hash % GetHashBucketNum();
+  Elf_Word hash = elfhash(symbol_name.c_str());
+  Elf_Word bucket_index = hash % GetHashBucketNum();
   bool ok;
-  Elf32_Word symbol_and_chain_index = GetHashBucket(bucket_index, &ok);
+  Elf_Word symbol_and_chain_index = GetHashBucket(bucket_index, &ok);
   if (!ok) {
     return nullptr;
   }
   while (symbol_and_chain_index != 0 /* STN_UNDEF */) {
-    Elf32_Sym* symbol = GetSymbol(SHT_DYNSYM, symbol_and_chain_index);
+    Elf_Sym* symbol = GetSymbol(SHT_DYNSYM, symbol_and_chain_index);
     if (symbol == nullptr) {
       return nullptr;  // Failure condition.
     }
@@ -798,23 +965,49 @@
   return nullptr;
 }
 
-Elf32_Word ElfFile::GetSymbolNum(Elf32_Shdr& section_header) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::IsSymbolSectionType(Elf_Word section_type) {
+  return ((section_type == SHT_SYMTAB) || (section_type == SHT_DYNSYM));
+}
+
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetSymbolNum(Elf_Shdr& section_header) const {
   CHECK(IsSymbolSectionType(section_header.sh_type))
       << file_->GetPath() << " " << section_header.sh_type;
   CHECK_NE(0U, section_header.sh_entsize) << file_->GetPath();
   return section_header.sh_size / section_header.sh_entsize;
 }
 
-Elf32_Sym* ElfFile::GetSymbol(Elf32_Word section_type,
-                              Elf32_Word i) const {
-  Elf32_Sym* sym_start = GetSymbolSectionStart(section_type);
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Sym* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetSymbol(Elf_Word section_type,
+                Elf_Word i) const {
+  Elf_Sym* sym_start = GetSymbolSectionStart(section_type);
   if (sym_start == nullptr) {
     return nullptr;
   }
   return sym_start + i;
 }
 
-ElfFile::SymbolTable** ElfFile::GetSymbolTable(Elf32_Word section_type) {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+typename ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::SymbolTable** ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetSymbolTable(Elf_Word section_type) {
   CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type;
   switch (section_type) {
     case SHT_SYMTAB: {
@@ -830,9 +1023,14 @@
   }
 }
 
-Elf32_Sym* ElfFile::FindSymbolByName(Elf32_Word section_type,
-                                     const std::string& symbol_name,
-                                     bool build_map) {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Sym* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FindSymbolByName(Elf_Word section_type,
+                       const std::string& symbol_name,
+                       bool build_map) {
   CHECK(!program_header_only_) << file_->GetPath();
   CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type;
 
@@ -841,20 +1039,22 @@
     if (*symbol_table == nullptr) {
       DCHECK(build_map);
       *symbol_table = new SymbolTable;
-      Elf32_Shdr* symbol_section = FindSectionByType(section_type);
+      Elf_Shdr* symbol_section = FindSectionByType(section_type);
       if (symbol_section == nullptr) {
         return nullptr;  // Failure condition.
       }
-      Elf32_Shdr* string_section = GetSectionHeader(symbol_section->sh_link);
+      Elf_Shdr* string_section = GetSectionHeader(symbol_section->sh_link);
       if (string_section == nullptr) {
         return nullptr;  // Failure condition.
       }
       for (uint32_t i = 0; i < GetSymbolNum(*symbol_section); i++) {
-        Elf32_Sym* symbol = GetSymbol(section_type, i);
+        Elf_Sym* symbol = GetSymbol(section_type, i);
         if (symbol == nullptr) {
           return nullptr;  // Failure condition.
         }
-        unsigned char type = ELF32_ST_TYPE(symbol->st_info);
+        unsigned char type = (sizeof(Elf_Addr) == sizeof(Elf64_Addr))
+                             ? ELF64_ST_TYPE(symbol->st_info)
+                             : ELF32_ST_TYPE(symbol->st_info);
         if (type == STT_NOTYPE) {
           continue;
         }
@@ -862,7 +1062,7 @@
         if (name == nullptr) {
           continue;
         }
-        std::pair<SymbolTable::iterator, bool> result =
+        std::pair<typename SymbolTable::iterator, bool> result =
             (*symbol_table)->insert(std::make_pair(name, symbol));
         if (!result.second) {
           // If a duplicate, make sure it has the same logical value. Seen on x86.
@@ -877,7 +1077,7 @@
       }
     }
     CHECK(*symbol_table != nullptr);
-    SymbolTable::const_iterator it = (*symbol_table)->find(symbol_name);
+    typename SymbolTable::const_iterator it = (*symbol_table)->find(symbol_name);
     if (it == (*symbol_table)->end()) {
       return nullptr;
     }
@@ -885,16 +1085,16 @@
   }
 
   // Fall back to linear search
-  Elf32_Shdr* symbol_section = FindSectionByType(section_type);
+  Elf_Shdr* symbol_section = FindSectionByType(section_type);
   if (symbol_section == nullptr) {
     return nullptr;
   }
-  Elf32_Shdr* string_section = GetSectionHeader(symbol_section->sh_link);
+  Elf_Shdr* string_section = GetSectionHeader(symbol_section->sh_link);
   if (string_section == nullptr) {
     return nullptr;
   }
   for (uint32_t i = 0; i < GetSymbolNum(*symbol_section); i++) {
-    Elf32_Sym* symbol = GetSymbol(section_type, i);
+    Elf_Sym* symbol = GetSymbol(section_type, i);
     if (symbol == nullptr) {
       return nullptr;  // Failure condition.
     }
@@ -909,20 +1109,30 @@
   return nullptr;
 }
 
-Elf32_Addr ElfFile::FindSymbolAddress(Elf32_Word section_type,
-                                      const std::string& symbol_name,
-                                      bool build_map) {
-  Elf32_Sym* symbol = FindSymbolByName(section_type, symbol_name, build_map);
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Addr ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FindSymbolAddress(Elf_Word section_type,
+                        const std::string& symbol_name,
+                        bool build_map) {
+  Elf_Sym* symbol = FindSymbolByName(section_type, symbol_name, build_map);
   if (symbol == nullptr) {
     return 0;
   }
   return symbol->st_value;
 }
 
-const char* ElfFile::GetString(Elf32_Shdr& string_section, Elf32_Word i) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+const char* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetString(Elf_Shdr& string_section, Elf_Word i) const {
   CHECK(!program_header_only_) << file_->GetPath();
   // TODO: remove this static_cast from enum when using -std=gnu++0x
-  if (static_cast<Elf32_Word>(SHT_STRTAB) != string_section.sh_type) {
+  if (static_cast<Elf_Word>(SHT_STRTAB) != string_section.sh_type) {
     return nullptr;  // Failure condition.
   }
   if (i >= string_section.sh_size) {
@@ -931,26 +1141,41 @@
   if (i == 0) {
     return nullptr;
   }
-  byte* strings = Begin() + string_section.sh_offset;
-  byte* string = strings + i;
+  uint8_t* strings = Begin() + string_section.sh_offset;
+  uint8_t* string = strings + i;
   if (string >= End()) {
     return nullptr;
   }
   return reinterpret_cast<const char*>(string);
 }
 
-Elf32_Word ElfFile::GetDynamicNum() const {
-  return GetDynamicProgramHeader().p_filesz / sizeof(Elf32_Dyn);
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetDynamicNum() const {
+  return GetDynamicProgramHeader().p_filesz / sizeof(Elf_Dyn);
 }
 
-Elf32_Dyn& ElfFile::GetDynamic(Elf32_Word i) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Dyn& ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetDynamic(Elf_Word i) const {
   CHECK_LT(i, GetDynamicNum()) << file_->GetPath();
   return *(GetDynamicSectionStart() + i);
 }
 
-Elf32_Dyn* ElfFile::FindDynamicByType(Elf32_Sword type) const {
-  for (Elf32_Word i = 0; i < GetDynamicNum(); i++) {
-    Elf32_Dyn* dyn = &GetDynamic(i);
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Dyn* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FindDynamicByType(Elf_Sword type) const {
+  for (Elf_Word i = 0; i < GetDynamicNum(); i++) {
+    Elf_Dyn* dyn = &GetDynamic(i);
     if (dyn->d_tag == type) {
       return dyn;
     }
@@ -958,8 +1183,13 @@
   return NULL;
 }
 
-Elf32_Word ElfFile::FindDynamicValueByType(Elf32_Sword type) const {
-  Elf32_Dyn* dyn = FindDynamicByType(type);
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FindDynamicValueByType(Elf_Sword type) const {
+  Elf_Dyn* dyn = FindDynamicByType(type);
   if (dyn == NULL) {
     return 0;
   } else {
@@ -967,53 +1197,88 @@
   }
 }
 
-Elf32_Rel* ElfFile::GetRelSectionStart(Elf32_Shdr& section_header) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Rel* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetRelSectionStart(Elf_Shdr& section_header) const {
   CHECK(SHT_REL == section_header.sh_type) << file_->GetPath() << " " << section_header.sh_type;
-  return reinterpret_cast<Elf32_Rel*>(Begin() + section_header.sh_offset);
+  return reinterpret_cast<Elf_Rel*>(Begin() + section_header.sh_offset);
 }
 
-Elf32_Word ElfFile::GetRelNum(Elf32_Shdr& section_header) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetRelNum(Elf_Shdr& section_header) const {
   CHECK(SHT_REL == section_header.sh_type) << file_->GetPath() << " " << section_header.sh_type;
   CHECK_NE(0U, section_header.sh_entsize) << file_->GetPath();
   return section_header.sh_size / section_header.sh_entsize;
 }
 
-Elf32_Rel& ElfFile::GetRel(Elf32_Shdr& section_header, Elf32_Word i) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Rel& ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetRel(Elf_Shdr& section_header, Elf_Word i) const {
   CHECK(SHT_REL == section_header.sh_type) << file_->GetPath() << " " << section_header.sh_type;
   CHECK_LT(i, GetRelNum(section_header)) << file_->GetPath();
   return *(GetRelSectionStart(section_header) + i);
 }
 
-Elf32_Rela* ElfFile::GetRelaSectionStart(Elf32_Shdr& section_header) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Rela* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+  Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+  ::GetRelaSectionStart(Elf_Shdr& section_header) const {
   CHECK(SHT_RELA == section_header.sh_type) << file_->GetPath() << " " << section_header.sh_type;
-  return reinterpret_cast<Elf32_Rela*>(Begin() + section_header.sh_offset);
+  return reinterpret_cast<Elf_Rela*>(Begin() + section_header.sh_offset);
 }
 
-Elf32_Word ElfFile::GetRelaNum(Elf32_Shdr& section_header) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Word ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetRelaNum(Elf_Shdr& section_header) const {
   CHECK(SHT_RELA == section_header.sh_type) << file_->GetPath() << " " << section_header.sh_type;
   return section_header.sh_size / section_header.sh_entsize;
 }
 
-Elf32_Rela& ElfFile::GetRela(Elf32_Shdr& section_header, Elf32_Word i) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Rela& ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetRela(Elf_Shdr& section_header, Elf_Word i) const {
   CHECK(SHT_RELA == section_header.sh_type) << file_->GetPath() << " " << section_header.sh_type;
   CHECK_LT(i, GetRelaNum(section_header)) << file_->GetPath();
   return *(GetRelaSectionStart(section_header) + i);
 }
 
 // Base on bionic phdr_table_get_load_size
-size_t ElfFile::GetLoadedSize() const {
-  Elf32_Addr min_vaddr = 0xFFFFFFFFu;
-  Elf32_Addr max_vaddr = 0x00000000u;
-  for (Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) {
-    Elf32_Phdr* program_header = GetProgramHeader(i);
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+size_t ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GetLoadedSize() const {
+  Elf_Addr min_vaddr = 0xFFFFFFFFu;
+  Elf_Addr max_vaddr = 0x00000000u;
+  for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) {
+    Elf_Phdr* program_header = GetProgramHeader(i);
     if (program_header->p_type != PT_LOAD) {
       continue;
     }
-    Elf32_Addr begin_vaddr = program_header->p_vaddr;
+    Elf_Addr begin_vaddr = program_header->p_vaddr;
     if (begin_vaddr < min_vaddr) {
        min_vaddr = begin_vaddr;
     }
-    Elf32_Addr end_vaddr = program_header->p_vaddr + program_header->p_memsz;
+    Elf_Addr end_vaddr = program_header->p_vaddr + program_header->p_memsz;
     if (end_vaddr > max_vaddr) {
       max_vaddr = end_vaddr;
     }
@@ -1025,7 +1290,12 @@
   return loaded_size;
 }
 
-bool ElfFile::Load(bool executable, std::string* error_msg) {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::Load(bool executable, std::string* error_msg) {
   CHECK(program_header_only_) << file_->GetPath();
 
   if (executable) {
@@ -1062,8 +1332,8 @@
   }
 
   bool reserved = false;
-  for (Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) {
-    Elf32_Phdr* program_header = GetProgramHeader(i);
+  for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) {
+    Elf_Phdr* program_header = GetProgramHeader(i);
     if (program_header == nullptr) {
       *error_msg = StringPrintf("No program header for entry %d in ELF file %s.",
                                 i, file_->GetPath().c_str());
@@ -1132,7 +1402,7 @@
     if (program_header->p_memsz == 0) {
       continue;
     }
-    byte* p_vaddr = base_address_ + program_header->p_vaddr;
+    uint8_t* p_vaddr = base_address_ + program_header->p_vaddr;
     int prot = 0;
     if (executable && ((program_header->p_flags & PF_X) != 0)) {
       prot |= PROT_EXEC;
@@ -1152,8 +1422,8 @@
     }
     if (file_length < (program_header->p_offset + program_header->p_memsz)) {
       *error_msg = StringPrintf("File size of %zd bytes not large enough to contain ELF segment "
-                                "%d of %d bytes: '%s'", file_length, i,
-                                program_header->p_offset + program_header->p_memsz,
+                                "%d of %" PRIu64 " bytes: '%s'", file_length, i,
+                                static_cast<uint64_t>(program_header->p_offset + program_header->p_memsz),
                                 file_->GetPath().c_str());
       return false;
     }
@@ -1179,17 +1449,17 @@
   }
 
   // Now that we are done loading, .dynamic should be in memory to find .dynstr, .dynsym, .hash
-  byte* dsptr = base_address_ + GetDynamicProgramHeader().p_vaddr;
+  uint8_t* dsptr = base_address_ + GetDynamicProgramHeader().p_vaddr;
   if ((dsptr < Begin() || dsptr >= End()) && !ValidPointer(dsptr)) {
     *error_msg = StringPrintf("dynamic section address invalid in ELF file %s",
                               file_->GetPath().c_str());
     return false;
   }
-  dynamic_section_start_ = reinterpret_cast<Elf32_Dyn*>(dsptr);
+  dynamic_section_start_ = reinterpret_cast<Elf_Dyn*>(dsptr);
 
-  for (Elf32_Word i = 0; i < GetDynamicNum(); i++) {
-    Elf32_Dyn& elf_dyn = GetDynamic(i);
-    byte* d_ptr = base_address_ + elf_dyn.d_un.d_ptr;
+  for (Elf_Word i = 0; i < GetDynamicNum(); i++) {
+    Elf_Dyn& elf_dyn = GetDynamic(i);
+    uint8_t* d_ptr = base_address_ + elf_dyn.d_un.d_ptr;
     switch (elf_dyn.d_tag) {
       case DT_HASH: {
         if (!ValidPointer(d_ptr)) {
@@ -1197,7 +1467,7 @@
                                     d_ptr, file_->GetPath().c_str());
           return false;
         }
-        hash_section_start_ = reinterpret_cast<Elf32_Word*>(d_ptr);
+        hash_section_start_ = reinterpret_cast<Elf_Word*>(d_ptr);
         break;
       }
       case DT_STRTAB: {
@@ -1215,7 +1485,7 @@
                                     d_ptr, file_->GetPath().c_str());
           return false;
         }
-        dynsym_section_start_ = reinterpret_cast<Elf32_Sym*>(d_ptr);
+        dynsym_section_start_ = reinterpret_cast<Elf_Sym*>(d_ptr);
         break;
       }
       case DT_NULL: {
@@ -1243,7 +1513,12 @@
   return true;
 }
 
-bool ElfFile::ValidPointer(const byte* start) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::ValidPointer(const uint8_t* start) const {
   for (size_t i = 0; i < segments_.size(); ++i) {
     const MemMap* segment = segments_[i];
     if (segment->Begin() <= start && start < segment->End()) {
@@ -1254,14 +1529,19 @@
 }
 
 
-Elf32_Shdr* ElfFile::FindSectionByName(const std::string& name) const {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+Elf_Shdr* ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FindSectionByName(const std::string& name) const {
   CHECK(!program_header_only_);
-  Elf32_Shdr* shstrtab_sec = GetSectionNameStringSection();
+  Elf_Shdr* shstrtab_sec = GetSectionNameStringSection();
   if (shstrtab_sec == nullptr) {
     return nullptr;
   }
   for (uint32_t i = 0; i < GetSectionHeaderNum(); i++) {
-    Elf32_Shdr* shdr = GetSectionHeader(i);
+    Elf_Shdr* shdr = GetSectionHeader(i);
     if (shdr == nullptr) {
       return nullptr;
     }
@@ -1276,7 +1556,7 @@
   return nullptr;
 }
 
-struct PACKED(1) FDE {
+struct PACKED(1) FDE32 {
   uint32_t raw_length_;
   uint32_t GetLength() {
     return raw_length_ + sizeof(raw_length_);
@@ -1287,25 +1567,186 @@
   uint8_t instructions[0];
 };
 
-static FDE* NextFDE(FDE* frame) {
-  byte* fde_bytes = reinterpret_cast<byte*>(frame);
+static FDE32* NextFDE(FDE32* frame) {
+  uint8_t* fde_bytes = reinterpret_cast<uint8_t*>(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) {
+  uint8_t* fde_bytes = reinterpret_cast<uint8_t*>(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,
+                           uint8_t* 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 FINAL {
+ 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() const {
+    return current_instruction_;
+  }
+
+  bool IsExtendedOpcode() const {
+    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* const header_;
+  uint8_t* const 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;
 }
@@ -1358,14 +1799,13 @@
   }
 }
 
-class DebugTag {
+class DebugTag FINAL {
  public:
-  const uint32_t index_;
   ~DebugTag() {}
   // Creates a new tag and moves data pointer up to the start of the next one.
   // nullptr means error.
-  static DebugTag* Create(const byte** data_pointer) {
-    const byte* data = *data_pointer;
+  static DebugTag* Create(const uint8_t** data_pointer) {
+    const uint8_t* data = *data_pointer;
     uint32_t index = DecodeUnsignedLeb128(&data);
     std::unique_ptr<DebugTag> tag(new DebugTag(index));
     tag->size_ = static_cast<uint32_t>(
@@ -1397,14 +1837,18 @@
     return size_;
   }
 
-  bool HasChild() {
+  bool HasChild() const {
     return has_child_;
   }
 
-  uint32_t GetTagNumber() {
+  uint32_t GetTagNumber() const {
     return tag_;
   }
 
+  uint32_t GetIndex() const {
+    return index_;
+  }
+
   // Gets the offset of a particular attribute in this tag structure.
   // Interpretation of the data is left to the consumer. 0 is returned if the
   // tag does not contain the attribute.
@@ -1434,6 +1878,8 @@
     size_map_.insert(std::pair<uint32_t, uint32_t>(type, attr_size));
     size_ += attr_size;
   }
+
+  const uint32_t index_;
   std::map<uint32_t, uint32_t> off_map_;
   std::map<uint32_t, uint32_t> size_map_;
   uint32_t size_;
@@ -1444,22 +1890,31 @@
 class DebugAbbrev {
  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));
-      }
+  static DebugAbbrev* Create(const uint8_t* dbg_abbrev, size_t dbg_abbrev_size) {
+    std::unique_ptr<DebugAbbrev> abbrev(new DebugAbbrev(dbg_abbrev, dbg_abbrev + dbg_abbrev_size));
+    if (!abbrev->ReadAtOffset(0)) {
+      return nullptr;
     }
     return abbrev.release();
   }
 
-  DebugTag* ReadTag(const byte* entry) {
+  bool ReadAtOffset(uint32_t abbrev_offset) {
+    tags_.clear();
+    tag_list_.clear();
+    const uint8_t* 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->GetIndex(), tag_list_.size()));
+        tag_list_.push_back(std::move(tag));
+      }
+    }
+    return true;
+  }
+
+  DebugTag* ReadTag(const uint8_t* entry) {
     uint32_t tag_num = DecodeUnsignedLeb128(&entry);
     auto it = tags_.find(tag_num);
     if (it == tags_.end()) {
@@ -1471,7 +1926,9 @@
   }
 
  private:
-  DebugAbbrev() {}
+  DebugAbbrev(const uint8_t* begin, const uint8_t* end) : begin_(begin), end_(end) {}
+  const uint8_t* const begin_;
+  const uint8_t* const end_;
   std::map<uint32_t, uint32_t> tags_;
   std::vector<std::unique_ptr<DebugTag>> tag_list_;
 };
@@ -1495,11 +1952,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<uint8_t*>(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,7 +1979,7 @@
   const DebugTag* GetCurrentTag() {
     return const_cast<DebugTag*>(current_tag_);
   }
-  byte* GetPointerToField(uint8_t dwarf_field) {
+  uint8_t* GetPointerToField(uint8_t dwarf_field) {
     if (current_tag_ == nullptr || current_entry_ == nullptr || current_entry_ >= last_entry_) {
       return nullptr;
     }
@@ -1527,52 +1994,104 @@
   }
 
  private:
+  static DebugInfoHeader* GetNextCu(DebugInfoHeader* hdr) {
+    uint8_t* hdr_byte = reinterpret_cast<uint8_t*>(hdr);
+    return reinterpret_cast<DebugInfoHeader*>(hdr_byte + sizeof(uint32_t) + hdr->unit_length);
+  }
+
   DebugInfoIterator(DebugInfoHeader* header, size_t frame_size, DebugAbbrev* abbrev)
       : abbrev_(abbrev),
-        last_entry_(reinterpret_cast<byte*>(header) + frame_size),
-        current_entry_(reinterpret_cast<byte*>(header) + sizeof(DebugInfoHeader)),
+        current_cu_(header),
+        next_cu_(GetNextCu(header)),
+        last_entry_(reinterpret_cast<uint8_t*>(header) + frame_size),
+        current_entry_(reinterpret_cast<uint8_t*>(header) + sizeof(DebugInfoHeader)),
         current_tag_(abbrev_->ReadTag(current_entry_)) {}
-  DebugAbbrev* abbrev_;
-  byte* last_entry_;
-  byte* current_entry_;
+  DebugAbbrev* const abbrev_;
+  DebugInfoHeader* current_cu_;
+  DebugInfoHeader* next_cu_;
+  uint8_t* const last_entry_;
+  uint8_t* 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));
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FixupDebugSections(off_t base_address_delta) {
+  const Elf_Shdr* debug_info = FindSectionByName(".debug_info");
+  const Elf_Shdr* debug_abbrev = FindSectionByName(".debug_abbrev");
+  const Elf_Shdr* eh_frame = FindSectionByName(".eh_frame");
+  const Elf_Shdr* debug_str = FindSectionByName(".debug_str");
+  const Elf_Shdr* debug_line = FindSectionByName(".debug_line");
+  const Elf_Shdr* strtab_sec = FindSectionByName(".strtab");
+  const Elf_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() {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+void ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::GdbJITSupport() {
   // We only get here if we only are mapping the program header.
   DCHECK(program_header_only_);
 
@@ -1580,43 +2099,36 @@
   std::string error_msg;
   // Make it MAP_PRIVATE so we can just give it to gdb if all the necessary
   // sections are there.
-  std::unique_ptr<ElfFile> all_ptr(Open(const_cast<File*>(file_), PROT_READ | PROT_WRITE,
-                                        MAP_PRIVATE, &error_msg));
+  std::unique_ptr<ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+      Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>>
+      all_ptr(Open(const_cast<File*>(file_), PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE, &error_msg));
   if (all_ptr.get() == nullptr) {
     return;
   }
-  ElfFile& all = *all_ptr;
+  ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+      Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>& 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");
-  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) {
+  // We need the eh_frame for gdb but debug info might be present without it.
+  const Elf_Shdr* eh_frame = all.FindSectionByName(".eh_frame");
+  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.
-  Elf32_Ehdr& elf_hdr = all.GetHeader();
+  Elf_Ehdr& elf_hdr = all.GetHeader();
   elf_hdr.e_entry = 0;
   elf_hdr.e_phoff = 0;
   elf_hdr.e_phnum = 0;
   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;
   }
@@ -1625,4 +2137,463 @@
   gdb_file_mapping_.reset(all_ptr.release());
 }
 
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::Strip(std::string* error_msg) {
+  // ELF files produced by MCLinker look roughly like this
+  //
+  // +------------+
+  // | Elf_Ehdr   | contains number of Elf_Shdr and offset to first
+  // +------------+
+  // | Elf_Phdr   | program headers
+  // | Elf_Phdr   |
+  // | ...        |
+  // | Elf_Phdr   |
+  // +------------+
+  // | section    | mixture of needed and unneeded sections
+  // +------------+
+  // | section    |
+  // +------------+
+  // | ...        |
+  // +------------+
+  // | section    |
+  // +------------+
+  // | Elf_Shdr   | section headers
+  // | Elf_Shdr   |
+  // | ...        | contains offset to section start
+  // | Elf_Shdr   |
+  // +------------+
+  //
+  // To strip:
+  // - leave the Elf_Ehdr and Elf_Phdr values in place.
+  // - walk the sections making a new set of Elf_Shdr section headers for what we want to keep
+  // - move the sections are keeping up to fill in gaps of sections we want to strip
+  // - write new Elf_Shdr section headers to end of file, updating Elf_Ehdr
+  // - truncate rest of file
+  //
+
+  std::vector<Elf_Shdr> section_headers;
+  std::vector<Elf_Word> section_headers_original_indexes;
+  section_headers.reserve(GetSectionHeaderNum());
+
+
+  Elf_Shdr* string_section = GetSectionNameStringSection();
+  CHECK(string_section != nullptr);
+  for (Elf_Word i = 0; i < GetSectionHeaderNum(); i++) {
+    Elf_Shdr* sh = GetSectionHeader(i);
+    CHECK(sh != nullptr);
+    const char* name = GetString(*string_section, sh->sh_name);
+    if (name == nullptr) {
+      CHECK_EQ(0U, i);
+      section_headers.push_back(*sh);
+      section_headers_original_indexes.push_back(0);
+      continue;
+    }
+    if (StartsWith(name, ".debug")
+        || (strcmp(name, ".strtab") == 0)
+        || (strcmp(name, ".symtab") == 0)) {
+      continue;
+    }
+    section_headers.push_back(*sh);
+    section_headers_original_indexes.push_back(i);
+  }
+  CHECK_NE(0U, section_headers.size());
+  CHECK_EQ(section_headers.size(), section_headers_original_indexes.size());
+
+  // section 0 is the NULL section, sections start at offset of first section
+  CHECK(GetSectionHeader(1) != nullptr);
+  Elf_Off offset = GetSectionHeader(1)->sh_offset;
+  for (size_t i = 1; i < section_headers.size(); i++) {
+    Elf_Shdr& new_sh = section_headers[i];
+    Elf_Shdr* old_sh = GetSectionHeader(section_headers_original_indexes[i]);
+    CHECK(old_sh != nullptr);
+    CHECK_EQ(new_sh.sh_name, old_sh->sh_name);
+    if (old_sh->sh_addralign > 1) {
+      offset = RoundUp(offset, old_sh->sh_addralign);
+    }
+    if (old_sh->sh_offset == offset) {
+      // already in place
+      offset += old_sh->sh_size;
+      continue;
+    }
+    // shift section earlier
+    memmove(Begin() + offset,
+            Begin() + old_sh->sh_offset,
+            old_sh->sh_size);
+    new_sh.sh_offset = offset;
+    offset += old_sh->sh_size;
+  }
+
+  Elf_Off shoff = offset;
+  size_t section_headers_size_in_bytes = section_headers.size() * sizeof(Elf_Shdr);
+  memcpy(Begin() + offset, &section_headers[0], section_headers_size_in_bytes);
+  offset += section_headers_size_in_bytes;
+
+  GetHeader().e_shnum = section_headers.size();
+  GetHeader().e_shoff = shoff;
+  int result = ftruncate(file_->Fd(), offset);
+  if (result != 0) {
+    *error_msg = StringPrintf("Failed to truncate while stripping ELF file: '%s': %s",
+                              file_->GetPath().c_str(), strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+static const bool DEBUG_FIXUP = false;
+
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::Fixup(uintptr_t base_address) {
+  if (!FixupDynamic(base_address)) {
+    LOG(WARNING) << "Failed to fixup .dynamic in " << file_->GetPath();
+    return false;
+  }
+  if (!FixupSectionHeaders(base_address)) {
+    LOG(WARNING) << "Failed to fixup section headers in " << file_->GetPath();
+    return false;
+  }
+  if (!FixupProgramHeaders(base_address)) {
+    LOG(WARNING) << "Failed to fixup program headers in " << file_->GetPath();
+    return false;
+  }
+  if (!FixupSymbols(base_address, true)) {
+    LOG(WARNING) << "Failed to fixup .dynsym in " << file_->GetPath();
+    return false;
+  }
+  if (!FixupSymbols(base_address, false)) {
+    LOG(WARNING) << "Failed to fixup .symtab in " << file_->GetPath();
+    return false;
+  }
+  if (!FixupRelocations(base_address)) {
+    LOG(WARNING) << "Failed to fixup .rel.dyn in " << file_->GetPath();
+    return false;
+  }
+  if (!FixupDebugSections(base_address)) {
+    LOG(WARNING) << "Failed to fixup debug sections in " << file_->GetPath();
+    return false;
+  }
+  return true;
+}
+
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FixupDynamic(uintptr_t base_address) {
+  for (Elf_Word i = 0; i < GetDynamicNum(); i++) {
+    Elf_Dyn& elf_dyn = GetDynamic(i);
+    Elf_Word d_tag = elf_dyn.d_tag;
+    if (IsDynamicSectionPointer(d_tag, GetHeader().e_machine)) {
+      Elf_Addr d_ptr = elf_dyn.d_un.d_ptr;
+      if (DEBUG_FIXUP) {
+        LOG(INFO) << StringPrintf("In %s moving Elf_Dyn[%d] from 0x%" PRIx64 " to 0x%" PRIx64,
+                                  GetFile().GetPath().c_str(), i,
+                                  static_cast<uint64_t>(d_ptr),
+                                  static_cast<uint64_t>(d_ptr + base_address));
+      }
+      d_ptr += base_address;
+      elf_dyn.d_un.d_ptr = d_ptr;
+    }
+  }
+  return true;
+}
+
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FixupSectionHeaders(uintptr_t base_address) {
+  for (Elf_Word i = 0; i < GetSectionHeaderNum(); i++) {
+    Elf_Shdr* sh = GetSectionHeader(i);
+    CHECK(sh != nullptr);
+    // 0 implies that the section will not exist in the memory of the process
+    if (sh->sh_addr == 0) {
+      continue;
+    }
+    if (DEBUG_FIXUP) {
+      LOG(INFO) << StringPrintf("In %s moving Elf_Shdr[%d] from 0x%" PRIx64 " to 0x%" PRIx64,
+                                GetFile().GetPath().c_str(), i,
+                                static_cast<uint64_t>(sh->sh_addr),
+                                static_cast<uint64_t>(sh->sh_addr + base_address));
+    }
+    sh->sh_addr += base_address;
+  }
+  return true;
+}
+
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FixupProgramHeaders(uintptr_t base_address) {
+  // TODO: ELFObjectFile doesn't have give to Elf_Phdr, so we do that ourselves for now.
+  for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) {
+    Elf_Phdr* ph = GetProgramHeader(i);
+    CHECK(ph != nullptr);
+    CHECK_EQ(ph->p_vaddr, ph->p_paddr) << GetFile().GetPath() << " i=" << i;
+    CHECK((ph->p_align == 0) || (0 == ((ph->p_vaddr - ph->p_offset) & (ph->p_align - 1))))
+            << GetFile().GetPath() << " i=" << i;
+    if (DEBUG_FIXUP) {
+      LOG(INFO) << StringPrintf("In %s moving Elf_Phdr[%d] from 0x%" PRIx64 " to 0x%" PRIx64,
+                                GetFile().GetPath().c_str(), i,
+                                static_cast<uint64_t>(ph->p_vaddr),
+                                static_cast<uint64_t>(ph->p_vaddr + base_address));
+    }
+    ph->p_vaddr += base_address;
+    ph->p_paddr += base_address;
+    CHECK((ph->p_align == 0) || (0 == ((ph->p_vaddr - ph->p_offset) & (ph->p_align - 1))))
+            << GetFile().GetPath() << " i=" << i;
+  }
+  return true;
+}
+
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FixupSymbols(uintptr_t base_address, bool dynamic) {
+  Elf_Word section_type = dynamic ? SHT_DYNSYM : SHT_SYMTAB;
+  // TODO: Unfortunate ELFObjectFile has protected symbol access, so use ElfFile
+  Elf_Shdr* symbol_section = FindSectionByType(section_type);
+  if (symbol_section == nullptr) {
+    // file is missing optional .symtab
+    CHECK(!dynamic) << GetFile().GetPath();
+    return true;
+  }
+  for (uint32_t i = 0; i < GetSymbolNum(*symbol_section); i++) {
+    Elf_Sym* symbol = GetSymbol(section_type, i);
+    CHECK(symbol != nullptr);
+    if (symbol->st_value != 0) {
+      if (DEBUG_FIXUP) {
+        LOG(INFO) << StringPrintf("In %s moving Elf_Sym[%d] from 0x%" PRIx64 " to 0x%" PRIx64,
+                                  GetFile().GetPath().c_str(), i,
+                                  static_cast<uint64_t>(symbol->st_value),
+                                  static_cast<uint64_t>(symbol->st_value + base_address));
+      }
+      symbol->st_value += base_address;
+    }
+  }
+  return true;
+}
+
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::FixupRelocations(uintptr_t base_address) {
+  for (Elf_Word i = 0; i < GetSectionHeaderNum(); i++) {
+    Elf_Shdr* sh = GetSectionHeader(i);
+    CHECK(sh != nullptr);
+    if (sh->sh_type == SHT_REL) {
+      for (uint32_t j = 0; j < GetRelNum(*sh); j++) {
+        Elf_Rel& rel = GetRel(*sh, j);
+        if (DEBUG_FIXUP) {
+          LOG(INFO) << StringPrintf("In %s moving Elf_Rel[%d] from 0x%" PRIx64 " to 0x%" PRIx64,
+                                    GetFile().GetPath().c_str(), j,
+                                    static_cast<uint64_t>(rel.r_offset),
+                                    static_cast<uint64_t>(rel.r_offset + base_address));
+        }
+        rel.r_offset += base_address;
+      }
+    } else if (sh->sh_type == SHT_RELA) {
+      for (uint32_t j = 0; j < GetRelaNum(*sh); j++) {
+        Elf_Rela& rela = GetRela(*sh, j);
+        if (DEBUG_FIXUP) {
+          LOG(INFO) << StringPrintf("In %s moving Elf_Rela[%d] from 0x%" PRIx64 " to 0x%" PRIx64,
+                                    GetFile().GetPath().c_str(), j,
+                                    static_cast<uint64_t>(rela.r_offset),
+                                    static_cast<uint64_t>(rela.r_offset + base_address));
+        }
+        rela.r_offset += base_address;
+      }
+    }
+  }
+  return true;
+}
+
+// Explicit instantiations
+template class ElfFileImpl<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Word,
+    Elf32_Sword, Elf32_Addr, Elf32_Sym, Elf32_Rel, Elf32_Rela, Elf32_Dyn, Elf32_Off>;
+template class ElfFileImpl<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Word,
+    Elf64_Sword, Elf64_Addr, Elf64_Sym, Elf64_Rel, Elf64_Rela, Elf64_Dyn, Elf64_Off>;
+
+ElfFile::ElfFile(ElfFileImpl32* elf32) : elf32_(elf32), elf64_(nullptr) {
+}
+
+ElfFile::ElfFile(ElfFileImpl64* elf64) : elf32_(nullptr), elf64_(elf64) {
+}
+
+ElfFile::~ElfFile() {
+  // Should never have 32 and 64-bit impls.
+  CHECK_NE(elf32_.get() == nullptr, elf64_.get() == nullptr);
+}
+
+ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+                       uint8_t* requested_base) {
+  if (file->GetLength() < EI_NIDENT) {
+    *error_msg = StringPrintf("File %s is too short to be a valid ELF file",
+                              file->GetPath().c_str());
+    return nullptr;
+  }
+  std::unique_ptr<MemMap> map(MemMap::MapFile(EI_NIDENT, PROT_READ, MAP_PRIVATE, file->Fd(), 0,
+                                              file->GetPath().c_str(), error_msg));
+  if (map == nullptr && map->Size() != EI_NIDENT) {
+    return nullptr;
+  }
+  uint8_t* header = map->Begin();
+  if (header[EI_CLASS] == ELFCLASS64) {
+    ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file, writable, program_header_only,
+                                                       error_msg, requested_base);
+    if (elf_file_impl == nullptr)
+      return nullptr;
+    return new ElfFile(elf_file_impl);
+  } else if (header[EI_CLASS] == ELFCLASS32) {
+    ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file, writable, program_header_only,
+                                                       error_msg, requested_base);
+    if (elf_file_impl == nullptr) {
+      return nullptr;
+    }
+    return new ElfFile(elf_file_impl);
+  } else {
+    *error_msg = StringPrintf("Failed to find expected EI_CLASS value %d or %d in %s, found %d",
+                              ELFCLASS32, ELFCLASS64,
+                              file->GetPath().c_str(),
+                              header[EI_CLASS]);
+    return nullptr;
+  }
+}
+
+ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg) {
+  if (file->GetLength() < EI_NIDENT) {
+    *error_msg = StringPrintf("File %s is too short to be a valid ELF file",
+                              file->GetPath().c_str());
+    return nullptr;
+  }
+  std::unique_ptr<MemMap> map(MemMap::MapFile(EI_NIDENT, PROT_READ, MAP_PRIVATE, file->Fd(), 0,
+                                              file->GetPath().c_str(), error_msg));
+  if (map == nullptr && map->Size() != EI_NIDENT) {
+    return nullptr;
+  }
+  uint8_t* header = map->Begin();
+  if (header[EI_CLASS] == ELFCLASS64) {
+    ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file, mmap_prot, mmap_flags, error_msg);
+    if (elf_file_impl == nullptr) {
+      return nullptr;
+    }
+    return new ElfFile(elf_file_impl);
+  } else if (header[EI_CLASS] == ELFCLASS32) {
+    ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file, mmap_prot, mmap_flags, error_msg);
+    if (elf_file_impl == nullptr) {
+      return nullptr;
+    }
+    return new ElfFile(elf_file_impl);
+  } else {
+    *error_msg = StringPrintf("Failed to find expected EI_CLASS value %d or %d in %s, found %d",
+                              ELFCLASS32, ELFCLASS64,
+                              file->GetPath().c_str(),
+                              header[EI_CLASS]);
+    return nullptr;
+  }
+}
+
+#define DELEGATE_TO_IMPL(func, ...) \
+  if (elf64_.get() != nullptr) { \
+    return elf64_->func(__VA_ARGS__); \
+  } else { \
+    DCHECK(elf32_.get() != nullptr); \
+    return elf32_->func(__VA_ARGS__); \
+  }
+
+bool ElfFile::Load(bool executable, std::string* error_msg) {
+  DELEGATE_TO_IMPL(Load, executable, error_msg);
+}
+
+const uint8_t* ElfFile::FindDynamicSymbolAddress(const std::string& symbol_name) const {
+  DELEGATE_TO_IMPL(FindDynamicSymbolAddress, symbol_name);
+}
+
+size_t ElfFile::Size() const {
+  DELEGATE_TO_IMPL(Size);
+}
+
+uint8_t* ElfFile::Begin() const {
+  DELEGATE_TO_IMPL(Begin);
+}
+
+uint8_t* ElfFile::End() const {
+  DELEGATE_TO_IMPL(End);
+}
+
+const File& ElfFile::GetFile() const {
+  DELEGATE_TO_IMPL(GetFile);
+}
+
+bool ElfFile::GetSectionOffsetAndSize(const char* section_name, uint64_t* offset, uint64_t* size) {
+  if (elf32_.get() == nullptr) {
+    CHECK(elf64_.get() != nullptr);
+
+    Elf64_Shdr *shdr = elf64_->FindSectionByName(section_name);
+    if (shdr == nullptr) {
+      return false;
+    }
+    if (offset != nullptr) {
+      *offset = shdr->sh_offset;
+    }
+    if (size != nullptr) {
+      *size = shdr->sh_size;
+    }
+    return true;
+  } else {
+    Elf32_Shdr *shdr = elf32_->FindSectionByName(section_name);
+    if (shdr == nullptr) {
+      return false;
+    }
+    if (offset != nullptr) {
+      *offset = shdr->sh_offset;
+    }
+    if (size != nullptr) {
+      *size = shdr->sh_size;
+    }
+    return true;
+  }
+}
+
+uint64_t ElfFile::FindSymbolAddress(unsigned section_type,
+                                    const std::string& symbol_name,
+                                    bool build_map) {
+  DELEGATE_TO_IMPL(FindSymbolAddress, section_type, symbol_name, build_map);
+}
+
+size_t ElfFile::GetLoadedSize() const {
+  DELEGATE_TO_IMPL(GetLoadedSize);
+}
+
+bool ElfFile::Strip(File* file, std::string* error_msg) {
+  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg));
+  if (elf_file.get() == nullptr) {
+    return false;
+  }
+
+  if (elf_file->elf64_.get() != nullptr)
+    return elf_file->elf64_->Strip(error_msg);
+  else
+    return elf_file->elf32_->Strip(error_msg);
+}
+
+bool ElfFile::Fixup(uintptr_t base_address) {
+  DELEGATE_TO_IMPL(Fixup, base_address);
+}
+
 }  // namespace art
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 9c0bc9a..41c54bc 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -17,23 +17,25 @@
 #ifndef ART_RUNTIME_ELF_FILE_H_
 #define ART_RUNTIME_ELF_FILE_H_
 
-#include <map>
 #include <memory>
-#include <vector>
+#include <string>
 
-#include "base/unix_file/fd_file.h"
-#include "globals.h"
-#include "elf_utils.h"
-#include "mem_map.h"
+#include "base/macros.h"
+// Explicitly include our own elf.h to avoid Linux and other dependencies.
+#include "./elf.h"
 #include "os.h"
 
 namespace art {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+class ElfFileImpl;
 
-// Interface to GDB JIT for backtrace information.
-extern "C" {
-  struct JITCodeEntry;
-}
-
+// Explicitly instantiated in elf_file.cc
+typedef ElfFileImpl<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Word, Elf32_Sword,
+                    Elf32_Addr, Elf32_Sym, Elf32_Rel, Elf32_Rela, Elf32_Dyn, Elf32_Off> ElfFileImpl32;
+typedef ElfFileImpl<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Word, Elf64_Sword,
+                    Elf64_Addr, Elf64_Sym, Elf64_Rel, Elf64_Rela, Elf64_Dyn, Elf64_Off> ElfFileImpl64;
 
 // Used for compile time and runtime for ElfFile access. Because of
 // the need for use at runtime, cannot directly use LLVM classes such as
@@ -48,164 +50,58 @@
   ~ElfFile();
 
   // Load segments into memory based on PT_LOAD program headers
-
-  const File& GetFile() const {
-    return *file_;
-  }
-
-  // The start of the memory map address range for this ELF file.
-  byte* Begin() const {
-    return map_->Begin();
-  }
-
-  // The end of the memory map address range for this ELF file.
-  byte* End() const {
-    return map_->End();
-  }
-
-  size_t Size() const {
-    return map_->Size();
-  }
-
-  Elf32_Ehdr& GetHeader() const;
-
-  Elf32_Word GetProgramHeaderNum() const;
-  Elf32_Phdr* GetProgramHeader(Elf32_Word) const;
-
-  Elf32_Word GetSectionHeaderNum() const;
-  Elf32_Shdr* GetSectionHeader(Elf32_Word) const;
-  Elf32_Shdr* FindSectionByType(Elf32_Word type) const;
-  Elf32_Shdr* FindSectionByName(const std::string& name) const;
-
-  Elf32_Shdr* GetSectionNameStringSection() const;
-
-  // Find .dynsym using .hash for more efficient lookup than FindSymbolAddress.
-  const byte* FindDynamicSymbolAddress(const std::string& symbol_name) const;
-
-  Elf32_Word GetSymbolNum(Elf32_Shdr&) const;
-  Elf32_Sym* GetSymbol(Elf32_Word section_type, Elf32_Word i) const;
-
-  // Find address of symbol in specified table, returning 0 if it is
-  // not found. See FindSymbolByName for an explanation of build_map.
-  Elf32_Addr FindSymbolAddress(Elf32_Word section_type,
-                               const std::string& symbol_name,
-                               bool build_map);
-
-  // Lookup a string given string section and offset. Returns nullptr for
-  // special 0 offset.
-  const char* GetString(Elf32_Shdr&, Elf32_Word) const;
-
-  Elf32_Word GetDynamicNum() const;
-  Elf32_Dyn& GetDynamic(Elf32_Word) const;
-
-  Elf32_Word GetRelNum(Elf32_Shdr&) const;
-  Elf32_Rel& GetRel(Elf32_Shdr&, Elf32_Word) const;
-
-  Elf32_Word GetRelaNum(Elf32_Shdr&) const;
-  Elf32_Rela& GetRela(Elf32_Shdr&, Elf32_Word) const;
-
-  // Returns the expected size when the file is loaded at runtime
-  size_t GetLoadedSize() const;
-
-  // Load segments into memory based on PT_LOAD program headers.
-  // executable is true at run time, false at compile time.
   bool Load(bool executable, std::string* error_msg);
 
+  const uint8_t* FindDynamicSymbolAddress(const std::string& symbol_name) const;
+
+  size_t Size() const;
+
+  // The start of the memory map address range for this ELF file.
+  uint8_t* Begin() const;
+
+  // The end of the memory map address range for this ELF file.
+  uint8_t* End() const;
+
+  const File& GetFile() const;
+
+  bool GetSectionOffsetAndSize(const char* section_name, uint64_t* offset, uint64_t* size);
+
+  uint64_t FindSymbolAddress(unsigned section_type,
+                             const std::string& symbol_name,
+                             bool build_map);
+
+  size_t GetLoadedSize() const;
+
+  // Strip an ELF file of unneeded debugging information.
+  // Returns true on success, false on failure.
+  static bool Strip(File* file, std::string* error_msg);
+
+  // Fixup an ELF file so that that oat header will be loaded at oat_begin.
+  // Returns true on success, false on failure.
+  static bool Fixup(File* file, uintptr_t oat_data_begin);
+
+  bool Fixup(uintptr_t base_address);
+
+  bool Is64Bit() const {
+    return elf64_.get() != nullptr;
+  }
+
+  ElfFileImpl32* GetImpl32() const {
+    return elf32_.get();
+  }
+
+  ElfFileImpl64* GetImpl64() const {
+    return elf64_.get();
+  }
+
  private:
-  ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
+  explicit ElfFile(ElfFileImpl32* elf32);
+  explicit ElfFile(ElfFileImpl64* elf64);
 
-  bool Setup(int prot, int flags, std::string* error_msg);
+  const std::unique_ptr<ElfFileImpl32> elf32_;
+  const std::unique_ptr<ElfFileImpl64> elf64_;
 
-  bool SetMap(MemMap* map, std::string* error_msg);
-
-  byte* GetProgramHeadersStart() const;
-  byte* GetSectionHeadersStart() const;
-  Elf32_Phdr& GetDynamicProgramHeader() const;
-  Elf32_Dyn* GetDynamicSectionStart() const;
-  Elf32_Sym* GetSymbolSectionStart(Elf32_Word section_type) const;
-  const char* GetStringSectionStart(Elf32_Word section_type) const;
-  Elf32_Rel* GetRelSectionStart(Elf32_Shdr&) const;
-  Elf32_Rela* GetRelaSectionStart(Elf32_Shdr&) const;
-  Elf32_Word* GetHashSectionStart() const;
-  Elf32_Word GetHashBucketNum() const;
-  Elf32_Word GetHashChainNum() const;
-  Elf32_Word GetHashBucket(size_t i, bool* ok) const;
-  Elf32_Word GetHashChain(size_t i, bool* ok) const;
-
-  typedef std::map<std::string, Elf32_Sym*> SymbolTable;
-  SymbolTable** GetSymbolTable(Elf32_Word section_type);
-
-  bool ValidPointer(const byte* start) const;
-
-  const Elf32_Sym* FindDynamicSymbol(const std::string& symbol_name) const;
-
-  // Check that certain sections and their dependencies exist.
-  bool CheckSectionsExist(std::string* error_msg) const;
-
-  // Check that the link of the first section links to the second section.
-  bool CheckSectionsLinked(const byte* source, const byte* target) const;
-
-  // Check whether the offset is in range, and set to target to Begin() + offset if OK.
-  bool CheckAndSet(Elf32_Off offset, const char* label, byte** target, std::string* error_msg);
-
-  // Find symbol in specified table, returning nullptr if it is not found.
-  //
-  // If build_map is true, builds a map to speed repeated access. The
-  // map does not included untyped symbol values (aka STT_NOTYPE)
-  // since they can contain duplicates. If build_map is false, the map
-  // will be used if it was already created. Typically build_map
-  // should be set unless only a small number of symbols will be
-  // looked up.
-  Elf32_Sym* FindSymbolByName(Elf32_Word section_type,
-                              const std::string& symbol_name,
-                              bool build_map);
-
-  Elf32_Phdr* FindProgamHeaderByType(Elf32_Word type) const;
-
-  Elf32_Dyn* FindDynamicByType(Elf32_Sword type) const;
-  Elf32_Word FindDynamicValueByType(Elf32_Sword type) const;
-
-  // Lookup a string by section type. Returns nullptr for special 0 offset.
-  const char* GetString(Elf32_Word section_type, Elf32_Word) const;
-
-  const File* const file_;
-  const bool writable_;
-  const bool program_header_only_;
-
-  // ELF header mapping. If program_header_only_ is false, will
-  // actually point to the entire elf file.
-  std::unique_ptr<MemMap> map_;
-  Elf32_Ehdr* header_;
-  std::vector<MemMap*> segments_;
-
-  // Pointer to start of first PT_LOAD program segment after Load()
-  // when program_header_only_ is true.
-  byte* base_address_;
-
-  // The program header should always available but use GetProgramHeadersStart() to be sure.
-  byte* program_headers_start_;
-
-  // Conditionally available values. Use accessors to ensure they exist if they are required.
-  byte* section_headers_start_;
-  Elf32_Phdr* dynamic_program_header_;
-  Elf32_Dyn* dynamic_section_start_;
-  Elf32_Sym* symtab_section_start_;
-  Elf32_Sym* dynsym_section_start_;
-  char* strtab_section_start_;
-  char* dynstr_section_start_;
-  Elf32_Word* hash_section_start_;
-
-  SymbolTable* symtab_symbol_table_;
-  SymbolTable* dynsym_symbol_table_;
-
-  // Support for GDB JIT
-  byte* jit_elf_image_;
-  JITCodeEntry* jit_gdb_entry_;
-  std::unique_ptr<ElfFile> gdb_file_mapping_;
-  void GdbJITSupport();
-
-  // When not-null, override the base vaddr we memory map LOAD segments into.
-  uint8_t* requested_base_;
+  DISALLOW_COPY_AND_ASSIGN(ElfFile);
 };
 
 }  // namespace art
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
new file mode 100644
index 0000000..a70fa17
--- /dev/null
+++ b/runtime/elf_file_impl.h
@@ -0,0 +1,218 @@
+/*
+ * 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_ELF_FILE_IMPL_H_
+#define ART_RUNTIME_ELF_FILE_IMPL_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+// Explicitly include our own elf.h to avoid Linux and other dependencies.
+#include "./elf.h"
+#include "mem_map.h"
+
+namespace art {
+
+extern "C" {
+  struct JITCodeEntry;
+}
+
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+class ElfFileImpl {
+ public:
+  static ElfFileImpl* Open(File* file, bool writable, bool program_header_only,
+                           std::string* error_msg, uint8_t* requested_base = nullptr);
+  static ElfFileImpl* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
+  ~ElfFileImpl();
+
+  const File& GetFile() const {
+    return *file_;
+  }
+
+  uint8_t* Begin() const {
+    return map_->Begin();
+  }
+
+  uint8_t* End() const {
+    return map_->End();
+  }
+
+  size_t Size() const {
+    return map_->Size();
+  }
+
+  Elf_Ehdr& GetHeader() const;
+
+  Elf_Word GetProgramHeaderNum() const;
+  Elf_Phdr* GetProgramHeader(Elf_Word) const;
+
+  Elf_Word GetSectionHeaderNum() const;
+  Elf_Shdr* GetSectionHeader(Elf_Word) const;
+  Elf_Shdr* FindSectionByType(Elf_Word type) const;
+  Elf_Shdr* FindSectionByName(const std::string& name) const;
+
+  Elf_Shdr* GetSectionNameStringSection() const;
+
+  // Find .dynsym using .hash for more efficient lookup than FindSymbolAddress.
+  const uint8_t* FindDynamicSymbolAddress(const std::string& symbol_name) const;
+
+  static bool IsSymbolSectionType(Elf_Word section_type);
+  Elf_Word GetSymbolNum(Elf_Shdr&) const;
+  Elf_Sym* GetSymbol(Elf_Word section_type, Elf_Word i) const;
+
+  // Find address of symbol in specified table, returning 0 if it is
+  // not found. See FindSymbolByName for an explanation of build_map.
+  Elf_Addr FindSymbolAddress(Elf_Word section_type,
+                             const std::string& symbol_name,
+                             bool build_map);
+
+  // Lookup a string given string section and offset. Returns nullptr for
+  // special 0 offset.
+  const char* GetString(Elf_Shdr&, Elf_Word) const;
+
+  Elf_Word GetDynamicNum() const;
+  Elf_Dyn& GetDynamic(Elf_Word) const;
+
+  Elf_Word GetRelNum(Elf_Shdr&) const;
+  Elf_Rel& GetRel(Elf_Shdr&, Elf_Word) const;
+
+  Elf_Word GetRelaNum(Elf_Shdr&) const;
+  Elf_Rela& GetRela(Elf_Shdr&, Elf_Word) const;
+
+  // Returns the expected size when the file is loaded at runtime
+  size_t GetLoadedSize() const;
+
+  // Load segments into memory based on PT_LOAD program headers.
+  // executable is true at run time, false at compile time.
+  bool Load(bool executable, std::string* error_msg);
+
+  bool Fixup(uintptr_t base_address);
+  bool FixupDynamic(uintptr_t base_address);
+  bool FixupSectionHeaders(uintptr_t base_address);
+  bool FixupProgramHeaders(uintptr_t base_address);
+  bool FixupSymbols(uintptr_t base_address, bool dynamic);
+  bool FixupRelocations(uintptr_t base_address);
+  bool FixupDebugSections(off_t base_address_delta);
+
+  bool Strip(std::string* error_msg);
+
+ private:
+  ElfFileImpl(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
+
+  bool Setup(int prot, int flags, std::string* error_msg);
+
+  bool SetMap(MemMap* map, std::string* error_msg);
+
+  uint8_t* GetProgramHeadersStart() const;
+  uint8_t* GetSectionHeadersStart() const;
+  Elf_Phdr& GetDynamicProgramHeader() const;
+  Elf_Dyn* GetDynamicSectionStart() const;
+  Elf_Sym* GetSymbolSectionStart(Elf_Word section_type) const;
+  const char* GetStringSectionStart(Elf_Word section_type) const;
+  Elf_Rel* GetRelSectionStart(Elf_Shdr&) const;
+  Elf_Rela* GetRelaSectionStart(Elf_Shdr&) const;
+  Elf_Word* GetHashSectionStart() const;
+  Elf_Word GetHashBucketNum() const;
+  Elf_Word GetHashChainNum() const;
+  Elf_Word GetHashBucket(size_t i, bool* ok) const;
+  Elf_Word GetHashChain(size_t i, bool* ok) const;
+
+  typedef std::map<std::string, Elf_Sym*> SymbolTable;
+  SymbolTable** GetSymbolTable(Elf_Word section_type);
+
+  bool ValidPointer(const uint8_t* start) const;
+
+  const Elf_Sym* FindDynamicSymbol(const std::string& symbol_name) const;
+
+  // Check that certain sections and their dependencies exist.
+  bool CheckSectionsExist(std::string* error_msg) const;
+
+  // Check that the link of the first section links to the second section.
+  bool CheckSectionsLinked(const uint8_t* source, const uint8_t* target) const;
+
+  // Check whether the offset is in range, and set to target to Begin() + offset if OK.
+  bool CheckAndSet(Elf32_Off offset, const char* label, uint8_t** target, std::string* error_msg);
+
+  // Find symbol in specified table, returning nullptr if it is not found.
+  //
+  // If build_map is true, builds a map to speed repeated access. The
+  // map does not included untyped symbol values (aka STT_NOTYPE)
+  // since they can contain duplicates. If build_map is false, the map
+  // will be used if it was already created. Typically build_map
+  // should be set unless only a small number of symbols will be
+  // looked up.
+  Elf_Sym* FindSymbolByName(Elf_Word section_type,
+                            const std::string& symbol_name,
+                            bool build_map);
+
+  Elf_Phdr* FindProgamHeaderByType(Elf_Word type) const;
+
+  Elf_Dyn* FindDynamicByType(Elf_Sword type) const;
+  Elf_Word FindDynamicValueByType(Elf_Sword type) const;
+
+  // Lookup a string by section type. Returns nullptr for special 0 offset.
+  const char* GetString(Elf_Word section_type, Elf_Word) const;
+
+  const File* const file_;
+  const bool writable_;
+  const bool program_header_only_;
+
+  // ELF header mapping. If program_header_only_ is false, will
+  // actually point to the entire elf file.
+  std::unique_ptr<MemMap> map_;
+  Elf_Ehdr* header_;
+  std::vector<MemMap*> segments_;
+
+  // Pointer to start of first PT_LOAD program segment after Load()
+  // when program_header_only_ is true.
+  uint8_t* base_address_;
+
+  // The program header should always available but use GetProgramHeadersStart() to be sure.
+  uint8_t* program_headers_start_;
+
+  // Conditionally available values. Use accessors to ensure they exist if they are required.
+  uint8_t* section_headers_start_;
+  Elf_Phdr* dynamic_program_header_;
+  Elf_Dyn* dynamic_section_start_;
+  Elf_Sym* symtab_section_start_;
+  Elf_Sym* dynsym_section_start_;
+  char* strtab_section_start_;
+  char* dynstr_section_start_;
+  Elf_Word* hash_section_start_;
+
+  SymbolTable* symtab_symbol_table_;
+  SymbolTable* dynsym_symbol_table_;
+
+  // Support for GDB JIT
+  uint8_t* jit_elf_image_;
+  JITCodeEntry* jit_gdb_entry_;
+  std::unique_ptr<ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+                  Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel,
+                  Elf_Rela, Elf_Dyn, Elf_Off>> gdb_file_mapping_;
+  void GdbJITSupport();
+
+  // Override the 'base' p_vaddr in the first LOAD segment with this value (if non-null).
+  uint8_t* requested_base_;
+
+  DISALLOW_COPY_AND_ASSIGN(ElfFileImpl);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_ELF_FILE_IMPL_H_
diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h
index 5966d05..7b00bad 100644
--- a/runtime/elf_utils.h
+++ b/runtime/elf_utils.h
@@ -24,6 +24,8 @@
 
 #include "base/logging.h"
 
+namespace art {
+
 // Architecture dependent flags for the ELF header.
 #define EF_ARM_EABI_VER5 0x05000000
 #define EF_MIPS_ABI_O32 0x00001000
@@ -67,11 +69,11 @@
 // Patching section type
 #define SHT_OAT_PATCH        SHT_LOUSER
 
-inline void SetBindingAndType(Elf32_Sym* sym, unsigned char b, unsigned char t) {
+static inline void SetBindingAndType(Elf32_Sym* sym, unsigned char b, unsigned char t) {
   sym->st_info = (b << 4) + (t & 0x0f);
 }
 
-inline bool IsDynamicSectionPointer(Elf32_Word d_tag, Elf32_Word e_machine) {
+static inline bool IsDynamicSectionPointer(Elf32_Word d_tag, Elf32_Word e_machine) {
   switch (d_tag) {
     // case 1: well known d_tag values that imply Elf32_Dyn.d_un contains an address in d_ptr
     case DT_PLTGOT:
@@ -163,4 +165,6 @@
   }
 }
 
+}  // namespace art
+
 #endif  // ART_RUNTIME_ELF_UTILS_H_
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index c0b7b06..1a8ca02 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -35,8 +35,8 @@
 
 namespace art {
 
-// 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 +78,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 {
@@ -89,7 +89,7 @@
   return klass;
 }
 
-// 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 +105,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
     }
@@ -118,8 +118,8 @@
 // cannot be resolved, throw an error. If it can, use it to create an instance.
 // When verification/compiler hasn't been able to verify access, optionally perform an access
 // check.
-// 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,
@@ -137,10 +137,9 @@
 }
 
 // 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,
                                                           gc::AllocatorType allocator_type) {
   DCHECK(klass != nullptr);
@@ -159,10 +158,9 @@
 }
 
 // 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,
                                                              gc::AllocatorType allocator_type) {
   DCHECK(klass != nullptr);
@@ -171,8 +169,8 @@
 }
 
 
-// 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,
@@ -207,8 +205,8 @@
 // it cannot be resolved, throw an error. If it can, use it to create an array.
 // When verification/compiler hasn't been able to verify access, optionally perform an access
 // 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 +221,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 +250,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 +315,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;
       }
@@ -354,7 +353,7 @@
                                                     mirror::Object** this_object,
                                                     mirror::ArtMethod** referrer, Thread* self) {
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
-  mirror::ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, *referrer, type);
+  mirror::ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, *referrer);
   if (resolved_method == nullptr) {
     StackHandleScope<1> hs(self);
     mirror::Object* null_this = nullptr;
@@ -498,11 +497,8 @@
     case StaticPrimitiveRead:    is_primitive = true;  is_set = false; is_static = true;  break;
     case StaticPrimitiveWrite:   is_primitive = true;  is_set = true;  is_static = true;  break;
     default:
-      LOG(FATAL) << "UNREACHABLE";  // Assignment below to avoid GCC warnings.
-      is_primitive = true;
-      is_set = true;
-      is_static = true;
-      break;
+      LOG(FATAL) << "UNREACHABLE";
+      UNREACHABLE();
   }
   if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
     // Incompatible class change.
@@ -601,7 +597,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
   }
@@ -638,18 +634,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..da2dfe1 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,17 @@
 }
 
 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());
-  }
   // 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();
+  mirror::Class* return_type = self->GetCurrentMethod(nullptr)->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(self->GetCurrentMethod(nullptr)).c_str());
   }
 }
 
@@ -282,20 +280,19 @@
       return zero;
     } else {
       StackHandleScope<1> hs(soa.Self());
-      MethodHelper mh_interface_method(
+      Handle<mirror::ArtMethod> h_interface_method(
           hs.NewHandle(soa.Decode<mirror::ArtMethod*>(interface_method_jobj)));
       // This can cause thread suspension.
-      mirror::Class* result_type = mh_interface_method.GetReturnType();
+      mirror::Class* result_type = h_interface_method->GetReturnType();
       mirror::Object* result_ref = soa.Decode<mirror::Object*>(result);
       mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj);
       mirror::ArtMethod* proxy_method;
-      if (mh_interface_method.GetMethod()->GetDeclaringClass()->IsInterface()) {
-        proxy_method = rcvr->GetClass()->FindVirtualMethodForInterface(
-            mh_interface_method.GetMethod());
+      if (h_interface_method->GetDeclaringClass()->IsInterface()) {
+        proxy_method = rcvr->GetClass()->FindVirtualMethodForInterface(h_interface_method.Get());
       } else {
         // Proxy dispatch to a method defined in Object.
-        DCHECK(mh_interface_method.GetMethod()->GetDeclaringClass()->IsObjectClass());
-        proxy_method = mh_interface_method.GetMethod();
+        DCHECK(h_interface_method->GetDeclaringClass()->IsObjectClass());
+        proxy_method = h_interface_method.Get();
       }
       ThrowLocation throw_location(rcvr, proxy_method, -1);
       JValue result_unboxed;
@@ -343,4 +340,28 @@
     return zero;
   }
 }
+
+bool FillArrayData(mirror::Object* obj, const Instruction::ArrayDataPayload* payload) {
+  DCHECK_EQ(payload->ident, static_cast<uint16_t>(Instruction::kArrayDataSignature));
+  if (UNLIKELY(obj == nullptr)) {
+    ThrowNullPointerException(nullptr, "null array in FILL_ARRAY_DATA");
+    return false;
+  }
+  mirror::Array* array = obj->AsArray();
+  DCHECK(!array->IsObjectArray());
+  if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
+    Thread* self = Thread::Current();
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewExceptionF(throw_location,
+                             "Ljava/lang/ArrayIndexOutOfBoundsException;",
+                             "failed FILL_ARRAY_DATA; length=%d, index=%d",
+                             array->GetLength(), payload->element_count);
+    return false;
+  }
+  // Copy data from dex file to memory assuming both are little endian.
+  uint32_t size_in_bytes = payload->element_count * payload->element_width;
+  memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes);
+  return true;
+}
+
 }  // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index bc95c23..311cafa 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -22,6 +22,7 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "dex_instruction.h"
 #include "gc/allocator_type.h"
 #include "invoke_type.h"
 #include "jvalue.h"
@@ -46,7 +47,6 @@
                                                             Thread* self, bool* slow_path)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-// 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)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -65,7 +65,6 @@
 // Given the context of a calling Method and a resolved class, create an instance.
 template <bool kInstrumented>
 ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeResolved(mirror::Class* klass,
-                                                                        mirror::ArtMethod* method,
                                                                         Thread* self,
                                                                         gc::AllocatorType allocator_type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -73,7 +72,6 @@
 // Given the context of a calling Method and an initialized class, create an instance.
 template <bool kInstrumented>
 ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeInitialized(mirror::Class* klass,
-                                                                           mirror::ArtMethod* method,
                                                                            Thread* self,
                                                                            gc::AllocatorType allocator_type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -174,69 +172,13 @@
 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)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-// Entry point for deoptimization.
-extern "C" void art_quick_deoptimize();
-static inline uintptr_t GetQuickDeoptimizationEntryPoint() {
-  return reinterpret_cast<uintptr_t>(art_quick_deoptimize);
-}
-
-// Return address of instrumentation stub.
-extern "C" void art_quick_instrumentation_entry(void*);
-static inline void* GetQuickInstrumentationEntryPoint() {
-  return reinterpret_cast<void*>(art_quick_instrumentation_entry);
-}
-
-// The return_pc of instrumentation exit stub.
-extern "C" void art_quick_instrumentation_exit();
-static inline uintptr_t GetQuickInstrumentationExitPc() {
-  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);
-}
-
-static inline const void* GetPortableToQuickBridge() {
-  // 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();
-}
-
-extern "C" void art_portable_proxy_invoke_handler();
-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() {
-  return reinterpret_cast<void*>(art_quick_proxy_invoke_handler);
-}
-
-extern "C" void* art_jni_dlsym_lookup_stub(JNIEnv*, jobject);
-static inline void* GetJniDlsymLookupStub() {
-  return reinterpret_cast<void*>(art_jni_dlsym_lookup_stub);
-}
+bool FillArrayData(mirror::Object* obj, const Instruction::ArrayDataPayload* payload)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 template <typename INT_TYPE, typename FLOAT_TYPE>
 static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f);
diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
index 64faf76..908d3cd 100644
--- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
+++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
@@ -25,7 +25,7 @@
 namespace art {
 
 // TODO: Make the MethodHelper here be compaction safe.
-extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh,
+extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper* mh,
                                                    const DexFile::CodeItem* code_item,
                                                    ShadowFrame* shadow_frame, JValue* result) {
   mirror::ArtMethod* method = shadow_frame->GetMethod();
@@ -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;
@@ -53,7 +54,7 @@
   } else {
     method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
                    (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
-                   result, mh.GetShorty());
+                   result, mh->GetShorty());
   }
 }
 
diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.h b/runtime/entrypoints/interpreter/interpreter_entrypoints.h
index d8b2204..5d646e9 100644
--- a/runtime/entrypoints/interpreter/interpreter_entrypoints.h
+++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.h
@@ -33,10 +33,10 @@
 
 // Pointers to functions that are called by interpreter trampolines via thread-local storage.
 struct PACKED(4) InterpreterEntryPoints {
-  void (*pInterpreterToInterpreterBridge)(Thread* self, MethodHelper& mh,
+  void (*pInterpreterToInterpreterBridge)(Thread* self, MethodHelper* mh,
                                           const DexFile::CodeItem* code_item,
                                           ShadowFrame* shadow_frame, JValue* result);
-  void (*pInterpreterToCompiledCodeBridge)(Thread* self, MethodHelper& mh,
+  void (*pInterpreterToCompiledCodeBridge)(Thread* self, MethodHelper* mh,
                                            const DexFile::CodeItem* code_item,
                                            ShadowFrame* shadow_frame, JValue* result);
 };
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index edb3b72..2752407 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -45,7 +45,7 @@
     return NULL;
   } else {
     // Register so that future calls don't come here
-    method->RegisterNative(self, native_code, false);
+    method->RegisterNative(native_code, false);
     return native_code;
   }
 }
diff --git a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc b/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
index 686954b..afe769e 100644
--- a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
@@ -15,9 +15,8 @@
  */
 
 #include "dex_instruction.h"
-#include "entrypoints/entrypoint_utils-inl.h"
+#include "entrypoints/entrypoint_utils.h"
 #include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
 
 namespace art {
 
@@ -26,25 +25,11 @@
                                                        mirror::Array* array,
                                                        uint32_t payload_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  UNUSED(dex_pc);
   const DexFile::CodeItem* code_item = method->GetCodeItem();
   const Instruction::ArrayDataPayload* payload =
       reinterpret_cast<const Instruction::ArrayDataPayload*>(code_item->insns_ + payload_offset);
-  DCHECK_EQ(payload->ident, static_cast<uint16_t>(Instruction::kArrayDataSignature));
-  if (UNLIKELY(array == NULL)) {
-    ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
-    return;  // Error
-  }
-  DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
-  if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
-    Thread* self = Thread::Current();
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;",
-                             "failed FILL_ARRAY_DATA; length=%d, index=%d",
-                             array->GetLength(), payload->element_count - 1);
-    return;  // Error
-  }
-  uint32_t size_in_bytes = payload->element_count * payload->element_width;
-  memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes);
+  FillArrayData(array, payload);
 }
 
 }  // namespace art
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..e7975f8 100644
--- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
@@ -19,6 +19,7 @@
 
 #include "dex_instruction-inl.h"
 #include "entrypoints/entrypoint_utils-inl.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "interpreter/interpreter.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/object-inl.h"
@@ -64,8 +65,8 @@
     caller_mh_(caller_mh),
     args_in_regs_(ComputeArgsInRegs(caller_mh)),
     num_params_(caller_mh.NumArgs()),
-    reg_args_(reinterpret_cast<byte*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
-    stack_args_(reinterpret_cast<byte*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
+    reg_args_(reinterpret_cast<uint8_t*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
+    stack_args_(reinterpret_cast<uint8_t*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
                 + PORTABLE_STACK_ARG_SKIP),
     cur_args_(reg_args_),
     cur_arg_index_(0),
@@ -88,8 +89,8 @@
     return caller_mh_.GetParamPrimitiveType(param_index_);
   }
 
-  byte* GetParamAddress() const {
-    return cur_args_ + (cur_arg_index_ * kPointerSize);
+  uint8_t* GetParamAddress() const {
+    return cur_args_ + (cur_arg_index_ * sizeof(void*));
   }
 
   void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -120,6 +121,7 @@
  private:
   static size_t ComputeArgsInRegs(MethodHelper& mh) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
 #if (defined(__i386__))
+    UNUSED(mh);
     return 0;
 #else
     size_t args_in_regs = 0;
@@ -137,9 +139,9 @@
   MethodHelper& caller_mh_;
   const size_t args_in_regs_;
   const size_t num_params_;
-  byte* const reg_args_;
-  byte* const stack_args_;
-  byte* cur_args_;
+  uint8_t* const reg_args_;
+  uint8_t* const stack_args_;
+  uint8_t* cur_args_;
   size_t cur_arg_index_;
   size_t param_index_;
 };
@@ -171,7 +173,7 @@
         break;
       case Primitive::kPrimVoid:
         LOG(FATAL) << "UNREACHABLE";
-        break;
+        UNREACHABLE();
     }
     ++cur_reg_;
   }
@@ -215,14 +217,14 @@
     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;
       }
     }
 
-    JValue result = interpreter::EnterInterpreterFromStub(self, mh, code_item, *shadow_frame);
+    JValue result = interpreter::EnterInterpreterFromEntryPoint(self, &mh, code_item, shadow_frame);
     // Pop transition.
     self->PopManagedStackFragment(fragment);
     return result.GetJ();
@@ -260,8 +262,7 @@
         break;
       case Primitive::kPrimVoid:
         LOG(FATAL) << "UNREACHABLE";
-        val.j = 0;
-        break;
+        UNREACHABLE();
     }
     args_.push_back(val);
   }
@@ -323,7 +324,7 @@
   uint32_t dex_pc;
   mirror::ArtMethod* caller = self->GetCurrentMethod(&dex_pc);
 
-  ClassLinker* linker = Runtime::Current()->GetClassLinker();
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   InvokeType invoke_type;
   bool is_range;
   if (called->IsRuntimeMethod()) {
@@ -379,7 +380,7 @@
         is_range = true;
     }
     uint32_t dex_method_idx = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
-    called = linker->ResolveMethod(Thread::Current(), dex_method_idx, &caller, invoke_type);
+    called = class_linker->ResolveMethod(Thread::Current(), dex_method_idx, &caller, invoke_type);
     // Incompatible class change should have been handled in resolve method.
     CHECK(!called->CheckIncompatibleClassChange(invoke_type));
     // Refine called method based on receiver.
@@ -399,41 +400,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);
+    class_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
+        code = class_linker->GetPortableOatCodeFor(called, &have_portable_code);
       }
     } 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
+        code = class_linker->GetPortableOatCodeFor(called, &have_portable_code);
       } 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
+          code = class_linker->GetPortableOatCodeFor(called, &have_portable_code);
         }
       }
     } else {
@@ -444,7 +431,7 @@
     // Expect class to at least be initializing.
     DCHECK(called->GetDeclaringClass()->IsInitializing());
     // Don't want infinite recursion.
-    DCHECK(code != linker->GetPortableResolutionTrampoline());
+    DCHECK(!class_linker->IsPortableResolutionStub(code));
     // Set up entry into main method
     *called_addr = called;
   }
diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h
index e573d6d..49357ad 100644
--- a/runtime/entrypoints/quick/callee_save_frame.h
+++ b/runtime/entrypoints/quick/callee_save_frame.h
@@ -19,6 +19,7 @@
 
 #include "base/mutex.h"
 #include "instruction_set.h"
+#include "runtime.h"
 #include "thread-inl.h"
 
 // Specific frame size code is in architecture-specific files. We include this to compile-time
@@ -34,16 +35,41 @@
 class ArtMethod;
 }  // namespace mirror
 
-// Place a special frame at the TOS that will save the callee saves for the given type.
-static inline void FinishCalleeSaveFrameSetup(Thread* self, StackReference<mirror::ArtMethod>* sp,
-                                              Runtime::CalleeSaveType type)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // Be aware the store below may well stomp on an incoming argument.
-  Locks::mutator_lock_->AssertSharedHeld(self);
-  sp->Assign(Runtime::Current()->GetCalleeSaveMethod(type));
-  self->SetTopOfStack(sp, 0);
-  self->VerifyStack();
-}
+class ScopedQuickEntrypointChecks {
+ public:
+  explicit ScopedQuickEntrypointChecks(Thread *self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : self_(self) {
+    if (kIsDebugBuild) {
+      TestsOnEntry();
+    }
+  }
+
+  explicit ScopedQuickEntrypointChecks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : self_(kIsDebugBuild ? Thread::Current() : nullptr) {
+    if (kIsDebugBuild) {
+      TestsOnEntry();
+    }
+  }
+
+  ~ScopedQuickEntrypointChecks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (kIsDebugBuild) {
+      TestsOnExit();
+    }
+  }
+
+ private:
+  void TestsOnEntry() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Locks::mutator_lock_->AssertSharedHeld(self_);
+    self_->VerifyStack();
+  }
+
+  void TestsOnExit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Locks::mutator_lock_->AssertSharedHeld(self_);
+    self_->VerifyStack();
+  }
+
+  Thread* const self_;
+};
 
 static constexpr size_t GetCalleeSaveFrameSize(InstructionSet isa, Runtime::CalleeSaveType type) {
   // constexpr must be a return statement.
@@ -69,7 +95,8 @@
 }
 
 // Note: this specialized statement is sanity-checked in the quick-trampoline gtest.
-static constexpr size_t GetCalleeSavePCOffset(InstructionSet isa, Runtime::CalleeSaveType type) {
+static constexpr size_t GetCalleeSaveReturnPcOffset(InstructionSet isa,
+                                                    Runtime::CalleeSaveType type) {
   return GetCalleeSaveFrameSize(isa, type) - GetConstExprPointerSize(isa);
 }
 
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 1f2713a..c0b79b2 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -25,64 +25,119 @@
 
 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) \
+    uint32_t type_idx, mirror::ArtMethod* method, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
+  ScopedQuickEntrypointChecks sqec(self); \
+  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; \
+      } \
+    } \
+  } \
   return AllocObjectFromCode<false, instrumented_bool>(type_idx, method, self, allocator_type); \
 } \
 extern "C" mirror::Object* artAllocObjectFromCodeResolved##suffix##suffix2( \
-    mirror::Class* klass, mirror::ArtMethod* method, Thread* self, \
-    StackReference<mirror::ArtMethod>* sp) \
+    mirror::Class* klass, mirror::ArtMethod* method, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
-  return AllocObjectFromCodeResolved<instrumented_bool>(klass, method, self, allocator_type); \
+  UNUSED(method); \
+  ScopedQuickEntrypointChecks sqec(self); \
+  if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \
+    if (LIKELY(klass->IsInitialized())) { \
+      size_t byte_count = klass->GetObjectSize(); \
+      byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
+      mirror::Object* obj; \
+      if (LIKELY(byte_count < self->TlabSize())) { \
+        obj = self->AllocTlab(byte_count); \
+        DCHECK(obj != nullptr) << "AllocTlab can't fail"; \
+        obj->SetClass(klass); \
+        if (kUseBakerOrBrooksReadBarrier) { \
+          if (kUseBrooksReadBarrier) { \
+            obj->SetReadBarrierPointer(obj); \
+          } \
+          obj->AssertReadBarrierPointer(); \
+        } \
+        QuasiAtomic::ThreadFenceForConstructor(); \
+        return obj; \
+      } \
+    } \
+  } \
+  return AllocObjectFromCodeResolved<instrumented_bool>(klass, self, allocator_type); \
 } \
 extern "C" mirror::Object* artAllocObjectFromCodeInitialized##suffix##suffix2( \
-    mirror::Class* klass, mirror::ArtMethod* method, Thread* self, \
-    StackReference<mirror::ArtMethod>* sp) \
+    mirror::Class* klass, mirror::ArtMethod* method, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
-  return AllocObjectFromCodeInitialized<instrumented_bool>(klass, method, self, allocator_type); \
+  UNUSED(method); \
+  ScopedQuickEntrypointChecks sqec(self); \
+  if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \
+    size_t byte_count = klass->GetObjectSize(); \
+    byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
+    mirror::Object* obj; \
+    if (LIKELY(byte_count < self->TlabSize())) { \
+      obj = self->AllocTlab(byte_count); \
+      DCHECK(obj != nullptr) << "AllocTlab can't fail"; \
+      obj->SetClass(klass); \
+      if (kUseBakerOrBrooksReadBarrier) { \
+        if (kUseBrooksReadBarrier) { \
+          obj->SetReadBarrierPointer(obj); \
+        } \
+        obj->AssertReadBarrierPointer(); \
+      } \
+      QuasiAtomic::ThreadFenceForConstructor(); \
+      return obj; \
+    } \
+  } \
+  return AllocObjectFromCodeInitialized<instrumented_bool>(klass, self, allocator_type); \
 } \
 extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheck##suffix##suffix2( \
-    uint32_t type_idx, mirror::ArtMethod* method, Thread* self, \
-    StackReference<mirror::ArtMethod>* sp) \
+    uint32_t type_idx, mirror::ArtMethod* method, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
+  ScopedQuickEntrypointChecks sqec(self); \
   return AllocObjectFromCode<true, instrumented_bool>(type_idx, method, self, allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \
-    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \
-    StackReference<mirror::ArtMethod>* sp) \
+    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
+  ScopedQuickEntrypointChecks sqec(self); \
   return AllocArrayFromCode<false, instrumented_bool>(type_idx, method, component_count, self, \
                                                       allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \
-    mirror::Class* klass, mirror::ArtMethod* method, int32_t component_count, Thread* self, \
-    StackReference<mirror::ArtMethod>* sp) \
+    mirror::Class* klass, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
+  ScopedQuickEntrypointChecks sqec(self); \
   return AllocArrayFromCodeResolved<false, instrumented_bool>(klass, method, component_count, self, \
                                                               allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
-    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \
-    StackReference<mirror::ArtMethod>* sp) \
+    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
+  ScopedQuickEntrypointChecks sqec(self); \
   return AllocArrayFromCode<true, instrumented_bool>(type_idx, method, component_count, self, \
                                                      allocator_type); \
 } \
 extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \
-    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \
-    StackReference<mirror::ArtMethod>* sp) \
+    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
+  ScopedQuickEntrypointChecks sqec(self); \
   if (!instrumented_bool) { \
     return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, false, allocator_type); \
   } else { \
@@ -90,10 +145,9 @@
   } \
 } \
 extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
-    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \
-    StackReference<mirror::ArtMethod>* sp) \
+    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
+  ScopedQuickEntrypointChecks sqec(self); \
   if (!instrumented_bool) { \
     return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true, allocator_type); \
   } else { \
@@ -155,10 +209,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;
@@ -173,31 +227,34 @@
 }
 
 void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) {
-  switch (entry_points_allocator) {
 #if !defined(__APPLE__) || !defined(__LP64__)
+  switch (entry_points_allocator) {
     case gc::kAllocatorTypeDlMalloc: {
       SetQuickAllocEntryPoints_dlmalloc(qpoints, entry_points_instrumented);
-      break;
+      return;
     }
     case gc::kAllocatorTypeRosAlloc: {
       SetQuickAllocEntryPoints_rosalloc(qpoints, entry_points_instrumented);
-      break;
+      return;
     }
     case gc::kAllocatorTypeBumpPointer: {
       CHECK(kMovingCollector);
       SetQuickAllocEntryPoints_bump_pointer(qpoints, entry_points_instrumented);
-      break;
+      return;
     }
     case gc::kAllocatorTypeTLAB: {
       CHECK(kMovingCollector);
       SetQuickAllocEntryPoints_tlab(qpoints, entry_points_instrumented);
+      return;
+    }
+    default:
       break;
-    }
-#endif
-    default: {
-      LOG(FATAL) << "Unimplemented";
-    }
   }
+#else
+  UNUSED(qpoints);
+#endif
+  UNIMPLEMENTED(FATAL);
+  UNREACHABLE();
 }
 
 }  // namespace art
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_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
new file mode 100644
index 0000000..7d77721
--- /dev/null
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -0,0 +1,109 @@
+/*
+ * 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_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_EXTERNS_H_
+#define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_EXTERNS_H_
+
+#include <cstdint>
+
+// These are extern declarations of assembly stubs with common names.
+
+// Cast entrypoints.
+extern "C" void art_quick_check_cast(void*, void*);
+
+// DexCache entrypoints.
+extern "C" void* art_quick_initialize_static_storage(uint32_t, void*);
+extern "C" void* art_quick_initialize_type(uint32_t, void*);
+extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, void*);
+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*);
+extern "C" int64_t art_quick_get64_static(uint32_t);
+extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
+extern "C" void* art_quick_get_obj_static(uint32_t);
+
+// Array entrypoints.
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
+extern "C" void art_quick_handle_fill_data(void*, void*);
+
+// Lock entrypoints.
+extern "C" void art_quick_lock_object(void*);
+extern "C" void art_quick_unlock_object(void*);
+
+// Math entrypoints.
+extern "C" int64_t art_quick_d2l(double);
+extern "C" int64_t art_quick_f2l(float);
+extern "C" int64_t art_quick_ldiv(int64_t, int64_t);
+extern "C" int64_t art_quick_lmod(int64_t, int64_t);
+extern "C" int64_t art_quick_lmul(int64_t, int64_t);
+extern "C" uint64_t art_quick_lshl(uint64_t, uint32_t);
+extern "C" uint64_t art_quick_lshr(uint64_t, uint32_t);
+extern "C" uint64_t art_quick_lushr(uint64_t, uint32_t);
+extern "C" int64_t art_quick_mul_long(int64_t, int64_t);
+extern "C" uint64_t art_quick_shl_long(uint64_t, uint32_t);
+extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t);
+extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t);
+
+// Intrinsic entrypoints.
+extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
+extern "C" int32_t art_quick_string_compareto(void*, void*);
+extern "C" void* art_quick_memcpy(void*, const void*, size_t);
+
+// Invoke entrypoints.
+extern "C" void art_quick_imt_conflict_trampoline(art::mirror::ArtMethod*);
+extern "C" void art_quick_resolution_trampoline(art::mirror::ArtMethod*);
+extern "C" void art_quick_to_interpreter_bridge(art::mirror::ArtMethod*);
+extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
+extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
+extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
+extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
+extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
+
+// Thread entrypoints.
+extern "C" void art_quick_test_suspend();
+
+// Throw entrypoints.
+extern "C" void art_quick_deliver_exception(void*);
+extern "C" void art_quick_throw_array_bounds(int32_t index, int32_t limit);
+extern "C" void art_quick_throw_div_zero();
+extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
+extern "C" void art_quick_throw_null_pointer_exception();
+extern "C" void art_quick_throw_stack_overflow(void*);
+
+#endif  // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_EXTERNS_H_
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index f9f62c2..14ab320 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -27,9 +27,8 @@
 
 namespace art {
 
-extern "C" void artDeoptimize(Thread* self, StackReference<mirror::ArtMethod>* sp)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+extern "C" void artDeoptimize(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   self->SetException(ThrowLocation(), Thread::GetDeoptimizationException());
   self->QuickDeliverException();
 }
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 704db05..2e7c8ba 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -27,42 +27,39 @@
 
 extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx,
                                                              mirror::ArtMethod* referrer,
-                                                             Thread* self,
-                                                             StackReference<mirror::ArtMethod>* sp)
+                                                             Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Called to ensure static storage base is initialized for direct static field reads and writes.
   // A class may be accessing another class' fields when it doesn't have access, as access has been
   // given by inheritance.
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  ScopedQuickEntrypointChecks sqec(self);
   return ResolveVerifyAndClinit(type_idx, referrer, self, true, false);
 }
 
 extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx,
                                                     mirror::ArtMethod* referrer,
-                                                    Thread* self,
-                                                    StackReference<mirror::ArtMethod>* sp)
+                                                    Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Called when method->dex_cache_resolved_types_[] misses.
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  ScopedQuickEntrypointChecks sqec(self);
   return ResolveVerifyAndClinit(type_idx, referrer, self, false, false);
 }
 
 extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx,
-    mirror::ArtMethod* referrer,
-    Thread* self,
-    StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+                                                                   mirror::ArtMethod* referrer,
+                                                                   Thread* self)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Called when caller isn't guaranteed to have access to a type and the dex cache may be
   // unpopulated.
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  ScopedQuickEntrypointChecks sqec(self);
   return ResolveVerifyAndClinit(type_idx, referrer, self, false, true);
 }
 
 extern "C" mirror::String* artResolveStringFromCode(mirror::ArtMethod* referrer,
                                                     int32_t string_idx,
-                                                    Thread* self,
-                                                    StackReference<mirror::ArtMethod>* sp)
+                                                    Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  ScopedQuickEntrypointChecks sqec(self);
   return ResolveStringFromCode(referrer, string_idx);
 }
 
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..7326fcf 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -25,143 +25,354 @@
 
 namespace art {
 
+extern "C" int8_t artGetByteStaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer,
+                                           Thread* self)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+                                          sizeof(int8_t));
+  if (LIKELY(field != nullptr)) {
+    return field->GetByte(field->GetDeclaringClass());
+  }
+  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t));
+  if (LIKELY(field != nullptr)) {
+    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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+                                          sizeof(int8_t));
+  if (LIKELY(field != nullptr)) {
+    return field->GetBoolean(field->GetDeclaringClass());
+  }
+  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t));
+  if (LIKELY(field != nullptr)) {
+    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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+                                          sizeof(int16_t));
+  if (LIKELY(field != nullptr)) {
+    return field->GetShort(field->GetDeclaringClass());
+  }
+  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t));
+  if (LIKELY(field != nullptr)) {
+    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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+                                          sizeof(int16_t));
+  if (LIKELY(field != nullptr)) {
+    return field->GetChar(field->GetDeclaringClass());
+  }
+  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t));
+  if (LIKELY(field != nullptr)) {
+    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)
+                                           Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
                                           sizeof(int32_t));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     return field->Get32(field->GetDeclaringClass());
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int32_t));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     return field->Get32(field->GetDeclaringClass());
   }
-  return 0;  // Will throw exception by checking with Thread::Current
+  return 0;  // Will throw exception by checking with Thread::Current.
 }
 
 extern "C" uint64_t artGet64StaticFromCode(uint32_t field_idx,
                                            mirror::ArtMethod* referrer,
-                                           Thread* self, StackReference<mirror::ArtMethod>* sp)
+                                           Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
                                           sizeof(int64_t));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     return field->Get64(field->GetDeclaringClass());
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int64_t));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     return field->Get64(field->GetDeclaringClass());
   }
-  return 0;  // Will throw exception by checking with Thread::Current
+  return 0;  // Will throw exception by checking with Thread::Current.
 }
 
 extern "C" mirror::Object* artGetObjStaticFromCode(uint32_t field_idx,
                                                    mirror::ArtMethod* referrer,
-                                                   Thread* self,
-                                                   StackReference<mirror::ArtMethod>* sp)
+                                                   Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectRead,
                                           sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     return field->GetObj(field->GetDeclaringClass());
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<StaticObjectRead, true>(field_idx, referrer, self,
                                                     sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     return field->GetObj(field->GetDeclaringClass());
   }
-  return NULL;  // Will throw exception by checking with Thread::Current
+  return nullptr;  // 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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+                                          sizeof(int8_t));
+  if (LIKELY(field != nullptr && obj != nullptr)) {
+    return field->GetByte(obj);
+  }
+  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+                                                         sizeof(int8_t));
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
+      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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+                                          sizeof(int8_t));
+  if (LIKELY(field != nullptr && obj != nullptr)) {
+    return field->GetBoolean(obj);
+  }
+  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+                                                         sizeof(int8_t));
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
+      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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+                                          sizeof(int16_t));
+  if (LIKELY(field != nullptr && obj != nullptr)) {
+    return field->GetShort(obj);
+  }
+  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+                                                         sizeof(int16_t));
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
+      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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+                                          sizeof(int16_t));
+  if (LIKELY(field != nullptr && obj != nullptr)) {
+    return field->GetChar(obj);
+  }
+  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+                                                         sizeof(int16_t));
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
+      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)
+                                             mirror::ArtMethod* referrer, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
                                           sizeof(int32_t));
-  if (LIKELY(field != NULL && obj != NULL)) {
+  if (LIKELY(field != nullptr && obj != nullptr)) {
     return field->Get32(obj);
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
                                                          sizeof(int32_t));
-  if (LIKELY(field != NULL)) {
-    if (UNLIKELY(obj == NULL)) {
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
       ThrowLocation throw_location = self->GetCurrentLocationForThrow();
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
     } else {
       return field->Get32(obj);
     }
   }
-  return 0;  // Will throw exception by checking with Thread::Current
+  return 0;  // Will throw exception by checking with Thread::Current.
 }
 
 extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
-                                             mirror::ArtMethod* referrer, Thread* self,
-                                             StackReference<mirror::ArtMethod>* sp)
+                                             mirror::ArtMethod* referrer, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
                                           sizeof(int64_t));
-  if (LIKELY(field != NULL && obj != NULL)) {
+  if (LIKELY(field != nullptr && obj != nullptr)) {
     return field->Get64(obj);
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
                                                          sizeof(int64_t));
-  if (LIKELY(field != NULL)) {
-    if (UNLIKELY(obj == NULL)) {
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
       ThrowLocation throw_location = self->GetCurrentLocationForThrow();
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
     } else {
       return field->Get64(obj);
     }
   }
-  return 0;  // Will throw exception by checking with Thread::Current
+  return 0;  // Will throw exception by checking with Thread::Current.
 }
 
 extern "C" mirror::Object* artGetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
                                                      mirror::ArtMethod* referrer,
-                                                     Thread* self,
-                                                     StackReference<mirror::ArtMethod>* sp)
+                                                     Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectRead,
                                           sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL && obj != NULL)) {
+  if (LIKELY(field != nullptr && obj != nullptr)) {
     return field->GetObj(obj);
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<InstanceObjectRead, true>(field_idx, referrer, self,
                                                       sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    if (UNLIKELY(obj == NULL)) {
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
       ThrowLocation throw_location = self->GetCurrentLocationForThrow();
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
     } else {
       return field->GetObj(obj);
     }
   }
-  return NULL;  // Will throw exception by checking with Thread::Current
+  return nullptr;  // 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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
+                                          sizeof(int8_t));
+  if (LIKELY(field != nullptr)) {
+    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
+  }
+  field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int8_t));
+  if (LIKELY(field != nullptr)) {
+    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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
+                                          sizeof(int16_t));
+  if (LIKELY(field != nullptr)) {
+    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
+  }
+  field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int16_t));
+  if (LIKELY(field != nullptr)) {
+    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)
+                                      mirror::ArtMethod* referrer, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
                                           sizeof(int32_t));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     // Compiled code can't use transactional mode.
     field->Set32<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int32_t));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     // Compiled code can't use transactional mode.
     field->Set32<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
@@ -170,19 +381,18 @@
 }
 
 extern "C" int artSet64StaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer,
-                                      uint64_t new_value, Thread* self,
-                                      StackReference<mirror::ArtMethod>* sp)
+                                      uint64_t new_value, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
                                           sizeof(int64_t));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     // Compiled code can't use transactional mode.
     field->Set64<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int64_t));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     // Compiled code can't use transactional mode.
     field->Set64<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
@@ -191,22 +401,21 @@
 }
 
 extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_value,
-                                       mirror::ArtMethod* referrer, Thread* self,
-                                       StackReference<mirror::ArtMethod>* sp)
+                                       mirror::ArtMethod* referrer, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectWrite,
                                           sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     if (LIKELY(!field->IsPrimitiveType())) {
       // Compiled code can't use transactional mode.
       field->SetObj<false>(field->GetDeclaringClass(), new_value);
       return 0;  // success
     }
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, self,
                                                      sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
+  if (LIKELY(field != nullptr)) {
     // Compiled code can't use transactional mode.
     field->SetObj<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
@@ -214,26 +423,108 @@
   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)
+extern "C" int artSet8InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint8_t new_value,
+                                       mirror::ArtMethod* referrer, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
+                                          sizeof(int8_t));
+  if (LIKELY(field != nullptr && obj != nullptr)) {
+    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
+  }
+  {
+    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 != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
+      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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
+                                          sizeof(int16_t));
+  if (LIKELY(field != nullptr && obj != nullptr)) {
+    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
+  }
+  {
+    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 != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
+      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)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
                                           sizeof(int32_t));
-  if (LIKELY(field != NULL && obj != NULL)) {
+  if (LIKELY(field != nullptr && obj != nullptr)) {
     // Compiled code can't use transactional mode.
     field->Set32<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(int32_t));
   }
-  if (LIKELY(field != NULL)) {
-    if (UNLIKELY(obj == NULL)) {
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
       ThrowLocation throw_location = self->GetCurrentLocationForThrow();
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
     } else {
@@ -246,25 +537,20 @@
 }
 
 extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint64_t new_value,
-                                        Thread* self, StackReference<mirror::ArtMethod>* sp)
+                                        mirror::ArtMethod* referrer, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  constexpr size_t frame_size = GetCalleeSaveFrameSize(kRuntimeISA, Runtime::kRefsOnly);
-  mirror::ArtMethod* referrer =
-      reinterpret_cast<StackReference<mirror::ArtMethod>*>(
-          reinterpret_cast<uint8_t*>(sp) + frame_size)->AsMirrorPtr();
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
                                           sizeof(int64_t));
-  if (LIKELY(field != NULL  && obj != NULL)) {
+  if (LIKELY(field != nullptr  && obj != nullptr)) {
     // Compiled code can't use transactional mode.
     field->Set64<false>(obj, new_value);
     return 0;  // success
   }
-  sp->Assign(Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsOnly));
-  self->SetTopOfStack(sp, 0);
   field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
                                                           sizeof(int64_t));
-  if (LIKELY(field != NULL)) {
-    if (UNLIKELY(obj == NULL)) {
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
       ThrowLocation throw_location = self->GetCurrentLocationForThrow();
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
     } else {
@@ -278,21 +564,20 @@
 
 extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
                                          mirror::Object* new_value,
-                                         mirror::ArtMethod* referrer, Thread* self,
-                                         StackReference<mirror::ArtMethod>* sp)
+                                         mirror::ArtMethod* referrer, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite,
                                           sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL && obj != NULL)) {
+  if (LIKELY(field != nullptr && obj != nullptr)) {
     // Compiled code can't use transactional mode.
     field->SetObj<false>(obj, new_value);
     return 0;  // success
   }
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, self,
                                                        sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    if (UNLIKELY(obj == NULL)) {
+  if (LIKELY(field != nullptr)) {
+    if (UNLIKELY(obj == nullptr)) {
       ThrowLocation throw_location = self->GetCurrentLocationForThrow();
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
     } else {
diff --git a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc
index 4ec2879..e336543 100644
--- a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc
@@ -15,49 +15,24 @@
  */
 
 #include "callee_save_frame.h"
-#include "common_throws.h"
-#include "dex_instruction.h"
 #include "mirror/array.h"
-#include "mirror/object-inl.h"
+#include "mirror/art_method-inl.h"
+#include "entrypoints/entrypoint_utils.h"
 
 namespace art {
 
 /*
- * Fill the array with predefined constant values, throwing exceptions if the array is null or
- * not of sufficient length.
- *
- * NOTE: When dealing with a raw dex file, the data to be copied uses
- * little-endian ordering.  Require that oat2dex do any required swapping
- * so this routine can get by with a memcpy().
- *
- * Format of the data:
- *  ushort ident = 0x0300   magic value
- *  ushort width            width of each element in the table
- *  uint   size             number of elements in the table
- *  ubyte  data[size*width] table of data values (may contain a single-byte
- *                          padding at the end)
+ * Handle fill array data by copying appropriate part of dex file into array.
  */
-extern "C" int artHandleFillArrayDataFromCode(mirror::Array* array,
-                                              const Instruction::ArrayDataPayload* payload,
-                                              Thread* self, StackReference<mirror::ArtMethod>* sp)
+extern "C" int artHandleFillArrayDataFromCode(uint32_t payload_offset, mirror::Array* array,
+                                              mirror::ArtMethod* method, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  DCHECK_EQ(payload->ident, static_cast<uint16_t>(Instruction::kArrayDataSignature));
-  if (UNLIKELY(array == NULL)) {
-    ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
-    return -1;  // Error
-  }
-  DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
-  if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;",
-                             "failed FILL_ARRAY_DATA; length=%d, index=%d",
-                             array->GetLength(), payload->element_count - 1);
-    return -1;  // Error
-  }
-  uint32_t size_in_bytes = payload->element_count * payload->element_width;
-  memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes);
-  return 0;  // Success
+  ScopedQuickEntrypointChecks sqec(self);
+  const uint16_t* const insns = method->GetCodeItem()->insns_;
+  const Instruction::ArrayDataPayload* payload =
+      reinterpret_cast<const Instruction::ArrayDataPayload*>(insns + payload_offset);
+  bool success = FillArrayData(array, payload);
+  return success ? 0 : -1;
 }
 
 }  // namespace art
diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
index 49df62d..f78273f 100644
--- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
@@ -15,6 +15,7 @@
  */
 
 #include "callee_save_frame.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "instruction_set.h"
 #include "instrumentation.h"
 #include "mirror/art_method-inl.h"
@@ -27,10 +28,9 @@
 extern "C" const void* artInstrumentationMethodEntryFromCode(mirror::ArtMethod* method,
                                                              mirror::Object* this_object,
                                                              Thread* self,
-                                                             StackReference<mirror::ArtMethod>* sp,
                                                              uintptr_t lr)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
+  ScopedQuickEntrypointChecks sqec(self);
   instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
   const void* result;
   if (instrumentation->IsDeoptimized(method)) {
@@ -38,8 +38,7 @@
   } else {
     result = instrumentation->GetQuickCodeFor(method);
   }
-  DCHECK((result != Runtime::Current()->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline())
-         || !Runtime::Current()->GetHeap()->HasImageSpace());
+  DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result));
   bool interpreter_entry = (result == GetQuickToInterpreterBridge());
   instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? nullptr : this_object,
                                                  method, lr, interpreter_entry);
@@ -52,23 +51,19 @@
                                                               uint64_t gpr_result,
                                                               uint64_t fpr_result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // TODO: use FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly) not the hand inlined below.
-  //       We use the hand inline version to ensure the return_pc is assigned before verifying the
-  //       stack.
-  // Be aware the store below may well stomp on an incoming argument.
-  Locks::mutator_lock_->AssertSharedHeld(self);
-  Runtime* runtime = Runtime::Current();
-  sp->Assign(runtime->GetCalleeSaveMethod(Runtime::kRefsOnly));
-  uint32_t return_pc_offset = GetCalleeSavePCOffset(kRuntimeISA, Runtime::kRefsOnly);
-  uintptr_t* return_pc = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp) +
+  // Compute address of return PC and sanity check that it currently holds 0.
+  size_t return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsOnly);
+  uintptr_t* return_pc = reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) +
                                                       return_pc_offset);
   CHECK_EQ(*return_pc, 0U);
-  self->SetTopOfStack(sp, 0);
-  self->VerifyStack();
+
+  // Pop the frame filling in the return pc. The low half of the return value is 0 when
+  // deoptimization shouldn't be performed with the high-half having the return address. When
+  // deoptimization should be performed the low half is zero and the high-half the address of the
+  // deoptimization entry point.
   instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
   TwoWordReturn return_or_deoptimize_pc = instrumentation->PopInstrumentationStackFrame(
       self, return_pc, gpr_result, fpr_result);
-  self->VerifyStack();
   return return_or_deoptimize_pc;
 }
 
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 6537249..c1276b5 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -14,15 +14,10 @@
  * 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 +51,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_lock_entrypoints.cc b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
index 92c0841..8ceac97 100644
--- a/runtime/entrypoints/quick/quick_lock_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
@@ -20,12 +20,11 @@
 
 namespace art {
 
-extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self,
-                                     StackReference<mirror::ArtMethod>* sp)
+extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
     NO_THREAD_SAFETY_ANALYSIS /* EXCLUSIVE_LOCK_FUNCTION(Monitor::monitor_lock_) */ {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  if (UNLIKELY(obj == NULL)) {
+  ScopedQuickEntrypointChecks sqec(self);
+  if (UNLIKELY(obj == nullptr)) {
     ThrowLocation throw_location(self->GetCurrentLocationForThrow());
     ThrowNullPointerException(&throw_location,
                               "Null reference used for synchronization (monitor-enter)");
@@ -43,12 +42,11 @@
   }
 }
 
-extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self,
-                                       StackReference<mirror::ArtMethod>* sp)
+extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
     NO_THREAD_SAFETY_ANALYSIS /* UNLOCK_FUNCTION(Monitor::monitor_lock_) */ {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  if (UNLIKELY(obj == NULL)) {
+  ScopedQuickEntrypointChecks sqec(self);
+  if (UNLIKELY(obj == nullptr)) {
     ThrowLocation throw_location(self->GetCurrentLocationForThrow());
     ThrowNullPointerException(&throw_location,
                               "Null reference used for synchronization (monitor-exit)");
diff --git a/runtime/entrypoints/quick/quick_math_entrypoints.cc b/runtime/entrypoints/quick/quick_math_entrypoints.cc
index 014aad3..1c658b7 100644
--- a/runtime/entrypoints/quick/quick_math_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_math_entrypoints.cc
@@ -18,6 +18,11 @@
 
 namespace art {
 
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wfloat-equal"
+#endif
+
 int CmplFloat(float a, float b) {
   if (a == b) {
     return 0;
@@ -62,6 +67,10 @@
   return -1;
 }
 
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
 extern "C" int64_t artLmul(int64_t a, int64_t b) {
   return a * b;
 }
diff --git a/runtime/entrypoints/quick/quick_thread_entrypoints.cc b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
index 118cd7f..87e0c6e 100644
--- a/runtime/entrypoints/quick/quick_thread_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
@@ -15,17 +15,14 @@
  */
 
 #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)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+extern "C" void artTestSuspendFromCode(Thread* self) 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);
+  ScopedQuickEntrypointChecks sqec(self);
+  self->CheckSuspend();
 }
 
 }  // namespace art
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 13decc8..25df40b 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -24,16 +24,14 @@
 namespace art {
 
 // Deliver an exception that's pending on thread helping set up a callee save frame on the way.
-extern "C" void artDeliverPendingExceptionFromCode(Thread* thread,
-                                                   StackReference<mirror::ArtMethod>* sp)
+extern "C" void artDeliverPendingExceptionFromCode(Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
-  thread->QuickDeliverException();
+  ScopedQuickEntrypointChecks sqec(self);
+  self->QuickDeliverException();
 }
 
 // Called by generated call to throw an exception.
-extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self,
-                                            StackReference<mirror::ArtMethod>* sp)
+extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   /*
    * exception may be NULL, in which case this routine should
@@ -42,9 +40,9 @@
    * and threw a NPE if NULL.  This routine responsible for setting
    * exception_ in thread and delivering the exception.
    */
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ScopedQuickEntrypointChecks sqec(self);
   ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-  if (exception == NULL) {
+  if (exception == nullptr) {
     self->ThrowNewException(throw_location, "Ljava/lang/NullPointerException;",
                             "throw with null exception");
   } else {
@@ -54,10 +52,9 @@
 }
 
 // Called by generated call to throw a NPE exception.
-extern "C" void artThrowNullPointerExceptionFromCode(Thread* self,
-                                                     StackReference<mirror::ArtMethod>* sp)
+extern "C" void artThrowNullPointerExceptionFromCode(Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ScopedQuickEntrypointChecks sqec(self);
   self->NoteSignalBeingHandled();
   ThrowLocation throw_location = self->GetCurrentLocationForThrow();
   ThrowNullPointerExceptionFromDexPC(throw_location);
@@ -66,52 +63,50 @@
 }
 
 // Called by generated call to throw an arithmetic divide by zero exception.
-extern "C" void artThrowDivZeroFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp)
+extern "C" void artThrowDivZeroFromCode(Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ScopedQuickEntrypointChecks sqec(self);
   ThrowArithmeticExceptionDivideByZero();
   self->QuickDeliverException();
 }
 
 // Called by generated call to throw an array index out of bounds exception.
-extern "C" void artThrowArrayBoundsFromCode(int index, int length, Thread* self,
-                                            StackReference<mirror::ArtMethod>*sp)
+extern "C" void artThrowArrayBoundsFromCode(int index, int length, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ScopedQuickEntrypointChecks sqec(self);
   ThrowArrayIndexOutOfBoundsException(index, length);
   self->QuickDeliverException();
 }
 
-extern "C" void artThrowStackOverflowFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp)
+extern "C" void artThrowStackOverflowFromCode(Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ScopedQuickEntrypointChecks sqec(self);
   self->NoteSignalBeingHandled();
   ThrowStackOverflowError(self);
   self->NoteSignalHandlerDone();
   self->QuickDeliverException();
 }
 
-extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self,
-                                             StackReference<mirror::ArtMethod>* sp)
+extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ScopedQuickEntrypointChecks sqec(self);
   ThrowNoSuchMethodError(method_idx);
   self->QuickDeliverException();
 }
 
 extern "C" void artThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type,
-                                           Thread* self, StackReference<mirror::ArtMethod>* sp)
+                                           Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
-  CHECK(!dest_type->IsAssignableFrom(src_type));
+  ScopedQuickEntrypointChecks sqec(self);
+  DCHECK(!dest_type->IsAssignableFrom(src_type));
   ThrowClassCastException(dest_type, src_type);
   self->QuickDeliverException();
 }
 
 extern "C" void artThrowArrayStoreException(mirror::Object* array, mirror::Object* value,
-                                            Thread* self, StackReference<mirror::ArtMethod>* sp)
+                                            Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  ScopedQuickEntrypointChecks sqec(self);
   ThrowArrayStoreException(value->GetClass(), array->GetClass());
   self->QuickDeliverException();
 }
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index dfd2e11..4f61707 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -19,6 +19,7 @@
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
 #include "entrypoints/entrypoint_utils-inl.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc/accounting/card_table-inl.h"
 #include "instruction_set.h"
 #include "interpreter/interpreter.h"
@@ -49,15 +50,19 @@
   // | arg1 spill |  |
   // | Method*    | ---
   // | LR         |
-  // | ...        |    callee saves
-  // | R3         |    arg3
-  // | R2         |    arg2
-  // | R1         |    arg1
-  // | R0         |    padding
+  // | ...        |    4x6 bytes callee saves
+  // | R3         |
+  // | R2         |
+  // | R1         |
+  // | S15        |
+  // | :          |
+  // | S0         |
+  // |            |    4x2 bytes padding
   // | Method*    |  <- sp
-  static constexpr bool kQuickSoftFloatAbi = true;  // This is a soft float ABI.
-  static constexpr size_t kNumQuickGprArgs = 3;  // 3 arguments passed in GPRs.
-  static constexpr size_t kNumQuickFprArgs = 0;  // 0 arguments passed in FPRs.
+  static constexpr bool kQuickSoftFloatAbi = kArm32QuickCodeUseSoftFloat;
+  static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = !kArm32QuickCodeUseSoftFloat;
+  static constexpr size_t kNumQuickGprArgs = 3;
+  static constexpr size_t kNumQuickFprArgs = kArm32QuickCodeUseSoftFloat ? 0 : 16;
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =
       arm::ArmCalleeSaveFpr1Offset(Runtime::kRefsAndArgs);  // Offset of first FPR arg.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset =
@@ -89,6 +94,7 @@
   // |            |    padding
   // | Method*    |  <- sp
   static constexpr bool kQuickSoftFloatAbi = false;  // This is a hard float ABI.
+  static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
   static constexpr size_t kNumQuickGprArgs = 7;  // 7 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 8;  // 8 arguments passed in FPRs.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =
@@ -116,6 +122,7 @@
   // | A1         |    arg1
   // | A0/Method* |  <- sp
   static constexpr bool kQuickSoftFloatAbi = true;  // This is a soft float ABI.
+  static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
   static constexpr size_t kNumQuickGprArgs = 3;  // 3 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 0;  // 0 arguments passed in FPRs.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0;  // Offset of first FPR arg.
@@ -140,6 +147,7 @@
   // | ECX         |    arg1
   // | EAX/Method* |  <- sp
   static constexpr bool kQuickSoftFloatAbi = true;  // This is a soft float ABI.
+  static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
   static constexpr size_t kNumQuickGprArgs = 3;  // 3 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 0;  // 0 arguments passed in FPRs.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0;  // Offset of first FPR arg.
@@ -177,6 +185,7 @@
   // | Padding         |
   // | RDI/Method*     |  <- sp
   static constexpr bool kQuickSoftFloatAbi = false;  // This is a hard float ABI.
+  static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
   static constexpr size_t kNumQuickGprArgs = 5;  // 5 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 8;  // 8 arguments passed in FPRs.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16;  // Offset of first FPR arg.
@@ -202,7 +211,7 @@
   static mirror::ArtMethod* GetCallingMethod(StackReference<mirror::ArtMethod>* sp)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod());
-    byte* previous_sp = reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize;
+    uint8_t* previous_sp = reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize;
     return reinterpret_cast<StackReference<mirror::ArtMethod>*>(previous_sp)->AsMirrorPtr();
   }
 
@@ -210,19 +219,28 @@
   static uintptr_t GetCallingPc(StackReference<mirror::ArtMethod>* sp)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod());
-    byte* lr = reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_LrOffset;
+    uint8_t* lr = reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_LrOffset;
     return *reinterpret_cast<uintptr_t*>(lr);
   }
 
   QuickArgumentVisitor(StackReference<mirror::ArtMethod>* sp, bool is_static, const char* shorty,
                        uint32_t shorty_len) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
           is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len),
-          gpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset),
-          fpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset),
-          stack_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize
-                      + StackArgumentStartFromShorty(is_static, shorty, shorty_len)),
-          gpr_index_(0), fpr_index_(0), stack_index_(0), cur_type_(Primitive::kPrimVoid),
-          is_split_long_or_double_(false) {}
+          gpr_args_(reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset),
+          fpr_args_(reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset),
+          stack_args_(reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize
+              + sizeof(StackReference<mirror::ArtMethod>)),  // Skip StackReference<ArtMethod>.
+          gpr_index_(0), fpr_index_(0), fpr_double_index_(0), stack_index_(0),
+          cur_type_(Primitive::kPrimVoid), is_split_long_or_double_(false) {
+    static_assert(kQuickSoftFloatAbi == (kNumQuickFprArgs == 0),
+                  "Number of Quick FPR arguments unexpected");
+    static_assert(!(kQuickSoftFloatAbi && kQuickDoubleRegAlignedFloatBackFilled),
+                  "Double alignment unexpected");
+    // For register alignment, we want to assume that counters(fpr_double_index_) are even if the
+    // next register is even.
+    static_assert(!kQuickDoubleRegAlignedFloatBackFilled || kNumQuickFprArgs % 2 == 0,
+                  "Number of Quick FPR arguments not even");
+  }
 
   virtual ~QuickArgumentVisitor() {}
 
@@ -232,11 +250,15 @@
     return cur_type_;
   }
 
-  byte* GetParamAddress() const {
+  uint8_t* GetParamAddress() const {
     if (!kQuickSoftFloatAbi) {
       Primitive::Type type = GetParamPrimitiveType();
       if (UNLIKELY((type == Primitive::kPrimDouble) || (type == Primitive::kPrimFloat))) {
-        if ((kNumQuickFprArgs != 0) && (fpr_index_ + 1 < kNumQuickFprArgs + 1)) {
+        if (type == Primitive::kPrimDouble && kQuickDoubleRegAlignedFloatBackFilled) {
+          if (fpr_double_index_ + 2 < kNumQuickFprArgs + 1) {
+            return fpr_args_ + (fpr_double_index_ * GetBytesPerFprSpillLocation(kRuntimeISA));
+          }
+        } else if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
           return fpr_args_ + (fpr_index_ * GetBytesPerFprSpillLocation(kRuntimeISA));
         }
         return stack_args_ + (stack_index_ * kBytesStackArgLocation);
@@ -267,28 +289,30 @@
 
   uint64_t ReadSplitLongParam() const {
     DCHECK(IsSplitLongOrDouble());
+    // Read low half from register.
     uint64_t low_half = *reinterpret_cast<uint32_t*>(GetParamAddress());
-    uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args_);
+    // Read high half from the stack. As current stack_index_ indexes the argument, the high part
+    // index should be (stack_index_ + 1).
+    uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args_
+        + (stack_index_ + 1) * kBytesStackArgLocation);
     return (low_half & 0xffffffffULL) | (high_half << 32);
   }
 
   void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // This implementation doesn't support reg-spill area for hard float
-    // ABI targets such as x86_64 and aarch64. So, for those targets whose
-    // 'kQuickSoftFloatAbi' is 'false':
-    //     (a) 'stack_args_' should point to the first method's argument
-    //     (b) whatever the argument type it is, the 'stack_index_' should
-    //         be moved forward along with every visiting.
+    // (a) 'stack_args_' should point to the first method's argument
+    // (b) whatever the argument type it is, the 'stack_index_' should
+    //     be moved forward along with every visiting.
     gpr_index_ = 0;
     fpr_index_ = 0;
+    if (kQuickDoubleRegAlignedFloatBackFilled) {
+      fpr_double_index_ = 0;
+    }
     stack_index_ = 0;
     if (!is_static_) {  // Handle this.
       cur_type_ = Primitive::kPrimNot;
       is_split_long_or_double_ = false;
       Visit();
-      if (!kQuickSoftFloatAbi || kNumQuickGprArgs == 0) {
-        stack_index_++;
-      }
+      stack_index_++;
       if (kNumQuickGprArgs > 0) {
         gpr_index_++;
       }
@@ -304,9 +328,7 @@
         case Primitive::kPrimInt:
           is_split_long_or_double_ = false;
           Visit();
-          if (!kQuickSoftFloatAbi || kNumQuickGprArgs == gpr_index_) {
-            stack_index_++;
-          }
+          stack_index_++;
           if (gpr_index_ < kNumQuickGprArgs) {
             gpr_index_++;
           }
@@ -314,17 +336,24 @@
         case Primitive::kPrimFloat:
           is_split_long_or_double_ = false;
           Visit();
+          stack_index_++;
           if (kQuickSoftFloatAbi) {
             if (gpr_index_ < kNumQuickGprArgs) {
               gpr_index_++;
-            } else {
-              stack_index_++;
             }
           } else {
-            if ((kNumQuickFprArgs != 0) && (fpr_index_ + 1 < kNumQuickFprArgs + 1)) {
+            if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
               fpr_index_++;
+              if (kQuickDoubleRegAlignedFloatBackFilled) {
+                // Double should not overlap with float.
+                // For example, if fpr_index_ = 3, fpr_double_index_ should be at least 4.
+                fpr_double_index_ = std::max(fpr_double_index_, RoundUp(fpr_index_, 2));
+                // Float should not overlap with double.
+                if (fpr_index_ % 2 == 0) {
+                  fpr_index_ = std::max(fpr_double_index_, fpr_index_);
+                }
+              }
             }
-            stack_index_++;
           }
           break;
         case Primitive::kPrimDouble:
@@ -333,42 +362,46 @@
             is_split_long_or_double_ = (GetBytesPerGprSpillLocation(kRuntimeISA) == 4) &&
                 ((gpr_index_ + 1) == kNumQuickGprArgs);
             Visit();
-            if (!kQuickSoftFloatAbi || kNumQuickGprArgs == gpr_index_) {
-              if (kBytesStackArgLocation == 4) {
-                stack_index_+= 2;
-              } else {
-                CHECK_EQ(kBytesStackArgLocation, 8U);
-                stack_index_++;
-              }
+            if (kBytesStackArgLocation == 4) {
+              stack_index_+= 2;
+            } else {
+              CHECK_EQ(kBytesStackArgLocation, 8U);
+              stack_index_++;
             }
             if (gpr_index_ < kNumQuickGprArgs) {
               gpr_index_++;
               if (GetBytesPerGprSpillLocation(kRuntimeISA) == 4) {
                 if (gpr_index_ < kNumQuickGprArgs) {
                   gpr_index_++;
-                } else if (kQuickSoftFloatAbi) {
-                  stack_index_++;
                 }
               }
             }
           } else {
             is_split_long_or_double_ = (GetBytesPerFprSpillLocation(kRuntimeISA) == 4) &&
-                ((fpr_index_ + 1) == kNumQuickFprArgs);
+                ((fpr_index_ + 1) == kNumQuickFprArgs) && !kQuickDoubleRegAlignedFloatBackFilled;
             Visit();
-            if ((kNumQuickFprArgs != 0) && (fpr_index_ + 1 < kNumQuickFprArgs + 1)) {
-              fpr_index_++;
-              if (GetBytesPerFprSpillLocation(kRuntimeISA) == 4) {
-                if ((kNumQuickFprArgs != 0) && (fpr_index_ + 1 < kNumQuickFprArgs + 1)) {
-                  fpr_index_++;
-                }
-              }
-            }
             if (kBytesStackArgLocation == 4) {
               stack_index_+= 2;
             } else {
               CHECK_EQ(kBytesStackArgLocation, 8U);
               stack_index_++;
             }
+            if (kQuickDoubleRegAlignedFloatBackFilled) {
+              if (fpr_double_index_ + 2 < kNumQuickFprArgs + 1) {
+                fpr_double_index_ += 2;
+                // Float should not overlap with double.
+                if (fpr_index_ % 2 == 0) {
+                  fpr_index_ = std::max(fpr_double_index_, fpr_index_);
+                }
+              }
+            } else if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
+              fpr_index_++;
+              if (GetBytesPerFprSpillLocation(kRuntimeISA) == 4) {
+                if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
+                  fpr_index_++;
+                }
+              }
+            }
           }
           break;
         default:
@@ -377,32 +410,24 @@
     }
   }
 
- private:
-  static size_t StackArgumentStartFromShorty(bool is_static, const char* shorty,
-                                             uint32_t shorty_len) {
-    if (kQuickSoftFloatAbi) {
-      CHECK_EQ(kNumQuickFprArgs, 0U);
-      return (kNumQuickGprArgs * GetBytesPerGprSpillLocation(kRuntimeISA))
-          + sizeof(StackReference<mirror::ArtMethod>) /* StackReference<ArtMethod> */;
-    } else {
-      // For now, there is no reg-spill area for the targets with
-      // hard float ABI. So, the offset pointing to the first method's
-      // parameter ('this' for non-static methods) should be returned.
-      return sizeof(StackReference<mirror::ArtMethod>);  // Skip StackReference<ArtMethod>.
-    }
-  }
-
  protected:
   const bool is_static_;
   const char* const shorty_;
   const uint32_t shorty_len_;
 
  private:
-  byte* const gpr_args_;  // Address of GPR arguments in callee save frame.
-  byte* const fpr_args_;  // Address of FPR arguments in callee save frame.
-  byte* const stack_args_;  // Address of stack arguments in caller's frame.
+  uint8_t* const gpr_args_;  // Address of GPR arguments in callee save frame.
+  uint8_t* const fpr_args_;  // Address of FPR arguments in callee save frame.
+  uint8_t* const stack_args_;  // Address of stack arguments in caller's frame.
   uint32_t gpr_index_;  // Index into spilled GPRs.
-  uint32_t fpr_index_;  // Index into spilled FPRs.
+  // Index into spilled FPRs.
+  // In case kQuickDoubleRegAlignedFloatBackFilled, it may index a hole while fpr_double_index_
+  // holds a higher register number.
+  uint32_t fpr_index_;
+  // Index into spilled FPRs for aligned double.
+  // Only used when kQuickDoubleRegAlignedFloatBackFilled. Next available double register indexed in
+  // terms of singles, may be behind fpr_index.
+  uint32_t fpr_double_index_;
   uint32_t stack_index_;  // Index into arguments on the stack.
   // The current type of argument during VisitArguments.
   Primitive::Type cur_type_;
@@ -455,7 +480,7 @@
       break;
     case Primitive::kPrimVoid:
       LOG(FATAL) << "UNREACHABLE";
-      break;
+      UNREACHABLE();
   }
   ++cur_reg_;
 }
@@ -465,7 +490,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Ensure we don't get thread suspension until the object arguments are safely in the shadow
   // frame.
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
+  ScopedQuickEntrypointChecks sqec(self);
 
   if (method->IsAbstract()) {
     ThrowAbstractMethodError(method);
@@ -492,20 +517,19 @@
     self->PushShadowFrame(shadow_frame);
     self->EndAssertNoThreadSuspension(old_cause);
 
-    if (method->IsStatic() && !method->GetDeclaringClass()->IsInitialized()) {
+    StackHandleScope<1> hs(self);
+    MethodHelper mh(hs.NewHandle(method));
+    if (mh.Get()->IsStatic() && !mh.Get()->GetDeclaringClass()->IsInitialized()) {
       // 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)) {
-        DCHECK(Thread::Current()->IsExceptionPending()) << PrettyMethod(method);
+      StackHandleScope<1> hs2(self);
+      Handle<mirror::Class> h_class(hs2.NewHandle(mh.Get()->GetDeclaringClass()));
+      if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
+        DCHECK(Thread::Current()->IsExceptionPending()) << PrettyMethod(mh.Get());
         self->PopManagedStackFragment(fragment);
         return 0;
       }
     }
-
-    StackHandleScope<1> hs(self);
-    MethodHelper mh(hs.NewHandle(method));
-    JValue result = interpreter::EnterInterpreterFromStub(self, mh, code_item, *shadow_frame);
+    JValue result = interpreter::EnterInterpreterFromEntryPoint(self, &mh, code_item, shadow_frame);
     // Pop transition.
     self->PopManagedStackFragment(fragment);
     // No need to restore the args since the method has already been run by the interpreter.
@@ -564,8 +588,7 @@
       break;
     case Primitive::kPrimVoid:
       LOG(FATAL) << "UNREACHABLE";
-      val.j = 0;
-      break;
+      UNREACHABLE();
   }
   args_->push_back(val);
 }
@@ -593,7 +616,6 @@
       self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
   // Register the top of the managed stack, making stack crawlable.
   DCHECK_EQ(sp->AsMirrorPtr(), proxy_method) << PrettyMethod(proxy_method);
-  self->SetTopOfStack(sp, 0);
   DCHECK_EQ(proxy_method->GetFrameSizeInBytes(),
             Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes())
       << PrettyMethod(proxy_method);
@@ -678,7 +700,7 @@
                                                     Thread* self,
                                                     StackReference<mirror::ArtMethod>* sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
+  ScopedQuickEntrypointChecks sqec(self);
   // Start new JNI local reference state
   JNIEnvExt* env = self->GetJniEnv();
   ScopedObjectAccessUnchecked soa(env);
@@ -808,7 +830,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()) {
@@ -944,13 +966,13 @@
         delegate_(delegate) {
     // For register alignment, we want to assume that counters (gpr_index_, fpr_index_) are even iff
     // the next register is even; counting down is just to make the compiler happy...
-    CHECK_EQ(kNumNativeGprArgs % 2, 0U);
-    CHECK_EQ(kNumNativeFprArgs % 2, 0U);
+    static_assert(kNumNativeGprArgs % 2 == 0U, "Number of native GPR arguments not even");
+    static_assert(kNumNativeFprArgs % 2 == 0U, "Number of native FPR arguments not even");
   }
 
   virtual ~BuildNativeCallFrameStateMachine() {}
 
-  bool HavePointerGpr() {
+  bool HavePointerGpr() const {
     return gpr_index_ > 0;
   }
 
@@ -965,7 +987,7 @@
     }
   }
 
-  bool HaveHandleScopeGpr() {
+  bool HaveHandleScopeGpr() const {
     return gpr_index_ > 0;
   }
 
@@ -981,7 +1003,7 @@
     }
   }
 
-  bool HaveIntGpr() {
+  bool HaveIntGpr() const {
     return gpr_index_ > 0;
   }
 
@@ -996,17 +1018,17 @@
     }
   }
 
-  bool HaveLongGpr() {
+  bool HaveLongGpr() const {
     return gpr_index_ >= kRegistersNeededForLong + (LongGprNeedsPadding() ? 1 : 0);
   }
 
-  bool LongGprNeedsPadding() {
+  bool LongGprNeedsPadding() const {
     return kRegistersNeededForLong > 1 &&     // only pad when using multiple registers
         kAlignLongOnStack &&                  // and when it needs alignment
         (gpr_index_ & 1) == 1;                // counter is odd, see constructor
   }
 
-  bool LongStackNeedsPadding() {
+  bool LongStackNeedsPadding() const {
     return kRegistersNeededForLong > 1 &&     // only pad when using multiple registers
         kAlignLongOnStack &&                  // and when it needs 8B alignment
         (stack_entries_ & 1) == 1;            // counter is odd
@@ -1042,7 +1064,7 @@
     }
   }
 
-  bool HaveFloatFpr() {
+  bool HaveFloatFpr() const {
     return fpr_index_ > 0;
   }
 
@@ -1077,17 +1099,17 @@
     }
   }
 
-  bool HaveDoubleFpr() {
+  bool HaveDoubleFpr() const {
     return fpr_index_ >= kRegistersNeededForDouble + (DoubleFprNeedsPadding() ? 1 : 0);
   }
 
-  bool DoubleFprNeedsPadding() {
+  bool DoubleFprNeedsPadding() const {
     return kRegistersNeededForDouble > 1 &&     // only pad when using multiple registers
         kAlignDoubleOnStack &&                  // and when it needs alignment
         (fpr_index_ & 1) == 1;                  // counter is odd, see constructor
   }
 
-  bool DoubleStackNeedsPadding() {
+  bool DoubleStackNeedsPadding() const {
     return kRegistersNeededForDouble > 1 &&     // only pad when using multiple registers
         kAlignDoubleOnStack &&                  // and when it needs 8B alignment
         (stack_entries_ & 1) == 1;              // counter is odd
@@ -1122,15 +1144,15 @@
     }
   }
 
-  uint32_t getStackEntries() {
+  uint32_t GetStackEntries() const {
     return stack_entries_;
   }
 
-  uint32_t getNumberOfUsedGprs() {
+  uint32_t GetNumberOfUsedGprs() const {
     return kNumNativeGprArgs - gpr_index_;
   }
 
-  uint32_t getNumberOfUsedFprs() {
+  uint32_t GetNumberOfUsedFprs() const {
     return kNumNativeFprArgs - fpr_index_;
   }
 
@@ -1155,7 +1177,7 @@
   uint32_t fpr_index_;      // Number of free FPRs
   uint32_t stack_entries_;  // Stack entries are in multiples of 32b, as floats are usually not
                             // extended
-  T* delegate_;             // What Push implementation gets called
+  T* const delegate_;             // What Push implementation gets called
 };
 
 // Computes the sizes of register stacks and call stack area. Handling of references can be extended
@@ -1169,18 +1191,19 @@
 
   virtual ~ComputeNativeCallFrameSize() {}
 
-  uint32_t GetStackSize() {
+  uint32_t GetStackSize() const {
     return num_stack_entries_ * sizeof(uintptr_t);
   }
 
-  uint8_t* LayoutCallStack(uint8_t* sp8) {
+  uint8_t* LayoutCallStack(uint8_t* sp8) const {
     sp8 -= GetStackSize();
     // Align by kStackAlignment.
     sp8 = reinterpret_cast<uint8_t*>(RoundDown(reinterpret_cast<uintptr_t>(sp8), kStackAlignment));
     return sp8;
   }
 
-  uint8_t* LayoutCallRegisterStacks(uint8_t* sp8, uintptr_t** start_gpr, uint32_t** start_fpr) {
+  uint8_t* LayoutCallRegisterStacks(uint8_t* sp8, uintptr_t** start_gpr, uint32_t** start_fpr)
+      const {
     // Assumption is OK right now, as we have soft-float arm
     size_t fregs = BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize>::kNumNativeFprArgs;
     sp8 -= fregs * sizeof(uintptr_t);
@@ -1192,7 +1215,7 @@
   }
 
   uint8_t* LayoutNativeCall(uint8_t* sp8, uintptr_t** start_stack, uintptr_t** start_gpr,
-                            uint32_t** start_fpr) {
+                            uint32_t** start_fpr) const {
     // Native call stack.
     sp8 = LayoutCallStack(sp8);
     *start_stack = reinterpret_cast<uintptr_t*>(sp8);
@@ -1205,7 +1228,9 @@
   }
 
   virtual void WalkHeader(BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize>* sm)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {}
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    UNUSED(sm);
+  }
 
   void Walk(const char* shorty, uint32_t shorty_len) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize> sm(this);
@@ -1216,6 +1241,7 @@
       Primitive::Type cur_type_ = Primitive::GetType(shorty[i]);
       switch (cur_type_) {
         case Primitive::kPrimNot:
+          // TODO: fix abuse of mirror types.
           sm.AdvanceHandleScope(
               reinterpret_cast<mirror::Object*>(0x12345678));
           break;
@@ -1241,7 +1267,7 @@
       }
     }
 
-    num_stack_entries_ = sm.getStackEntries();
+    num_stack_entries_ = sm.GetStackEntries();
   }
 
   void PushGpr(uintptr_t /* val */) {
@@ -1276,8 +1302,8 @@
   // is at *m = sp. Will update to point to the bottom of the save frame.
   //
   // Note: assumes ComputeAll() has been run before.
-  void LayoutCalleeSaveFrame(StackReference<mirror::ArtMethod>** m, void* sp, HandleScope** table,
-                             uint32_t* handle_scope_entries)
+  void LayoutCalleeSaveFrame(Thread* self, StackReference<mirror::ArtMethod>** m, void* sp,
+                             HandleScope** handle_scope)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     mirror::ArtMethod* method = (*m)->AsMirrorPtr();
 
@@ -1287,11 +1313,9 @@
     // We have to squeeze in the HandleScope, and relocate the method pointer.
 
     // "Free" the slot for the method.
-    sp8 += kPointerSize;  // In the callee-save frame we use a full pointer.
+    sp8 += sizeof(void*);  // In the callee-save frame we use a full pointer.
 
     // Under the callee saves put handle scope and new method stack reference.
-    *handle_scope_entries = num_handle_scope_references_;
-
     size_t handle_scope_size = HandleScope::SizeOf(num_handle_scope_references_);
     size_t scope_and_method = handle_scope_size + sizeof(StackReference<mirror::ArtMethod>);
 
@@ -1301,8 +1325,8 @@
         reinterpret_cast<uintptr_t>(sp8), kStackAlignment));
 
     uint8_t* sp8_table = sp8 + sizeof(StackReference<mirror::ArtMethod>);
-    *table = reinterpret_cast<HandleScope*>(sp8_table);
-    (*table)->SetNumberOfReferences(num_handle_scope_references_);
+    *handle_scope = HandleScope::Create(sp8_table, self->GetTopHandleScope(),
+                                        num_handle_scope_references_);
 
     // Add a slot for the method pointer, and fill it. Fix the pointer-pointer given to us.
     uint8_t* method_pointer = sp8;
@@ -1313,19 +1337,19 @@
   }
 
   // Adds space for the cookie. Note: may leave stack unaligned.
-  void LayoutCookie(uint8_t** sp) {
+  void LayoutCookie(uint8_t** sp) const {
     // Reference cookie and padding
     *sp -= 8;
   }
 
   // Re-layout the callee-save frame (insert a handle-scope). Then add space for the cookie.
   // Returns the new bottom. Note: this may be unaligned.
-  uint8_t* LayoutJNISaveFrame(StackReference<mirror::ArtMethod>** m, void* sp, HandleScope** table,
-                              uint32_t* handle_scope_entries)
+  uint8_t* LayoutJNISaveFrame(Thread* self, StackReference<mirror::ArtMethod>** m, void* sp,
+                              HandleScope** handle_scope)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // First, fix up the layout of the callee-save frame.
     // We have to squeeze in the HandleScope, and relocate the method pointer.
-    LayoutCalleeSaveFrame(m, sp, table, handle_scope_entries);
+    LayoutCalleeSaveFrame(self, m, sp, handle_scope);
 
     // The bottom of the callee-save frame is now where the method is, *m.
     uint8_t* sp8 = reinterpret_cast<uint8_t*>(*m);
@@ -1337,14 +1361,14 @@
   }
 
   // WARNING: After this, *sp won't be pointing to the method anymore!
-  uint8_t* ComputeLayout(StackReference<mirror::ArtMethod>** m, bool is_static, const char* shorty,
-                         uint32_t shorty_len, HandleScope** table, uint32_t* handle_scope_entries,
+  uint8_t* ComputeLayout(Thread* self, StackReference<mirror::ArtMethod>** m,
+                         const char* shorty, uint32_t shorty_len, HandleScope** handle_scope,
                          uintptr_t** start_stack, uintptr_t** start_gpr, uint32_t** start_fpr)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     Walk(shorty, shorty_len);
 
     // JNI part.
-    uint8_t* sp8 = LayoutJNISaveFrame(m, reinterpret_cast<void*>(*m), table, handle_scope_entries);
+    uint8_t* sp8 = LayoutJNISaveFrame(self, m, reinterpret_cast<void*>(*m), handle_scope);
 
     sp8 = LayoutNativeCall(sp8, start_stack, start_gpr, start_fpr);
 
@@ -1412,9 +1436,9 @@
     cur_stack_arg_++;
   }
 
-  virtual uintptr_t PushHandle(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  virtual uintptr_t PushHandle(mirror::Object*) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     LOG(FATAL) << "(Non-JNI) Native call does not use handles.";
-    return 0U;
+    UNREACHABLE();
   }
 
  private:
@@ -1427,20 +1451,19 @@
 // of transitioning into native code.
 class BuildGenericJniFrameVisitor FINAL : public QuickArgumentVisitor {
  public:
-  BuildGenericJniFrameVisitor(StackReference<mirror::ArtMethod>** sp, bool is_static,
-                              const char* shorty, uint32_t shorty_len, Thread* self)
+  BuildGenericJniFrameVisitor(Thread* self, bool is_static, const char* shorty, uint32_t shorty_len,
+                              StackReference<mirror::ArtMethod>** sp)
      : QuickArgumentVisitor(*sp, is_static, shorty, shorty_len),
        jni_call_(nullptr, nullptr, nullptr, nullptr), sm_(&jni_call_) {
     ComputeGenericJniFrameSize fsc;
     uintptr_t* start_gpr_reg;
     uint32_t* start_fpr_reg;
     uintptr_t* start_stack_arg;
-    uint32_t handle_scope_entries;
-    bottom_of_used_area_ = fsc.ComputeLayout(sp, is_static, shorty, shorty_len, &handle_scope_,
-                                             &handle_scope_entries, &start_stack_arg,
+    bottom_of_used_area_ = fsc.ComputeLayout(self, sp, shorty, shorty_len,
+                                             &handle_scope_,
+                                             &start_stack_arg,
                                              &start_gpr_reg, &start_fpr_reg);
 
-    handle_scope_->SetNumberOfReferences(handle_scope_entries);
     jni_call_.Reset(start_gpr_reg, start_fpr_reg, start_stack_arg, handle_scope_);
 
     // jni environment is always first argument
@@ -1460,11 +1483,11 @@
     return handle_scope_->GetHandle(0).GetReference();
   }
 
-  jobject GetFirstHandleScopeJObject() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  jobject GetFirstHandleScopeJObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return handle_scope_->GetHandle(0).ToJObject();
   }
 
-  void* GetBottomOfUsedArea() {
+  void* GetBottomOfUsedArea() const {
     return bottom_of_used_area_;
   }
 
@@ -1488,7 +1511,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 +1532,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_++;
@@ -1558,7 +1581,7 @@
       break;
     case Primitive::kPrimVoid:
       LOG(FATAL) << "UNREACHABLE";
-      break;
+      UNREACHABLE();
   }
 }
 
@@ -1611,13 +1634,13 @@
   uint32_t shorty_len = 0;
   const char* shorty = called->GetShorty(&shorty_len);
 
-  // Run the visitor.
-  BuildGenericJniFrameVisitor visitor(&sp, called->IsStatic(), shorty, shorty_len, self);
+  // Run the visitor and update sp.
+  BuildGenericJniFrameVisitor visitor(self, called->IsStatic(), shorty, shorty_len, &sp);
   visitor.VisitArguments();
   visitor.FinalizeHandleScope(self);
 
   // Fix up managed-stack things in Thread.
-  self->SetTopOfStack(sp, 0);
+  self->SetTopOfStack(sp);
 
   self->VerifyStack();
 
@@ -1746,10 +1769,11 @@
 static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
                                      mirror::ArtMethod* caller_method,
                                      Thread* self, StackReference<mirror::ArtMethod>* sp) {
+  ScopedQuickEntrypointChecks sqec(self);
+  DCHECK_EQ(sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs));
   mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check,
                                              type);
   if (UNLIKELY(method == nullptr)) {
-    FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
     const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
     uint32_t shorty_len;
     const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(method_idx), &shorty_len);
@@ -1854,22 +1878,21 @@
                                                       Thread* self,
                                                       StackReference<mirror::ArtMethod>* sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
   mirror::ArtMethod* method;
   if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
     method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method);
     if (UNLIKELY(method == NULL)) {
-      FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
       ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(interface_method, this_object,
                                                                  caller_method);
       return GetTwoWordFailureValue();  // Failure.
     }
   } else {
-    FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
     DCHECK(interface_method == Runtime::Current()->GetResolutionMethod());
 
     // Find the caller PC.
-    constexpr size_t pc_offset = GetCalleeSavePCOffset(kRuntimeISA, Runtime::kRefsAndArgs);
-    uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp) + pc_offset);
+    constexpr size_t pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsAndArgs);
+    uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) + pc_offset);
 
     // Map the caller PC to a dex PC.
     uint32_t dex_pc = caller_method->ToDexPc(caller_pc);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc
index 66ee218..85a0b99 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc
@@ -34,7 +34,7 @@
     t->TransitionFromSuspendedToRunnable();  // So we can create callee-save methods.
 
     r->SetInstructionSet(isa);
-    mirror::ArtMethod* save_method = r->CreateCalleeSaveMethod(type);
+    mirror::ArtMethod* save_method = r->CreateCalleeSaveMethod();
     r->SetCalleeSaveMethod(save_method, type);
 
     t->TransitionFromRunnableToSuspended(ThreadState::kNative);  // So we can shut down.
@@ -55,9 +55,10 @@
       NO_THREAD_SAFETY_ANALYSIS {
     mirror::ArtMethod* save_method = CreateCalleeSaveMethod(isa, type);
     QuickMethodFrameInfo frame_info = save_method->GetQuickFrameInfo();
-    EXPECT_EQ(save_method->GetReturnPcOffsetInBytes(), pc_offset) << "Expected and real pc offset"
-        " differs for " << type << " core spills=" << std::hex << frame_info.CoreSpillMask() <<
-        " fp spills=" << frame_info.FpSpillMask() << std::dec << " ISA " << isa;
+    EXPECT_EQ(save_method->GetReturnPcOffset().SizeValue(), pc_offset)
+        << "Expected and real pc offset differs for " << type
+        << " core spills=" << std::hex << frame_info.CoreSpillMask()
+        << " fp spills=" << frame_info.FpSpillMask() << std::dec << " ISA " << isa;
   }
 };
 
@@ -95,13 +96,13 @@
 TEST_F(QuickTrampolineEntrypointsTest, ReturnPC) {
   // Ensure that the computation in callee_save_frame.h correct.
   // Note: we can only check against the kRuntimeISA, because the ArtMethod computation uses
-  // kPointerSize, which is wrong when the target bitwidth is not the same as the host's.
+  // sizeof(void*), which is wrong when the target bitwidth is not the same as the host's.
   CheckPCOffset(kRuntimeISA, Runtime::kRefsAndArgs,
-                GetCalleeSavePCOffset(kRuntimeISA, Runtime::kRefsAndArgs));
+                GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsAndArgs));
   CheckPCOffset(kRuntimeISA, Runtime::kRefsOnly,
-                GetCalleeSavePCOffset(kRuntimeISA, Runtime::kRefsOnly));
+                GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsOnly));
   CheckPCOffset(kRuntimeISA, Runtime::kSaveAll,
-                GetCalleeSavePCOffset(kRuntimeISA, Runtime::kSaveAll));
+                GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kSaveAll));
 }
 
 }  // namespace art
diff --git a/runtime/entrypoints/runtime_asm_entrypoints.h b/runtime/entrypoints/runtime_asm_entrypoints.h
new file mode 100644
index 0000000..db36a73
--- /dev/null
+++ b/runtime/entrypoints/runtime_asm_entrypoints.h
@@ -0,0 +1,117 @@
+/*
+ * 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_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_
+#define ART_RUNTIME_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_
+
+namespace art {
+
+#ifndef BUILDING_LIBART
+#error "File and symbols only for use within libart."
+#endif
+
+extern "C" void* art_jni_dlsym_lookup_stub(JNIEnv*, jobject);
+static inline const void* GetJniDlsymLookupStub() {
+  return reinterpret_cast<const void*>(art_jni_dlsym_lookup_stub);
+}
+
+// Return the address of portable stub code for handling IMT conflicts.
+extern "C" void art_portable_imt_conflict_trampoline(mirror::ArtMethod*);
+static inline const void* GetPortableImtConflictStub() {
+  return reinterpret_cast<const void*>(art_portable_imt_conflict_trampoline);
+}
+
+// Return the address of quick stub code for handling IMT conflicts.
+extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
+static inline const void* GetQuickImtConflictStub() {
+  return reinterpret_cast<const void*>(art_quick_imt_conflict_trampoline);
+}
+
+// Return the address of portable stub code for bridging from portable code to the interpreter.
+extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
+static inline const void* GetPortableToInterpreterBridge() {
+  return reinterpret_cast<const void*>(art_portable_to_interpreter_bridge);
+}
+
+// Return the address of quick stub code for bridging from quick code to the interpreter.
+extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
+static inline const void* GetQuickToInterpreterBridge() {
+  return reinterpret_cast<const void*>(art_quick_to_interpreter_bridge);
+}
+
+// Return the address of portable stub code for bridging from portable code to quick.
+static inline const void* GetPortableToQuickBridge() {
+  // TODO: portable to quick bridge. Bug: 8196384
+  return GetPortableToInterpreterBridge();
+}
+
+// Return the address of quick stub code for bridging from quick code to portable.
+static inline const void* GetQuickToPortableBridge() {
+  // TODO: quick to portable bridge. Bug: 8196384
+  return GetQuickToInterpreterBridge();
+}
+
+// Return the address of quick stub code for handling JNI calls.
+extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
+static inline const void* GetQuickGenericJniStub() {
+  return reinterpret_cast<const void*>(art_quick_generic_jni_trampoline);
+}
+
+// Return the address of portable stub code for handling transitions into the proxy invoke handler.
+extern "C" void art_portable_proxy_invoke_handler();
+static inline const void* GetPortableProxyInvokeHandler() {
+  return reinterpret_cast<const void*>(art_portable_proxy_invoke_handler);
+}
+
+// Return the address of quick stub code for handling transitions into the proxy invoke handler.
+extern "C" void art_quick_proxy_invoke_handler();
+static inline const void* GetQuickProxyInvokeHandler() {
+  return reinterpret_cast<const void*>(art_quick_proxy_invoke_handler);
+}
+
+// Return the address of portable stub code for resolving a method at first call.
+extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*);
+static inline const void* GetPortableResolutionStub() {
+  return reinterpret_cast<const void*>(art_portable_resolution_trampoline);
+}
+
+// Return the address of quick stub code for resolving a method at first call.
+extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
+static inline const void* GetQuickResolutionStub() {
+  return reinterpret_cast<const void*>(art_quick_resolution_trampoline);
+}
+
+// Entry point for quick code that performs deoptimization.
+extern "C" void art_quick_deoptimize();
+static inline const void* GetQuickDeoptimizationEntryPoint() {
+  return reinterpret_cast<const void*>(art_quick_deoptimize);
+}
+
+// Return address of instrumentation entry point used by non-interpreter based tracing.
+extern "C" void art_quick_instrumentation_entry(void*);
+static inline const void* GetQuickInstrumentationEntryPoint() {
+  return reinterpret_cast<const void*>(art_quick_instrumentation_entry);
+}
+
+// The return_pc of instrumentation exit stub.
+extern "C" void art_quick_instrumentation_exit();
+static inline const void* GetQuickInstrumentationExitPc() {
+  return reinterpret_cast<const void*>(art_quick_instrumentation_exit);
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index d205e2a..cfd2a3d 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -86,189 +86,201 @@
     // TODO: Better connection. Take alignment into account.
     EXPECT_OFFSET_DIFF_GT3(Thread, tls64_.stats, tlsPtr_.card_table, 8, thread_tls64_to_tlsptr);
 
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, card_table, exception, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, exception, stack_end, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_end, managed_stack, kPointerSize);
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, card_table, exception, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, exception, stack_end, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_end, managed_stack, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, managed_stack, suspend_trigger, sizeof(ManagedStack));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, suspend_trigger, jni_env, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jni_env, self, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, self, opeer, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, opeer, jpeer, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jpeer, stack_begin, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_begin, stack_size, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_size, throw_location, kPointerSize);
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, suspend_trigger, jni_env, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jni_env, self, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, self, opeer, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, opeer, jpeer, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jpeer, stack_begin, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_begin, stack_size, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_size, throw_location, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, throw_location, stack_trace_sample, sizeof(ThrowLocation));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_trace_sample, wait_next, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, wait_next, monitor_enter_object, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, monitor_enter_object, top_handle_scope, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, top_handle_scope, class_loader_override, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, class_loader_override, long_jump_context, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, long_jump_context, instrumentation_stack, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, instrumentation_stack, debug_invoke_req, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, debug_invoke_req, single_step_control, kPointerSize);
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_trace_sample, wait_next, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, wait_next, monitor_enter_object, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, monitor_enter_object, top_handle_scope, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, top_handle_scope, class_loader_override, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, class_loader_override, long_jump_context, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, long_jump_context, instrumentation_stack, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, instrumentation_stack, debug_invoke_req, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, debug_invoke_req, single_step_control, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, single_step_control, deoptimization_shadow_frame,
-                        kPointerSize);
+                        sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, deoptimization_shadow_frame,
-                        shadow_frame_under_construction, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, shadow_frame_under_construction, name, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, name, pthread_self, kPointerSize);
+                        shadow_frame_under_construction, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, shadow_frame_under_construction, name, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, name, pthread_self, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, pthread_self, last_no_thread_suspension_cause,
-                        kPointerSize);
+                        sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, last_no_thread_suspension_cause, checkpoint_functions,
-                        kPointerSize);
+                        sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_functions, interpreter_entrypoints,
-                        kPointerSize * 3);
+                        sizeof(void*) * 3);
 
     // Skip across the entrypoints structures.
 
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, rosalloc_runs, kPointerSize);
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, rosalloc_runs, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, rosalloc_runs, thread_local_alloc_stack_top,
-                        kPointerSize * kNumRosAllocThreadLocalSizeBrackets);
+                        sizeof(void*) * kNumRosAllocThreadLocalSizeBrackets);
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_top, thread_local_alloc_stack_end,
-                        kPointerSize);
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_end, held_mutexes, kPointerSize);
+                        sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_end, held_mutexes, sizeof(void*));
     EXPECT_OFFSET_DIFF(Thread, tlsPtr_.held_mutexes, Thread, wait_mutex_,
-                       kPointerSize * kLockLevelCount + kPointerSize, thread_tlsptr_end);
+                       sizeof(void*) * kLockLevelCount + sizeof(void*), thread_tlsptr_end);
   }
 
   void CheckInterpreterEntryPoints() {
     CHECKED(OFFSETOF_MEMBER(InterpreterEntryPoints, pInterpreterToInterpreterBridge) == 0,
             InterpreterEntryPoints_start_with_i2i);
     EXPECT_OFFSET_DIFFNP(InterpreterEntryPoints, pInterpreterToInterpreterBridge,
-                         pInterpreterToCompiledCodeBridge, kPointerSize);
+                         pInterpreterToCompiledCodeBridge, sizeof(void*));
     CHECKED(OFFSETOF_MEMBER(InterpreterEntryPoints, pInterpreterToCompiledCodeBridge)
-            + kPointerSize == sizeof(InterpreterEntryPoints), InterpreterEntryPoints_all);
+            + sizeof(void*) == sizeof(InterpreterEntryPoints), InterpreterEntryPoints_all);
   }
 
   void CheckJniEntryPoints() {
     CHECKED(OFFSETOF_MEMBER(JniEntryPoints, pDlsymLookup) == 0,
             JniEntryPoints_start_with_dlsymlookup);
     CHECKED(OFFSETOF_MEMBER(JniEntryPoints, pDlsymLookup)
-            + kPointerSize == sizeof(JniEntryPoints), JniEntryPoints_all);
+            + sizeof(void*) == sizeof(JniEntryPoints), JniEntryPoints_all);
   }
 
   void CheckPortableEntryPoints() {
     CHECKED(OFFSETOF_MEMBER(PortableEntryPoints, pPortableImtConflictTrampoline) == 0,
             PortableEntryPoints_start_with_imt);
     EXPECT_OFFSET_DIFFNP(PortableEntryPoints, pPortableImtConflictTrampoline,
-                         pPortableResolutionTrampoline, kPointerSize);
+                         pPortableResolutionTrampoline, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(PortableEntryPoints, pPortableResolutionTrampoline,
-                         pPortableToInterpreterBridge, kPointerSize);
+                         pPortableToInterpreterBridge, sizeof(void*));
     CHECKED(OFFSETOF_MEMBER(PortableEntryPoints, pPortableToInterpreterBridge)
-            + kPointerSize == sizeof(PortableEntryPoints), PortableEntryPoints_all);
+            + sizeof(void*) == sizeof(PortableEntryPoints), PortableEntryPoints_all);
   }
 
   void CheckQuickEntryPoints() {
     CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pAllocArray) == 0,
                 QuickEntryPoints_start_with_allocarray);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArray, pAllocArrayResolved, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArray, pAllocArrayResolved, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayResolved, pAllocArrayWithAccessCheck,
-                         kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayWithAccessCheck, pAllocObject, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObject, pAllocObjectResolved, kPointerSize);
+                         sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayWithAccessCheck, pAllocObject, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObject, pAllocObjectResolved, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectResolved, pAllocObjectInitialized,
-                         kPointerSize);
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithAccessCheck,
-                         kPointerSize);
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithAccessCheck, pCheckAndAllocArray,
-                         kPointerSize);
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck,
-                         kPointerSize);
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArrayWithAccessCheck,
-                         pInstanceofNonTrivial, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInstanceofNonTrivial, pCheckCast, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckCast, pInitializeStaticStorage, kPointerSize);
+                         pInstanceofNonTrivial, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInstanceofNonTrivial, pCheckCast, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckCast, pInitializeStaticStorage, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeStaticStorage, pInitializeTypeAndVerifyAccess,
-                         kPointerSize);
+                         sizeof(void*));
     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, 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, pGet32Instance, pGet32Static, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet32Static, pGet64Instance, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Static, pGetObjInstance, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjInstance, pGetObjStatic, kPointerSize);
+                         sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet8Instance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Instance, pSet8Static, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Static, pSet16Instance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet16Instance, pSet16Static, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet16Static, pSet32Instance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet32Instance, pSet32Static, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet32Static, pSet64Instance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet64Instance, pSet64Static, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet64Static, pSetObjInstance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSetObjInstance, pSetObjStatic, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSetObjStatic, pGetByteInstance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetByteInstance, pGetBooleanInstance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetBooleanInstance, pGetByteStatic, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetByteStatic, pGetBooleanStatic, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetBooleanStatic, pGetShortInstance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetShortInstance, pGetCharInstance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetCharInstance, pGetShortStatic, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetShortStatic, pGetCharStatic, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetCharStatic, pGet32Instance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet32Instance, pGet32Static, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet32Static, pGet64Instance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Static, pGetObjInstance, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjInstance, pGetObjStatic, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObjectWithNullAndBoundCheck,
-                         kPointerSize);
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithNullAndBoundCheck,
-                         pAputObjectWithBoundCheck, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, kPointerSize);
+                         pAputObjectWithBoundCheck, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodStartSynchronized,
-                         kPointerSize);
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStartSynchronized, pJniMethodEnd,
-                         kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEnd, pJniMethodEndSynchronized, kPointerSize);
+                         sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEnd, pJniMethodEndSynchronized, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEndSynchronized, pJniMethodEndWithReference,
-                         kPointerSize);
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEndWithReference,
-                         pJniMethodEndWithReferenceSynchronized, kPointerSize);
+                         pJniMethodEndWithReferenceSynchronized, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEndWithReferenceSynchronized,
-                         pQuickGenericJniTrampoline, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pQuickGenericJniTrampoline, pLockObject, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLockObject, pUnlockObject, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pUnlockObject, pCmpgDouble, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgDouble, pCmpgFloat, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgFloat, pCmplDouble, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplDouble, pCmplFloat, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplFloat, pFmod, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pFmod, pL2d, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pL2d, pFmodf, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pFmodf, pL2f, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pL2f, pD2iz, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pD2iz, pF2iz, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pF2iz, pIdivmod, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pIdivmod, pD2l, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pD2l, pF2l, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pF2l, pLdiv, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLdiv, pLmod, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLmod, pLmul, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLmul, pShlLong, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pShlLong, pShrLong, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pShrLong, pUshrLong, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pUshrLong, pIndexOf, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pIndexOf, pStringCompareTo, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pStringCompareTo, pMemcpy, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pMemcpy, pQuickImtConflictTrampoline, kPointerSize);
+                         pQuickGenericJniTrampoline, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pQuickGenericJniTrampoline, pLockObject, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLockObject, pUnlockObject, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pUnlockObject, pCmpgDouble, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgDouble, pCmpgFloat, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgFloat, pCmplDouble, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplDouble, pCmplFloat, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplFloat, pFmod, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pFmod, pL2d, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pL2d, pFmodf, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pFmodf, pL2f, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pL2f, pD2iz, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pD2iz, pF2iz, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pF2iz, pIdivmod, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pIdivmod, pD2l, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pD2l, pF2l, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pF2l, pLdiv, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLdiv, pLmod, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLmod, pLmul, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLmul, pShlLong, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pShlLong, pShrLong, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pShrLong, pUshrLong, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pUshrLong, pIndexOf, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pIndexOf, pStringCompareTo, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pStringCompareTo, pMemcpy, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pMemcpy, pQuickImtConflictTrampoline, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pQuickImtConflictTrampoline, pQuickResolutionTrampoline,
-                         kPointerSize);
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pQuickResolutionTrampoline, pQuickToInterpreterBridge,
-                         kPointerSize);
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pQuickToInterpreterBridge,
-                         pInvokeDirectTrampolineWithAccessCheck, kPointerSize);
+                         pInvokeDirectTrampolineWithAccessCheck, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeDirectTrampolineWithAccessCheck,
-                         pInvokeInterfaceTrampolineWithAccessCheck, kPointerSize);
+                         pInvokeInterfaceTrampolineWithAccessCheck, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeInterfaceTrampolineWithAccessCheck,
-                         pInvokeStaticTrampolineWithAccessCheck, kPointerSize);
+                         pInvokeStaticTrampolineWithAccessCheck, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeStaticTrampolineWithAccessCheck,
-                         pInvokeSuperTrampolineWithAccessCheck, kPointerSize);
+                         pInvokeSuperTrampolineWithAccessCheck, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeSuperTrampolineWithAccessCheck,
-                         pInvokeVirtualTrampolineWithAccessCheck, kPointerSize);
+                         pInvokeVirtualTrampolineWithAccessCheck, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeVirtualTrampolineWithAccessCheck,
-                         pTestSuspend, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTestSuspend, pDeliverException, kPointerSize);
+                         pTestSuspend, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTestSuspend, pDeliverException, sizeof(void*));
 
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeliverException, pThrowArrayBounds, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowArrayBounds, pThrowDivZero, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowDivZero, pThrowNoSuchMethod, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNoSuchMethod, pThrowNullPointer, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNullPointer, pThrowStackOverflow, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pA64Load, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Load, pA64Store, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeliverException, pThrowArrayBounds, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowArrayBounds, pThrowDivZero, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowDivZero, pThrowNoSuchMethod, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNoSuchMethod, pThrowNullPointer, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNullPointer, pThrowStackOverflow, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pA64Load, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Load, pA64Store, sizeof(void*));
 
     CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pA64Store)
-            + kPointerSize == sizeof(QuickEntryPoints), QuickEntryPoints_all);
+            + sizeof(void*) == sizeof(QuickEntryPoints), QuickEntryPoints_all);
   }
 };
 
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 99633a3..ee9b221 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -43,9 +43,9 @@
     Handle<mirror::ClassLoader> class_loader(
         hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("ExceptionHandle"))));
     my_klass_ = class_linker_->FindClass(soa.Self(), "LExceptionHandle;", class_loader);
-    ASSERT_TRUE(my_klass_ != NULL);
+    ASSERT_TRUE(my_klass_ != nullptr);
     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();
@@ -77,7 +77,7 @@
     uint32_t vmap_table_offset = sizeof(OatQuickMethodHeader) + fake_vmap_table_data.size();
     uint32_t mapping_table_offset = vmap_table_offset + fake_mapping_data.size();
     OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset,
-                                       4 * kPointerSize, 0u, 0u, code_size);
+                                       4 * sizeof(void*), 0u, 0u, code_size);
     fake_header_code_and_maps_.resize(sizeof(method_header));
     memcpy(&fake_header_code_and_maps_[0], &method_header, sizeof(method_header));
     fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(),
@@ -93,12 +93,12 @@
     const uint8_t* code_ptr = &fake_header_code_and_maps_[mapping_table_offset];
 
     method_f_ = my_klass_->FindVirtualMethod("f", "()I");
-    ASSERT_TRUE(method_f_ != NULL);
+    ASSERT_TRUE(method_f_ != nullptr);
     method_f_->SetEntryPointFromQuickCompiledCode(code_ptr);
     method_f_->SetNativeGcMap(&fake_gc_map_[0]);
 
     method_g_ = my_klass_->FindVirtualMethod("g", "(I)V");
-    ASSERT_TRUE(method_g_ != NULL);
+    ASSERT_TRUE(method_g_ != nullptr);
     method_g_->SetEntryPointFromQuickCompiledCode(code_ptr);
     method_g_->SetNativeGcMap(&fake_gc_map_[0]);
   }
@@ -122,7 +122,7 @@
   ScopedObjectAccess soa(Thread::Current());
   const DexFile::CodeItem* code_item = dex_->GetCodeItem(method_f_->GetCodeItemOffset());
 
-  ASSERT_TRUE(code_item != NULL);
+  ASSERT_TRUE(code_item != nullptr);
 
   ASSERT_EQ(2u, code_item->tries_size_);
   ASSERT_NE(0u, code_item->insns_size_in_code_units_);
@@ -163,19 +163,35 @@
   ScopedObjectAccess soa(env);
 
   std::vector<uintptr_t> fake_stack;
+  Runtime* r = Runtime::Current();
+  r->SetInstructionSet(kRuntimeISA);
+  mirror::ArtMethod* save_method = r->CreateCalleeSaveMethod();
+  r->SetCalleeSaveMethod(save_method, Runtime::kSaveAll);
+  QuickMethodFrameInfo frame_info = save_method->GetQuickFrameInfo();
+
   ASSERT_EQ(kStackAlignment, 16U);
   // ASSERT_EQ(sizeof(uintptr_t), sizeof(uint32_t));
 
+
   if (!kUsePortableCompiler) {
-    // Create two fake stack frames with mapping data created in SetUp. We map offset 3 in the code
-    // to dex pc 3.
+    // Create three fake stack frames with mapping data created in SetUp. We map offset 3 in the
+    // code to dex pc 3.
     const uint32_t dex_pc = 3;
 
+    // Create the stack frame for the callee save method, expected by the runtime.
+    fake_stack.push_back(reinterpret_cast<uintptr_t>(save_method));
+    for (size_t i = 0; i < frame_info.FrameSizeInBytes() - 2 * sizeof(uintptr_t);
+         i += sizeof(uintptr_t)) {
+      fake_stack.push_back(0);
+    }
+
+    fake_stack.push_back(method_g_->ToNativeQuickPc(dex_pc));  // return pc
+
     // Create/push fake 16byte stack frame for method g
     fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
     fake_stack.push_back(0);
     fake_stack.push_back(0);
-    fake_stack.push_back(method_f_->ToNativePc(dex_pc));  // return pc
+    fake_stack.push_back(method_f_->ToNativeQuickPc(dex_pc));  // return pc
 
     // Create/push fake 16byte stack frame for method f
     fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
@@ -183,7 +199,7 @@
     fake_stack.push_back(0);
     fake_stack.push_back(0xEBAD6070);  // return pc
 
-    // Pull Method* of NULL to terminate the trace
+    // Push Method* of NULL to terminate the trace
     fake_stack.push_back(0);
 
     // Push null values which will become null incoming arguments.
@@ -192,9 +208,7 @@
     fake_stack.push_back(0);
 
     // Set up thread to appear as if we called out of method_g_ at pc dex 3
-    thread->SetTopOfStack(
-        reinterpret_cast<StackReference<mirror::ArtMethod>*>(&fake_stack[0]),
-        method_g_->ToNativePc(dex_pc));  // return pc
+    thread->SetTopOfStack(reinterpret_cast<StackReference<mirror::ArtMethod>*>(&fake_stack[0]));
   } else {
     // Create/push fake 20-byte shadow frame for method g
     fake_stack.push_back(0);
@@ -215,33 +229,35 @@
   }
 
   jobject internal = thread->CreateInternalStackTrace<false>(soa);
-  ASSERT_TRUE(internal != NULL);
+  ASSERT_TRUE(internal != nullptr);
   jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
-  ASSERT_TRUE(ste_array != NULL);
+  ASSERT_TRUE(ste_array != nullptr);
   mirror::ObjectArray<mirror::StackTraceElement>* trace_array =
       soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(ste_array);
 
-  ASSERT_TRUE(trace_array != NULL);
-  ASSERT_TRUE(trace_array->Get(0) != NULL);
+  ASSERT_TRUE(trace_array != nullptr);
+  ASSERT_TRUE(trace_array->Get(0) != nullptr);
   EXPECT_STREQ("ExceptionHandle",
                trace_array->Get(0)->GetDeclaringClass()->ToModifiedUtf8().c_str());
-  EXPECT_STREQ("ExceptionHandle.java", trace_array->Get(0)->GetFileName()->ToModifiedUtf8().c_str());
+  EXPECT_STREQ("ExceptionHandle.java",
+               trace_array->Get(0)->GetFileName()->ToModifiedUtf8().c_str());
   EXPECT_STREQ("g", trace_array->Get(0)->GetMethodName()->ToModifiedUtf8().c_str());
   EXPECT_EQ(37, trace_array->Get(0)->GetLineNumber());
 
-  ASSERT_TRUE(trace_array->Get(1) != NULL);
+  ASSERT_TRUE(trace_array->Get(1) != nullptr);
   EXPECT_STREQ("ExceptionHandle",
                trace_array->Get(1)->GetDeclaringClass()->ToModifiedUtf8().c_str());
-  EXPECT_STREQ("ExceptionHandle.java", trace_array->Get(1)->GetFileName()->ToModifiedUtf8().c_str());
+  EXPECT_STREQ("ExceptionHandle.java",
+               trace_array->Get(1)->GetFileName()->ToModifiedUtf8().c_str());
   EXPECT_STREQ("f", trace_array->Get(1)->GetMethodName()->ToModifiedUtf8().c_str());
   EXPECT_EQ(22, trace_array->Get(1)->GetLineNumber());
 
-#if !defined(ART_USE_PORTABLE_COMPILER)
-  thread->SetTopOfStack(NULL, 0);  // Disarm the assertion that no code is running when we detach.
-#else
-  thread->PopShadowFrame();
-  thread->PopShadowFrame();
-#endif
+  if (!kUsePortableCompiler) {
+    thread->SetTopOfStack(nullptr);  // Disarm the assertion that no code is running when we detach.
+  } else {
+    thread->PopShadowFrame();
+    thread->PopShadowFrame();
+  }
 }
 
 }  // namespace art
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 56d709a..ab3ec62 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"
@@ -128,13 +129,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(...)
   //
@@ -163,11 +174,91 @@
   // 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.
+
+  // TODO: add SIGSEGV back to the nested signals when we can handle running out stack gracefully.
+  static const int handled_nested_signals[] = {SIGABRT};
+  constexpr size_t num_handled_nested_signals = arraysize(handled_nested_signals);
+
+  // 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);
+  for (int signal : handled_nested_signals) {
+    sigaddset(&sigset, signal);
+  }
+  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;
+  struct sigaction oldactions[num_handled_nested_signals];
+  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);
+  for (int signal : handled_nested_signals) {
+    sigaddset(&action.sa_mask, signal);
+  }
+
+  action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+#if !defined(__APPLE__) && !defined(__mips__)
+  action.sa_restorer = nullptr;
+#endif
+
+  // Catch handled signals to invoke our nested handler.
+  bool success = true;
+  for (size_t i = 0; i < num_handled_nested_signals; ++i) {
+    success = sigaction(handled_nested_signals[i], &action, &oldactions[i]) == 0;
+    if (!success) {
+      PLOG(ERROR) << "Unable to set up nested signal handler";
+      break;
     }
   }
+  if (success) {
+    // 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.
+          for (size_t i = 0; i < num_handled_nested_signals; ++i) {
+            success = sigaction(handled_nested_signals[i], &oldactions[i], nullptr) == 0;
+            if (!success) {
+              PLOG(ERROR) << "Unable to restore signal handler";
+            }
+          }
+          fault_manager.Init();
+          return;
+        }
+      }
+    } else {
+      LOG(ERROR) << "Nested signal detected - original signal being reported";
+    }
+
+    // Restore the signal handlers.
+    for (size_t i = 0; i < num_handled_nested_signals; ++i) {
+      success = sigaction(handled_nested_signals[i], &oldactions[i], nullptr) == 0;
+      if (!success) {
+        PLOG(ERROR) << "Unable to restore signal handler";
+      }
+    }
+  }
+
+  // Now put the fault manager back in place.
+  fault_manager.Init();
 
   // Set a breakpoint in this function to catch unhandled signals.
   art_sigsegv_fault();
@@ -177,6 +268,7 @@
 }
 
 void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) {
+  DCHECK(initialized_);
   if (generated_code) {
     generated_code_handlers_.push_back(handler);
   } else {
@@ -310,7 +402,7 @@
 
 bool JavaStackTraceHandler::Action(int sig, siginfo_t* siginfo, void* context) {
   // Make sure that we are in the generated code, but we may not have a dex pc.
-
+  UNUSED(sig);
 #ifdef TEST_NESTED_SIGNAL
   bool in_generated_code = true;
 #else
@@ -323,75 +415,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);
 #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 89c9360..adac4c2 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -39,11 +39,18 @@
   ~FaultManager();
 
   void Init();
+
+  // Unclaim signals.
+  void Release();
+
+  // Unclaim signals and delete registered handlers.
   void Shutdown();
   void EnsureArtActionInFrontOfSignalChain();
 
   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/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index 2c72ba1..929a1d2 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -213,7 +213,7 @@
     mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T),
                                         PROT_READ | PROT_WRITE, false, &error_msg));
     CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack.\n" << error_msg;
-    byte* addr = mem_map_->Begin();
+    uint8_t* addr = mem_map_->Begin();
     CHECK(addr != NULL);
     debug_is_sorted_ = true;
     begin_ = reinterpret_cast<T*>(addr);
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index 3b06f74..15562e5 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -27,9 +27,9 @@
 namespace gc {
 namespace accounting {
 
-static inline bool byte_cas(byte old_value, byte new_value, byte* address) {
+static inline bool byte_cas(uint8_t old_value, uint8_t new_value, uint8_t* address) {
 #if defined(__i386__) || defined(__x86_64__)
-  Atomic<byte>* byte_atomic = reinterpret_cast<Atomic<byte>*>(address);
+  Atomic<uint8_t>* byte_atomic = reinterpret_cast<Atomic<uint8_t>*>(address);
   return byte_atomic->CompareExchangeWeakRelaxed(old_value, new_value);
 #else
   // Little endian means most significant byte is on the left.
@@ -49,19 +49,19 @@
 }
 
 template <typename Visitor>
-inline size_t CardTable::Scan(ContinuousSpaceBitmap* bitmap, byte* scan_begin, byte* scan_end,
-                              const Visitor& visitor, const byte minimum_age) const {
-  DCHECK_GE(scan_begin, reinterpret_cast<byte*>(bitmap->HeapBegin()));
+inline size_t CardTable::Scan(ContinuousSpaceBitmap* bitmap, uint8_t* scan_begin, uint8_t* scan_end,
+                              const Visitor& visitor, const uint8_t minimum_age) const {
+  DCHECK_GE(scan_begin, reinterpret_cast<uint8_t*>(bitmap->HeapBegin()));
   // scan_end is the byte after the last byte we scan.
-  DCHECK_LE(scan_end, reinterpret_cast<byte*>(bitmap->HeapLimit()));
-  byte* card_cur = CardFromAddr(scan_begin);
-  byte* card_end = CardFromAddr(AlignUp(scan_end, kCardSize));
+  DCHECK_LE(scan_end, reinterpret_cast<uint8_t*>(bitmap->HeapLimit()));
+  uint8_t* card_cur = CardFromAddr(scan_begin);
+  uint8_t* card_end = CardFromAddr(AlignUp(scan_end, kCardSize));
   CheckCardValid(card_cur);
   CheckCardValid(card_end);
   size_t cards_scanned = 0;
 
   // Handle any unaligned cards at the start.
-  while (!IsAligned<sizeof(word)>(card_cur) && card_cur < card_end) {
+  while (!IsAligned<sizeof(intptr_t)>(card_cur) && card_cur < card_end) {
     if (*card_cur >= minimum_age) {
       uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(card_cur));
       bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
@@ -70,7 +70,7 @@
     ++card_cur;
   }
 
-  byte* aligned_end = card_end -
+  uint8_t* aligned_end = card_end -
       (reinterpret_cast<uintptr_t>(card_end) & (sizeof(uintptr_t) - 1));
 
   uintptr_t* word_end = reinterpret_cast<uintptr_t*>(aligned_end);
@@ -85,14 +85,14 @@
 
     // Find the first dirty card.
     uintptr_t start_word = *word_cur;
-    uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(reinterpret_cast<byte*>(word_cur)));
+    uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(reinterpret_cast<uint8_t*>(word_cur)));
     // TODO: Investigate if processing continuous runs of dirty cards with a single bitmap visit is
     // more efficient.
     for (size_t i = 0; i < sizeof(uintptr_t); ++i) {
-      if (static_cast<byte>(start_word) >= minimum_age) {
-        auto* card = reinterpret_cast<byte*>(word_cur) + i;
-        DCHECK(*card == static_cast<byte>(start_word) || *card == kCardDirty)
-            << "card " << static_cast<size_t>(*card) << " word " << (start_word & 0xFF);
+      if (static_cast<uint8_t>(start_word) >= minimum_age) {
+        auto* card = reinterpret_cast<uint8_t*>(word_cur) + i;
+        DCHECK(*card == static_cast<uint8_t>(start_word) || *card == kCardDirty)
+            << "card " << static_cast<size_t>(*card) << " intptr_t " << (start_word & 0xFF);
         bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
         ++cards_scanned;
       }
@@ -103,7 +103,7 @@
   exit_for:
 
   // Handle any unaligned cards at the end.
-  card_cur = reinterpret_cast<byte*>(word_end);
+  card_cur = reinterpret_cast<uint8_t*>(word_end);
   while (card_cur < card_end) {
     if (*card_cur >= minimum_age) {
       uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(card_cur));
@@ -125,16 +125,16 @@
  * us to know which cards got cleared.
  */
 template <typename Visitor, typename ModifiedVisitor>
-inline void CardTable::ModifyCardsAtomic(byte* scan_begin, byte* scan_end, const Visitor& visitor,
+inline void CardTable::ModifyCardsAtomic(uint8_t* scan_begin, uint8_t* scan_end, const Visitor& visitor,
                                          const ModifiedVisitor& modified) {
-  byte* card_cur = CardFromAddr(scan_begin);
-  byte* card_end = CardFromAddr(AlignUp(scan_end, kCardSize));
+  uint8_t* card_cur = CardFromAddr(scan_begin);
+  uint8_t* card_end = CardFromAddr(AlignUp(scan_end, kCardSize));
   CheckCardValid(card_cur);
   CheckCardValid(card_end);
 
   // Handle any unaligned cards at the start.
-  while (!IsAligned<sizeof(word)>(card_cur) && card_cur < card_end) {
-    byte expected, new_value;
+  while (!IsAligned<sizeof(intptr_t)>(card_cur) && card_cur < card_end) {
+    uint8_t expected, new_value;
     do {
       expected = *card_cur;
       new_value = visitor(expected);
@@ -146,9 +146,9 @@
   }
 
   // Handle unaligned cards at the end.
-  while (!IsAligned<sizeof(word)>(card_end) && card_end > card_cur) {
+  while (!IsAligned<sizeof(intptr_t)>(card_end) && card_end > card_cur) {
     --card_end;
-    byte expected, new_value;
+    uint8_t expected, new_value;
     do {
       expected = *card_end;
       new_value = visitor(expected);
@@ -184,10 +184,10 @@
       Atomic<uintptr_t>* atomic_word = reinterpret_cast<Atomic<uintptr_t>*>(word_cur);
       if (LIKELY(atomic_word->CompareExchangeWeakRelaxed(expected_word, new_word))) {
         for (size_t i = 0; i < sizeof(uintptr_t); ++i) {
-          const byte expected_byte = expected_bytes[i];
-          const byte new_byte = new_bytes[i];
+          const uint8_t expected_byte = expected_bytes[i];
+          const uint8_t new_byte = new_bytes[i];
           if (expected_byte != new_byte) {
-            modified(reinterpret_cast<byte*>(word_cur) + i, expected_byte, new_byte);
+            modified(reinterpret_cast<uint8_t*>(word_cur) + i, expected_byte, new_byte);
           }
         }
         break;
@@ -197,7 +197,7 @@
   }
 }
 
-inline void* CardTable::AddrFromCard(const byte *card_addr) const {
+inline void* CardTable::AddrFromCard(const uint8_t *card_addr) const {
   DCHECK(IsValidCard(card_addr))
     << " card_addr: " << reinterpret_cast<const void*>(card_addr)
     << " begin: " << reinterpret_cast<void*>(mem_map_->Begin() + offset_)
@@ -206,15 +206,15 @@
   return reinterpret_cast<void*>(offset << kCardShift);
 }
 
-inline byte* CardTable::CardFromAddr(const void *addr) const {
-  byte *card_addr = biased_begin_ + (reinterpret_cast<uintptr_t>(addr) >> kCardShift);
+inline uint8_t* CardTable::CardFromAddr(const void *addr) const {
+  uint8_t *card_addr = biased_begin_ + (reinterpret_cast<uintptr_t>(addr) >> kCardShift);
   // Sanity check the caller was asking for address covered by the card table
   DCHECK(IsValidCard(card_addr)) << "addr: " << addr
       << " card_addr: " << reinterpret_cast<void*>(card_addr);
   return card_addr;
 }
 
-inline void CardTable::CheckCardValid(byte* card) const {
+inline void CardTable::CheckCardValid(uint8_t* card) const {
   DCHECK(IsValidCard(card))
       << " card_addr: " << reinterpret_cast<const void*>(card)
       << " begin: " << reinterpret_cast<void*>(mem_map_->Begin() + offset_)
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index 0498550..b7b6099 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -55,7 +55,7 @@
  * byte is equal to GC_DIRTY_CARD. See CardTable::Create for details.
  */
 
-CardTable* CardTable::Create(const byte* heap_begin, size_t heap_capacity) {
+CardTable* CardTable::Create(const uint8_t* heap_begin, size_t heap_capacity) {
   /* Set up the card table */
   size_t capacity = heap_capacity / kCardSize;
   /* Allocate an extra 256 bytes to allow fixed low-byte of base */
@@ -66,15 +66,15 @@
   CHECK(mem_map.get() != NULL) << "couldn't allocate card table: " << error_msg;
   // All zeros is the correct initial value; all clean. Anonymous mmaps are initialized to zero, we
   // don't clear the card table to avoid unnecessary pages being allocated
-  COMPILE_ASSERT(kCardClean == 0, card_clean_must_be_0);
+  static_assert(kCardClean == 0, "kCardClean must be 0");
 
-  byte* cardtable_begin = mem_map->Begin();
+  uint8_t* cardtable_begin = mem_map->Begin();
   CHECK(cardtable_begin != NULL);
 
   // We allocated up to a bytes worth of extra space to allow biased_begin's byte value to equal
   // kCardDirty, compute a offset value to make this the case
   size_t offset = 0;
-  byte* biased_begin = reinterpret_cast<byte*>(reinterpret_cast<uintptr_t>(cardtable_begin) -
+  uint8_t* biased_begin = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(cardtable_begin) -
       (reinterpret_cast<uintptr_t>(heap_begin) >> kCardShift));
   uintptr_t biased_byte = reinterpret_cast<uintptr_t>(biased_begin) & 0xff;
   if (biased_byte != kCardDirty) {
@@ -86,19 +86,19 @@
   return new CardTable(mem_map.release(), biased_begin, offset);
 }
 
-CardTable::CardTable(MemMap* mem_map, byte* biased_begin, size_t offset)
+CardTable::CardTable(MemMap* mem_map, uint8_t* biased_begin, size_t offset)
     : mem_map_(mem_map), biased_begin_(biased_begin), offset_(offset) {
 }
 
 void CardTable::ClearSpaceCards(space::ContinuousSpace* space) {
   // TODO: clear just the range of the table that has been modified
-  byte* card_start = CardFromAddr(space->Begin());
-  byte* card_end = CardFromAddr(space->End());  // Make sure to round up.
+  uint8_t* card_start = CardFromAddr(space->Begin());
+  uint8_t* card_end = CardFromAddr(space->End());  // Make sure to round up.
   memset(reinterpret_cast<void*>(card_start), kCardClean, card_end - card_start);
 }
 
 void CardTable::ClearCardTable() {
-  COMPILE_ASSERT(kCardClean == 0, clean_card_must_be_0);
+  static_assert(kCardClean == 0, "kCardClean must be 0");
   mem_map_->MadviseDontNeedAndZero();
 }
 
@@ -106,10 +106,10 @@
   return IsValidCard(biased_begin_ + ((uintptr_t)addr >> kCardShift));
 }
 
-void CardTable::CheckAddrIsInCardTable(const byte* addr) const {
-  byte* card_addr = biased_begin_ + ((uintptr_t)addr >> kCardShift);
-  byte* begin = mem_map_->Begin() + offset_;
-  byte* end = mem_map_->End();
+void CardTable::CheckAddrIsInCardTable(const uint8_t* addr) const {
+  uint8_t* card_addr = biased_begin_ + ((uintptr_t)addr >> kCardShift);
+  uint8_t* begin = mem_map_->Begin() + offset_;
+  uint8_t* end = mem_map_->End();
   CHECK(AddrIsInCardTable(addr))
       << "Card table " << this
       << " begin: " << reinterpret_cast<void*>(begin)
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index 6d44d89..9bd3fba 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -51,7 +51,7 @@
   static constexpr uint8_t kCardClean = 0x0;
   static constexpr uint8_t kCardDirty = 0x70;
 
-  static CardTable* Create(const byte* heap_begin, size_t heap_capacity);
+  static CardTable* Create(const uint8_t* heap_begin, size_t heap_capacity);
 
   // Set the card associated with the given address to GC_CARD_DIRTY.
   ALWAYS_INLINE void MarkCard(const void *addr) {
@@ -64,16 +64,16 @@
   }
 
   // Return the state of the card at an address.
-  byte GetCard(const mirror::Object* obj) const {
+  uint8_t GetCard(const mirror::Object* obj) const {
     return *CardFromAddr(obj);
   }
 
   // Visit and clear cards within memory range, only visits dirty cards.
   template <typename Visitor>
   void VisitClear(const void* start, const void* end, const Visitor& visitor) {
-    byte* card_start = CardFromAddr(start);
-    byte* card_end = CardFromAddr(end);
-    for (byte* it = card_start; it != card_end; ++it) {
+    uint8_t* card_start = CardFromAddr(start);
+    uint8_t* card_end = CardFromAddr(end);
+    for (uint8_t* it = card_start; it != card_end; ++it) {
       if (*it == kCardDirty) {
         *it = kCardClean;
         visitor(it);
@@ -83,7 +83,7 @@
 
   // Returns a value that when added to a heap address >> GC_CARD_SHIFT will address the appropriate
   // card table byte. For convenience this value is cached in every Thread
-  byte* GetBiasedBegin() const {
+  uint8_t* GetBiasedBegin() const {
     return biased_begin_;
   }
 
@@ -96,20 +96,20 @@
    * us to know which cards got cleared.
    */
   template <typename Visitor, typename ModifiedVisitor>
-  void ModifyCardsAtomic(byte* scan_begin, byte* scan_end, const Visitor& visitor,
+  void ModifyCardsAtomic(uint8_t* scan_begin, uint8_t* scan_end, const Visitor& visitor,
                          const ModifiedVisitor& modified);
 
   // For every dirty at least minumum age between begin and end invoke the visitor with the
   // specified argument. Returns how many cards the visitor was run on.
   template <typename Visitor>
-  size_t Scan(SpaceBitmap<kObjectAlignment>* bitmap, byte* scan_begin, byte* scan_end,
+  size_t Scan(SpaceBitmap<kObjectAlignment>* bitmap, uint8_t* scan_begin, uint8_t* scan_end,
               const Visitor& visitor,
-              const byte minimum_age = kCardDirty) const
+              const uint8_t minimum_age = kCardDirty) const
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Assertion used to check the given address is covered by the card table
-  void CheckAddrIsInCardTable(const byte* addr) const;
+  void CheckAddrIsInCardTable(const uint8_t* addr) const;
 
   // Resets all of the bytes in the card table to clean.
   void ClearCardTable();
@@ -118,24 +118,24 @@
   void ClearSpaceCards(space::ContinuousSpace* space);
 
   // Returns the first address in the heap which maps to this card.
-  void* AddrFromCard(const byte *card_addr) const ALWAYS_INLINE;
+  void* AddrFromCard(const uint8_t *card_addr) const ALWAYS_INLINE;
 
   // Returns the address of the relevant byte in the card table, given an address on the heap.
-  byte* CardFromAddr(const void *addr) const ALWAYS_INLINE;
+  uint8_t* CardFromAddr(const void *addr) const ALWAYS_INLINE;
 
   bool AddrIsInCardTable(const void* addr) const;
 
  private:
-  CardTable(MemMap* begin, byte* biased_begin, size_t offset);
+  CardTable(MemMap* begin, uint8_t* biased_begin, size_t offset);
 
   // Returns true iff the card table address is within the bounds of the card table.
-  bool IsValidCard(const byte* card_addr) const {
-    byte* begin = mem_map_->Begin() + offset_;
-    byte* end = mem_map_->End();
+  bool IsValidCard(const uint8_t* card_addr) const {
+    uint8_t* begin = mem_map_->Begin() + offset_;
+    uint8_t* end = mem_map_->End();
     return card_addr >= begin && card_addr < end;
   }
 
-  void CheckCardValid(byte* card) const ALWAYS_INLINE;
+  void CheckCardValid(uint8_t* card) const ALWAYS_INLINE;
 
   // Verifies that all gray objects are on a dirty card.
   void VerifyCardTable();
@@ -143,7 +143,7 @@
   // Mmapped pages for the card table
   std::unique_ptr<MemMap> mem_map_;
   // Value used to compute card table addresses from object addresses, see GetBiasedBegin
-  byte* const biased_begin_;
+  uint8_t* const biased_begin_;
   // Card table doesn't begin at the beginning of the mem_map_, instead it is displaced by offset
   // to allow the byte value of biased_begin_ to equal GC_CARD_DIRTY
   const size_t offset_;
diff --git a/runtime/gc/accounting/card_table_test.cc b/runtime/gc/accounting/card_table_test.cc
index a88b2c9..819cb85 100644
--- a/runtime/gc/accounting/card_table_test.cc
+++ b/runtime/gc/accounting/card_table_test.cc
@@ -33,103 +33,109 @@
   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();
     }
   }
   // Default values for the test, not random to avoid undeterministic behaviour.
-  CardTableTest() : heap_begin_(reinterpret_cast<byte*>(0x2000000)), heap_size_(2 * MB) {
+  CardTableTest() : heap_begin_(reinterpret_cast<uint8_t*>(0x2000000)), heap_size_(2 * MB) {
   }
   void ClearCardTable() {
     card_table_->ClearCardTable();
   }
-  byte* HeapBegin() const {
+  uint8_t* HeapBegin() const {
     return heap_begin_;
   }
-  byte* HeapLimit() const {
+  uint8_t* 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.
+  uint8_t PseudoRandomCard(const uint8_t* 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 uint8_t* addr = HeapBegin(); addr != HeapLimit(); addr += CardTable::kCardSize) {
       EXPECT_TRUE(card_table_->AddrIsInCardTable(addr));
-      byte* card = card_table_->CardFromAddr(addr);
-      *card = PRandCard(addr);
+      uint8_t* card = card_table_->CardFromAddr(addr);
+      *card = PseudoRandomCard(addr);
     }
   }
 
  private:
-  byte* const heap_begin_;
+  uint8_t* const heap_begin_;
   const size_t heap_size_;
 };
 
 TEST_F(CardTableTest, TestMarkCard) {
   CommonSetup();
-  for (const byte* addr = HeapBegin(); addr < HeapLimit(); addr += kObjectAlignment) {
+  for (const uint8_t* 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);
-    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_table_->GetCard(obj), CardTable::kCardDirty);
+    uint8_t* card_addr = card_table_->CardFromAddr(addr);
+    EXPECT_EQ(*card_addr, CardTable::kCardDirty);
+    *card_addr = CardTable::kCardClean;
+    EXPECT_EQ(*card_addr, CardTable::kCardClean);
   }
 }
 
 class UpdateVisitor {
  public:
-  byte operator()(byte c) const {
+  uint8_t operator()(uint8_t c) const {
     return c * 93 + 123;
   }
-  void operator()(byte* /*card*/, byte /*expected_value*/, byte /*new_value*/) const {
+  void operator()(uint8_t* /*card*/, uint8_t /*expected_value*/, uint8_t /*new_value*/) const {
   }
 };
 
 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 (uint8_t* 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 (uint8_t* 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;
+      uint8_t* start = cstart + start_offset;
+      uint8_t* end = cend - end_offset;
+      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 (uint8_t* 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 (uint8_t* 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) {
-        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);
-        }
+      for (uint8_t* cur = start; cur < AlignUp(end, CardTable::kCardSize);
+          cur += CardTable::kCardSize) {
+        uint8_t* card = card_table_->CardFromAddr(cur);
+        uint8_t 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/heap_bitmap-inl.h b/runtime/gc/accounting/heap_bitmap-inl.h
index c67542f..34c15c7 100644
--- a/runtime/gc/accounting/heap_bitmap-inl.h
+++ b/runtime/gc/accounting/heap_bitmap-inl.h
@@ -40,9 +40,9 @@
   if (LIKELY(bitmap != nullptr)) {
     return bitmap->Test(obj);
   }
-  for (const auto& bitmap : large_object_bitmaps_) {
-    if (LIKELY(bitmap->HasAddress(obj))) {
-      return bitmap->Test(obj);
+  for (const auto& lo_bitmap : large_object_bitmaps_) {
+    if (LIKELY(lo_bitmap->HasAddress(obj))) {
+      return lo_bitmap->Test(obj);
     }
   }
   LOG(FATAL) << "Invalid object " << obj;
@@ -55,9 +55,9 @@
     bitmap->Clear(obj);
     return;
   }
-  for (const auto& bitmap : large_object_bitmaps_) {
-    if (LIKELY(bitmap->HasAddress(obj))) {
-      bitmap->Clear(obj);
+  for (const auto& lo_bitmap : large_object_bitmaps_) {
+    if (LIKELY(lo_bitmap->HasAddress(obj))) {
+      lo_bitmap->Clear(obj);
     }
   }
   LOG(FATAL) << "Invalid object " << obj;
@@ -70,9 +70,9 @@
     return bitmap->Set(obj);
   }
   visitor(obj);
-  for (const auto& bitmap : large_object_bitmaps_) {
-    if (LIKELY(bitmap->HasAddress(obj))) {
-      return bitmap->Set(obj);
+  for (const auto& lo_bitmap : large_object_bitmaps_) {
+    if (LIKELY(lo_bitmap->HasAddress(obj))) {
+      return lo_bitmap->Set(obj);
     }
   }
   LOG(FATAL) << "Invalid object " << obj;
@@ -87,9 +87,9 @@
     return bitmap->AtomicTestAndSet(obj);
   }
   visitor(obj);
-  for (const auto& bitmap : large_object_bitmaps_) {
-    if (LIKELY(bitmap->HasAddress(obj))) {
-      return bitmap->AtomicTestAndSet(obj);
+  for (const auto& lo_bitmap : large_object_bitmaps_) {
+    if (LIKELY(lo_bitmap->HasAddress(obj))) {
+      return lo_bitmap->AtomicTestAndSet(obj);
     }
   }
   LOG(FATAL) << "Invalid object " << obj;
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 2686af0..0a15e9e 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -45,7 +45,8 @@
     : cleared_cards_(cleared_cards) {
   }
 
-  inline void operator()(byte* card, byte expected_value, byte new_value) const {
+  inline void operator()(uint8_t* card, uint8_t expected_value, uint8_t new_value) const {
+    UNUSED(new_value);
     if (expected_value == CardTable::kCardDirty) {
       cleared_cards_->insert(card);
     }
@@ -57,24 +58,27 @@
 
 class ModUnionClearCardVisitor {
  public:
-  explicit ModUnionClearCardVisitor(std::vector<byte*>* cleared_cards)
+  explicit ModUnionClearCardVisitor(std::vector<uint8_t*>* cleared_cards)
     : cleared_cards_(cleared_cards) {
   }
 
-  void operator()(byte* card, byte expected_card, byte new_card) const {
+  void operator()(uint8_t* card, uint8_t expected_card, uint8_t new_card) const {
+    UNUSED(new_card);
     if (expected_card == CardTable::kCardDirty) {
       cleared_cards_->push_back(card);
     }
   }
  private:
-  std::vector<byte*>* const cleared_cards_;
+  std::vector<uint8_t*>* const cleared_cards_;
 };
 
 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 +86,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 +96,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() {
@@ -226,7 +244,7 @@
   CardTable* card_table = heap_->GetCardTable();
   ContinuousSpaceBitmap* live_bitmap = space_->GetLiveBitmap();
   for (const auto& ref_pair : references_) {
-    const byte* card = ref_pair.first;
+    const uint8_t* card = ref_pair.first;
     if (*card == CardTable::kCardClean) {
       std::set<const Object*> reference_set;
       for (mirror::HeapReference<Object>* obj_ptr : ref_pair.second) {
@@ -242,14 +260,14 @@
 void ModUnionTableReferenceCache::Dump(std::ostream& os) {
   CardTable* card_table = heap_->GetCardTable();
   os << "ModUnionTable cleared cards: [";
-  for (byte* card_addr : cleared_cards_) {
+  for (uint8_t* card_addr : cleared_cards_) {
     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
     uintptr_t end = start + CardTable::kCardSize;
     os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << ",";
   }
   os << "]\nModUnionTable references: [";
   for (const auto& ref_pair : references_) {
-    const byte* card_addr = ref_pair.first;
+    const uint8_t* card_addr = ref_pair.first;
     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
     uintptr_t end = start + CardTable::kCardSize;
     os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << "->{";
@@ -313,19 +331,27 @@
 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;
+    }
   }
 }
 
 void ModUnionTableCardCache::Dump(std::ostream& os) {
   CardTable* card_table = heap_->GetCardTable();
   os << "ModUnionTable dirty cards: [";
-  for (const byte* card_addr : cleared_cards_) {
+  for (const uint8_t* card_addr : cleared_cards_) {
     auto start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
     auto end = start + CardTable::kCardSize;
     os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << "\n";
@@ -333,6 +359,17 @@
   os << "]";
 }
 
+void ModUnionTableCardCache::SetCards() {
+  CardTable* card_table = heap_->GetCardTable();
+  for (uint8_t* 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..d6342cf 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -50,8 +50,8 @@
 // cleared between GC phases, reducing the number of dirty cards that need to be scanned.
 class ModUnionTable {
  public:
-  typedef std::set<byte*, std::less<byte*>,
-                   TrackingAllocator<byte*, kAllocatorTagModUnionCardSet>> CardSet;
+  typedef std::set<uint8_t*, std::less<uint8_t*>,
+                   TrackingAllocator<uint8_t*, kAllocatorTagModUnionCardSet>> CardSet;
 
   explicit ModUnionTable(const std::string& name, Heap* heap, space::ContinuousSpace* space)
       : name_(name),
@@ -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,12 +124,14 @@
 
   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_;
 
   // Maps from dirty cards to their corresponding alloc space references.
-  AllocationTrackingSafeMap<const byte*, std::vector<mirror::HeapReference<mirror::Object>*>,
+  AllocationTrackingSafeMap<const uint8_t*, std::vector<mirror::HeapReference<mirror::Object>*>,
                             kAllocatorTagModUnionReferenceArray> references_;
 };
 
@@ -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/accounting/remembered_set.cc b/runtime/gc/accounting/remembered_set.cc
index 3ff5874..b16a146 100644
--- a/runtime/gc/accounting/remembered_set.cc
+++ b/runtime/gc/accounting/remembered_set.cc
@@ -42,7 +42,8 @@
   explicit RememberedSetCardVisitor(RememberedSet::CardSet* const dirty_cards)
       : dirty_cards_(dirty_cards) {}
 
-  void operator()(byte* card, byte expected_value, byte new_value) const {
+  void operator()(uint8_t* card, uint8_t expected_value, uint8_t new_value) const {
+    UNUSED(new_value);
     if (expected_value == CardTable::kCardDirty) {
       dirty_cards_->insert(card);
     }
@@ -129,7 +130,7 @@
                                          &contains_reference_to_target_space, arg);
   ContinuousSpaceBitmap* bitmap = space_->GetLiveBitmap();
   CardSet remove_card_set;
-  for (byte* const card_addr : dirty_cards_) {
+  for (uint8_t* const card_addr : dirty_cards_) {
     contains_reference_to_target_space = false;
     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
     DCHECK(space_->HasAddress(reinterpret_cast<mirror::Object*>(start)));
@@ -145,7 +146,7 @@
 
   // Remove the cards that didn't contain a reference to the target
   // space from the dirty card set.
-  for (byte* const card_addr : remove_card_set) {
+  for (uint8_t* const card_addr : remove_card_set) {
     DCHECK(dirty_cards_.find(card_addr) != dirty_cards_.end());
     dirty_cards_.erase(card_addr);
   }
@@ -154,7 +155,7 @@
 void RememberedSet::Dump(std::ostream& os) {
   CardTable* card_table = heap_->GetCardTable();
   os << "RememberedSet dirty cards: [";
-  for (const byte* card_addr : dirty_cards_) {
+  for (const uint8_t* card_addr : dirty_cards_) {
     auto start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
     auto end = start + CardTable::kCardSize;
     os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << "\n";
@@ -164,8 +165,8 @@
 
 void RememberedSet::AssertAllDirtyCardsAreWithinSpace() const {
   CardTable* card_table = heap_->GetCardTable();
-  for (const byte* card_addr : dirty_cards_) {
-    auto start = reinterpret_cast<byte*>(card_table->AddrFromCard(card_addr));
+  for (const uint8_t* card_addr : dirty_cards_) {
+    auto start = reinterpret_cast<uint8_t*>(card_table->AddrFromCard(card_addr));
     auto end = start + CardTable::kCardSize;
     DCHECK_LE(space_->Begin(), start);
     DCHECK_LE(end, space_->Limit());
diff --git a/runtime/gc/accounting/remembered_set.h b/runtime/gc/accounting/remembered_set.h
index 8d66e0e..c51e26d 100644
--- a/runtime/gc/accounting/remembered_set.h
+++ b/runtime/gc/accounting/remembered_set.h
@@ -43,8 +43,8 @@
 // from the free list spaces to the bump pointer spaces.
 class RememberedSet {
  public:
-  typedef std::set<byte*, std::less<byte*>,
-                   TrackingAllocator<byte*, kAllocatorTagRememberedSet>> CardSet;
+  typedef std::set<uint8_t*, std::less<uint8_t*>,
+                   TrackingAllocator<uint8_t*, kAllocatorTagRememberedSet>> CardSet;
 
   explicit RememberedSet(const std::string& name, Heap* heap, space::ContinuousSpace* space)
       : name_(name), heap_(heap), space_(space) {}
diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h
index fc4213e..11347a5 100644
--- a/runtime/gc/accounting/space_bitmap-inl.h
+++ b/runtime/gc/accounting/space_bitmap-inl.h
@@ -35,10 +35,10 @@
   DCHECK_GE(addr, heap_begin_);
   const uintptr_t offset = addr - heap_begin_;
   const size_t index = OffsetToIndex(offset);
-  const uword mask = OffsetToMask(offset);
-  Atomic<uword>* atomic_entry = reinterpret_cast<Atomic<uword>*>(&bitmap_begin_[index]);
-  DCHECK_LT(index, bitmap_size_ / kWordSize) << " bitmap_size_ = " << bitmap_size_;
-  uword old_word;
+  const uintptr_t mask = OffsetToMask(offset);
+  Atomic<uintptr_t>* atomic_entry = reinterpret_cast<Atomic<uintptr_t>*>(&bitmap_begin_[index]);
+  DCHECK_LT(index, bitmap_size_ / sizeof(intptr_t)) << " bitmap_size_ = " << bitmap_size_;
+  uintptr_t old_word;
   do {
     old_word = atomic_entry->LoadRelaxed();
     // Fast path: The bit is already set.
@@ -82,8 +82,8 @@
   const uintptr_t index_start = OffsetToIndex(offset_start);
   const uintptr_t index_end = OffsetToIndex(offset_end);
 
-  const size_t bit_start = (offset_start / kAlignment) % kBitsPerWord;
-  const size_t bit_end = (offset_end / kAlignment) % kBitsPerWord;
+  const size_t bit_start = (offset_start / kAlignment) % kBitsPerIntPtrT;
+  const size_t bit_end = (offset_end / kAlignment) % kBitsPerIntPtrT;
 
   // Index(begin)  ...    Index(end)
   // [xxxxx???][........][????yyyy]
@@ -93,12 +93,12 @@
   //
 
   // Left edge.
-  uword left_edge = bitmap_begin_[index_start];
+  uintptr_t left_edge = bitmap_begin_[index_start];
   // Mark of lower bits that are not in range.
-  left_edge &= ~((static_cast<uword>(1) << bit_start) - 1);
+  left_edge &= ~((static_cast<uintptr_t>(1) << bit_start) - 1);
 
   // Right edge. Either unique, or left_edge.
-  uword right_edge;
+  uintptr_t right_edge;
 
   if (index_start < index_end) {
     // Left edge != right edge.
@@ -110,20 +110,20 @@
         const size_t shift = CTZ(left_edge);
         mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
         visitor(obj);
-        left_edge ^= (static_cast<uword>(1)) << shift;
+        left_edge ^= (static_cast<uintptr_t>(1)) << shift;
       } while (left_edge != 0);
     }
 
     // Traverse the middle, full part.
     for (size_t i = index_start + 1; i < index_end; ++i) {
-      uword w = bitmap_begin_[i];
+      uintptr_t w = bitmap_begin_[i];
       if (w != 0) {
         const uintptr_t ptr_base = IndexToOffset(i) + heap_begin_;
         do {
           const size_t shift = CTZ(w);
           mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
           visitor(obj);
-          w ^= (static_cast<uword>(1)) << shift;
+          w ^= (static_cast<uintptr_t>(1)) << shift;
         } while (w != 0);
       }
     }
@@ -142,14 +142,14 @@
   }
 
   // Right edge handling.
-  right_edge &= ((static_cast<uword>(1) << bit_end) - 1);
+  right_edge &= ((static_cast<uintptr_t>(1) << bit_end) - 1);
   if (right_edge != 0) {
     const uintptr_t ptr_base = IndexToOffset(index_end) + heap_begin_;
     do {
       const size_t shift = CTZ(right_edge);
       mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
       visitor(obj);
-      right_edge ^= (static_cast<uword>(1)) << shift;
+      right_edge ^= (static_cast<uintptr_t>(1)) << shift;
     } while (right_edge != 0);
   }
 #endif
@@ -161,10 +161,10 @@
   DCHECK_GE(addr, heap_begin_);
   const uintptr_t offset = addr - heap_begin_;
   const size_t index = OffsetToIndex(offset);
-  const uword mask = OffsetToMask(offset);
-  DCHECK_LT(index, bitmap_size_ / kWordSize) << " bitmap_size_ = " << bitmap_size_;
-  uword* address = &bitmap_begin_[index];
-  uword old_word = *address;
+  const uintptr_t mask = OffsetToMask(offset);
+  DCHECK_LT(index, bitmap_size_ / sizeof(intptr_t)) << " bitmap_size_ = " << bitmap_size_;
+  uintptr_t* address = &bitmap_begin_[index];
+  uintptr_t old_word = *address;
   if (kSetBit) {
     *address = old_word | mask;
   } else {
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index 39d1f9e..feb9565 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -29,21 +29,21 @@
 
 template<size_t kAlignment>
 size_t SpaceBitmap<kAlignment>::ComputeBitmapSize(uint64_t capacity) {
-  const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerWord;
-  return (RoundUp(capacity, kBytesCoveredPerWord) / kBytesCoveredPerWord) * kWordSize;
+  const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerIntPtrT;
+  return (RoundUp(capacity, kBytesCoveredPerWord) / kBytesCoveredPerWord) * sizeof(intptr_t);
 }
 
 template<size_t kAlignment>
 SpaceBitmap<kAlignment>* SpaceBitmap<kAlignment>::CreateFromMemMap(
-    const std::string& name, MemMap* mem_map, byte* heap_begin, size_t heap_capacity) {
+    const std::string& name, MemMap* mem_map, uint8_t* heap_begin, size_t heap_capacity) {
   CHECK(mem_map != nullptr);
-  uword* bitmap_begin = reinterpret_cast<uword*>(mem_map->Begin());
+  uintptr_t* bitmap_begin = reinterpret_cast<uintptr_t*>(mem_map->Begin());
   const size_t bitmap_size = ComputeBitmapSize(heap_capacity);
   return new SpaceBitmap(name, mem_map, bitmap_begin, bitmap_size, heap_begin);
 }
 
 template<size_t kAlignment>
-SpaceBitmap<kAlignment>::SpaceBitmap(const std::string& name, MemMap* mem_map, uword* bitmap_begin,
+SpaceBitmap<kAlignment>::SpaceBitmap(const std::string& name, MemMap* mem_map, uintptr_t* bitmap_begin,
                                      size_t bitmap_size, const void* heap_begin)
     : mem_map_(mem_map), bitmap_begin_(bitmap_begin), bitmap_size_(bitmap_size),
       heap_begin_(reinterpret_cast<uintptr_t>(heap_begin)),
@@ -57,7 +57,7 @@
 
 template<size_t kAlignment>
 SpaceBitmap<kAlignment>* SpaceBitmap<kAlignment>::Create(
-    const std::string& name, byte* heap_begin, size_t heap_capacity) {
+    const std::string& name, uint8_t* heap_begin, size_t heap_capacity) {
   // Round up since heap_capacity is not necessarily a multiple of kAlignment * kBitsPerWord.
   const size_t bitmap_size = ComputeBitmapSize(heap_capacity);
   std::string error_msg;
@@ -72,8 +72,8 @@
 
 template<size_t kAlignment>
 void SpaceBitmap<kAlignment>::SetHeapLimit(uintptr_t new_end) {
-  DCHECK(IsAligned<kBitsPerWord * kAlignment>(new_end));
-  size_t new_size = OffsetToIndex(new_end - heap_begin_) * kWordSize;
+  DCHECK(IsAligned<kBitsPerIntPtrT * kAlignment>(new_end));
+  size_t new_size = OffsetToIndex(new_end - heap_begin_) * sizeof(intptr_t);
   if (new_size < bitmap_size_) {
     bitmap_size_ = new_size;
   }
@@ -97,7 +97,7 @@
 template<size_t kAlignment>
 void SpaceBitmap<kAlignment>::CopyFrom(SpaceBitmap* source_bitmap) {
   DCHECK_EQ(Size(), source_bitmap->Size());
-  std::copy(source_bitmap->Begin(), source_bitmap->Begin() + source_bitmap->Size() / kWordSize, Begin());
+  std::copy(source_bitmap->Begin(), source_bitmap->Begin() + source_bitmap->Size() / sizeof(intptr_t), Begin());
 }
 
 template<size_t kAlignment>
@@ -106,16 +106,16 @@
   CHECK(callback != NULL);
 
   uintptr_t end = OffsetToIndex(HeapLimit() - heap_begin_ - 1);
-  uword* bitmap_begin = bitmap_begin_;
+  uintptr_t* bitmap_begin = bitmap_begin_;
   for (uintptr_t i = 0; i <= end; ++i) {
-    uword w = bitmap_begin[i];
+    uintptr_t w = bitmap_begin[i];
     if (w != 0) {
       uintptr_t ptr_base = IndexToOffset(i) + heap_begin_;
       do {
         const size_t shift = CTZ(w);
         mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
         (*callback)(obj, arg);
-        w ^= (static_cast<uword>(1)) << shift;
+        w ^= (static_cast<uintptr_t>(1)) << shift;
       } while (w != 0);
     }
   }
@@ -139,7 +139,7 @@
   }
 
   // TODO: rewrite the callbacks to accept a std::vector<mirror::Object*> rather than a mirror::Object**?
-  constexpr size_t buffer_size = kWordSize * kBitsPerWord;
+  constexpr size_t buffer_size = sizeof(intptr_t) * kBitsPerIntPtrT;
 #ifdef __LP64__
   // Heap-allocate for smaller stack frame.
   std::unique_ptr<mirror::Object*[]> pointer_buf_ptr(new mirror::Object*[buffer_size]);
@@ -152,21 +152,21 @@
 
   size_t start = OffsetToIndex(sweep_begin - live_bitmap.heap_begin_);
   size_t end = OffsetToIndex(sweep_end - live_bitmap.heap_begin_ - 1);
-  CHECK_LT(end, live_bitmap.Size() / kWordSize);
-  uword* live = live_bitmap.bitmap_begin_;
-  uword* mark = mark_bitmap.bitmap_begin_;
+  CHECK_LT(end, live_bitmap.Size() / sizeof(intptr_t));
+  uintptr_t* live = live_bitmap.bitmap_begin_;
+  uintptr_t* mark = mark_bitmap.bitmap_begin_;
   for (size_t i = start; i <= end; i++) {
-    uword garbage = live[i] & ~mark[i];
+    uintptr_t garbage = live[i] & ~mark[i];
     if (UNLIKELY(garbage != 0)) {
       uintptr_t ptr_base = IndexToOffset(i) + live_bitmap.heap_begin_;
       do {
         const size_t shift = CTZ(garbage);
-        garbage ^= (static_cast<uword>(1)) << shift;
+        garbage ^= (static_cast<uintptr_t>(1)) << shift;
         *pb++ = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
       } while (garbage != 0);
       // Make sure that there are always enough slots available for an
       // entire word of one bits.
-      if (pb >= &pointer_buf[buffer_size - kBitsPerWord]) {
+      if (pb >= &pointer_buf[buffer_size - kBitsPerIntPtrT]) {
         (*callback)(pb - &pointer_buf[0], &pointer_buf[0], arg);
         pb = &pointer_buf[0];
       }
@@ -245,21 +245,21 @@
 template<size_t kAlignment>
 void SpaceBitmap<kAlignment>::InOrderWalk(ObjectCallback* callback, void* arg) {
   std::unique_ptr<SpaceBitmap<kAlignment>> visited(
-      Create("bitmap for in-order walk", reinterpret_cast<byte*>(heap_begin_),
-             IndexToOffset(bitmap_size_ / kWordSize)));
+      Create("bitmap for in-order walk", reinterpret_cast<uint8_t*>(heap_begin_),
+             IndexToOffset(bitmap_size_ / sizeof(intptr_t))));
   CHECK(bitmap_begin_ != nullptr);
   CHECK(callback != nullptr);
-  uintptr_t end = Size() / kWordSize;
+  uintptr_t end = Size() / sizeof(intptr_t);
   for (uintptr_t i = 0; i < end; ++i) {
     // Need uint for unsigned shift.
-    uword w = bitmap_begin_[i];
+    uintptr_t w = bitmap_begin_[i];
     if (UNLIKELY(w != 0)) {
       uintptr_t ptr_base = IndexToOffset(i) + heap_begin_;
       while (w != 0) {
         const size_t shift = CTZ(w);
         mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
         WalkFieldsInOrder(visited.get(), callback, obj, arg);
-        w ^= (static_cast<uword>(1)) << shift;
+        w ^= (static_cast<uintptr_t>(1)) << shift;
       }
     }
   }
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index f72b30f..e73166b 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -45,13 +45,13 @@
 
   // Initialize a space bitmap so that it points to a bitmap large enough to cover a heap at
   // heap_begin of heap_capacity bytes, where objects are guaranteed to be kAlignment-aligned.
-  static SpaceBitmap* Create(const std::string& name, byte* heap_begin, size_t heap_capacity);
+  static SpaceBitmap* Create(const std::string& name, uint8_t* heap_begin, size_t heap_capacity);
 
   // Initialize a space bitmap using the provided mem_map as the live bits. Takes ownership of the
   // mem map. The address range covered starts at heap_begin and is of size equal to heap_capacity.
   // Objects are kAlignement-aligned.
   static SpaceBitmap* CreateFromMemMap(const std::string& name, MemMap* mem_map,
-                                       byte* heap_begin, size_t heap_capacity);
+                                       uint8_t* heap_begin, size_t heap_capacity);
 
   ~SpaceBitmap();
 
@@ -59,17 +59,17 @@
   // <index> is the index of .bits that contains the bit representing
   //         <offset>.
   static constexpr size_t OffsetToIndex(size_t offset) {
-    return offset / kAlignment / kBitsPerWord;
+    return offset / kAlignment / kBitsPerIntPtrT;
   }
 
   template<typename T>
   static constexpr T IndexToOffset(T index) {
-    return static_cast<T>(index * kAlignment * kBitsPerWord);
+    return static_cast<T>(index * kAlignment * kBitsPerIntPtrT);
   }
 
   // Bits are packed in the obvious way.
-  static constexpr uword OffsetToMask(uintptr_t offset) {
-    return (static_cast<size_t>(1)) << ((offset / kAlignment) % kBitsPerWord);
+  static constexpr uintptr_t OffsetToMask(uintptr_t offset) {
+    return (static_cast<size_t>(1)) << ((offset / kAlignment) % kBitsPerIntPtrT);
   }
 
   bool Set(const mirror::Object* obj) ALWAYS_INLINE {
@@ -95,7 +95,7 @@
     // bitmap.
     const uintptr_t offset = reinterpret_cast<uintptr_t>(obj) - heap_begin_;
     const size_t index = OffsetToIndex(offset);
-    return index < bitmap_size_ / kWordSize;
+    return index < bitmap_size_ / sizeof(intptr_t);
   }
 
   void VisitRange(uintptr_t base, uintptr_t max, ObjectCallback* callback, void* arg) const;
@@ -146,7 +146,7 @@
   void CopyFrom(SpaceBitmap* source_bitmap);
 
   // Starting address of our internal storage.
-  uword* Begin() {
+  uintptr_t* Begin() {
     return bitmap_begin_;
   }
 
@@ -157,7 +157,7 @@
 
   // Size in bytes of the memory that the bitmaps spans.
   uint64_t HeapSize() const {
-    return IndexToOffset<uint64_t>(Size() / kWordSize);
+    return IndexToOffset<uint64_t>(Size() / sizeof(intptr_t));
   }
 
   uintptr_t HeapBegin() const {
@@ -192,7 +192,7 @@
  private:
   // TODO: heap_end_ is initialized so that the heap bitmap is empty, this doesn't require the -1,
   // however, we document that this is expected on heap_end_
-  SpaceBitmap(const std::string& name, MemMap* mem_map, uword* bitmap_begin, size_t bitmap_size,
+  SpaceBitmap(const std::string& name, MemMap* mem_map, uintptr_t* bitmap_begin, size_t bitmap_size,
               const void* heap_begin);
 
   // Helper function for computing bitmap size based on a 64 bit capacity.
@@ -214,7 +214,7 @@
   std::unique_ptr<MemMap> mem_map_;
 
   // This bitmap itself, word sized for efficiency in scanning.
-  uword* const bitmap_begin_;
+  uintptr_t* const bitmap_begin_;
 
   // Size of this bitmap.
   size_t bitmap_size_;
diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc
index a30bb25..850325a 100644
--- a/runtime/gc/accounting/space_bitmap_test.cc
+++ b/runtime/gc/accounting/space_bitmap_test.cc
@@ -30,7 +30,7 @@
 class SpaceBitmapTest : public CommonRuntimeTest {};
 
 TEST_F(SpaceBitmapTest, Init) {
-  byte* heap_begin = reinterpret_cast<byte*>(0x10000000);
+  uint8_t* heap_begin = reinterpret_cast<uint8_t*>(0x10000000);
   size_t heap_capacity = 16 * MB;
   std::unique_ptr<ContinuousSpaceBitmap> space_bitmap(
       ContinuousSpaceBitmap::Create("test bitmap", heap_begin, heap_capacity));
@@ -51,21 +51,21 @@
     EXPECT_EQ(bitmap_->Test(obj), ((reinterpret_cast<uintptr_t>(obj) & 0xF) != 0));
   }
 
-  ContinuousSpaceBitmap* bitmap_;
+  ContinuousSpaceBitmap* const bitmap_;
   const mirror::Object* begin_;
   const mirror::Object* end_;
 };
 
 TEST_F(SpaceBitmapTest, ScanRange) {
-  byte* heap_begin = reinterpret_cast<byte*>(0x10000000);
+  uint8_t* heap_begin = reinterpret_cast<uint8_t*>(0x10000000);
   size_t heap_capacity = 16 * MB;
 
   std::unique_ptr<ContinuousSpaceBitmap> space_bitmap(
       ContinuousSpaceBitmap::Create("test bitmap", heap_begin, heap_capacity));
   EXPECT_TRUE(space_bitmap.get() != NULL);
 
-  // Set all the odd bits in the first BitsPerWord * 3 to one.
-  for (size_t j = 0; j < kBitsPerWord * 3; ++j) {
+  // Set all the odd bits in the first BitsPerIntPtrT * 3 to one.
+  for (size_t j = 0; j < kBitsPerIntPtrT * 3; ++j) {
     const mirror::Object* obj =
         reinterpret_cast<mirror::Object*>(heap_begin + j * kObjectAlignment);
     if (reinterpret_cast<uintptr_t>(obj) & 0xF) {
@@ -76,10 +76,10 @@
   // possible length up to a maximum of kBitsPerWord * 2 - 1 bits.
   // This handles all the cases, having runs which start and end on the same word, and different
   // words.
-  for (size_t i = 0; i < static_cast<size_t>(kBitsPerWord); ++i) {
+  for (size_t i = 0; i < static_cast<size_t>(kBitsPerIntPtrT); ++i) {
     mirror::Object* start =
         reinterpret_cast<mirror::Object*>(heap_begin + i * kObjectAlignment);
-    for (size_t j = 0; j < static_cast<size_t>(kBitsPerWord * 2); ++j) {
+    for (size_t j = 0; j < static_cast<size_t>(kBitsPerIntPtrT * 2); ++j) {
       mirror::Object* end =
           reinterpret_cast<mirror::Object*>(heap_begin + (i + j) * kObjectAlignment);
       BitmapVerify(space_bitmap.get(), start, end);
@@ -91,11 +91,11 @@
  public:
   explicit SimpleCounter(size_t* counter) : count_(counter) {}
 
-  void operator()(mirror::Object* obj) const {
+  void operator()(mirror::Object* obj ATTRIBUTE_UNUSED) const {
     (*count_)++;
   }
 
-  size_t* count_;
+  size_t* const count_;
 };
 
 class RandGen {
@@ -112,7 +112,7 @@
 
 template <size_t kAlignment>
 void RunTest() NO_THREAD_SAFETY_ANALYSIS {
-  byte* heap_begin = reinterpret_cast<byte*>(0x10000000);
+  uint8_t* heap_begin = reinterpret_cast<uint8_t*>(0x10000000);
   size_t heap_capacity = 16 * MB;
 
   // Seed with 0x1234 for reproducability.
diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc
index a6a3ee7..8558f96 100644
--- a/runtime/gc/allocator/dlmalloc.cc
+++ b/runtime/gc/allocator/dlmalloc.cc
@@ -19,8 +19,8 @@
 #include "base/logging.h"
 
 // ART specific morecore implementation defined in space.cc.
+static void* art_heap_morecore(void* m, intptr_t increment);
 #define MORECORE(x) art_heap_morecore(m, x)
-extern "C" void* art_heap_morecore(void* m, intptr_t increment);
 
 // Custom heap error handling.
 #define PROCEED_ON_ERROR 0
@@ -31,19 +31,24 @@
 
 // Ugly inclusion of C file so that ART specific #defines configure dlmalloc for our use for
 // mspaces (regular dlmalloc is still declared in bionic).
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
 #pragma GCC diagnostic ignored "-Wempty-body"
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #include "../../../bionic/libc/upstream-dlmalloc/malloc.c"
-#pragma GCC diagnostic warning "-Wstrict-aliasing"
-#pragma GCC diagnostic warning "-Wempty-body"
+#pragma GCC diagnostic pop
 
+static void* art_heap_morecore(void* m, intptr_t increment) {
+  return ::art::gc::allocator::ArtDlMallocMoreCore(m, increment);
+}
 
 static void art_heap_corruption(const char* function) {
-  LOG(FATAL) << "Corrupt heap detected in: " << function;
+  LOG(::art::FATAL) << "Corrupt heap detected in: " << function;
 }
 
 static void art_heap_usage_error(const char* function, void* p) {
-  LOG(FATAL) << "Incorrect use of function '" << function << "' argument " << p << " not expected";
+  LOG(::art::FATAL) << "Incorrect use of function '" << function << "' argument " << p
+      << " not expected";
 }
 
 #include "globals.h"
@@ -63,14 +68,16 @@
     int rc = madvise(start, length, MADV_DONTNEED);
     if (UNLIKELY(rc != 0)) {
       errno = rc;
-      PLOG(FATAL) << "madvise failed during heap trimming";
+      PLOG(::art::FATAL) << "madvise failed during heap trimming";
     }
     size_t* reclaimed = reinterpret_cast<size_t*>(arg);
     *reclaimed += length;
   }
 }
 
-extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) {
+extern "C" void DlmallocBytesAllocatedCallback(void* start ATTRIBUTE_UNUSED,
+                                               void* end ATTRIBUTE_UNUSED, size_t used_bytes,
+                                               void* arg) {
   if (used_bytes == 0) {
     return;
   }
@@ -78,7 +85,10 @@
   *bytes_allocated += used_bytes + sizeof(size_t);
 }
 
-extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) {
+extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes,
+                                                 void* arg) {
+  UNUSED(start);
+  UNUSED(end);
   if (used_bytes == 0) {
     return;
   }
diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h
index c820b19..0e91a43 100644
--- a/runtime/gc/allocator/dlmalloc.h
+++ b/runtime/gc/allocator/dlmalloc.h
@@ -17,6 +17,8 @@
 #ifndef ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
 #define ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
 
+#include <cstdint>
+
 // Configure dlmalloc for mspaces.
 // Avoid a collision with one used in llvm.
 #undef HAVE_MMAP
@@ -28,12 +30,17 @@
 #define ONLY_MSPACES 1
 #define MALLOC_INSPECT_ALL 1
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
 #include "../../bionic/libc/upstream-dlmalloc/malloc.h"
+#pragma GCC diagnostic pop
 
+#ifdef HAVE_ANDROID_OS
 // Define dlmalloc routines from bionic that cannot be included directly because of redefining
 // symbols from the include above.
 extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), void* arg);
 extern "C" int  dlmalloc_trim(size_t);
+#endif
 
 // Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused
 // pages back to the kernel.
@@ -45,4 +52,16 @@
 extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
 extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
 
+namespace art {
+namespace gc {
+namespace allocator {
+
+// Callback from dlmalloc when it needs to increase the footprint. Must be implemented somewhere
+// else (currently dlmalloc_space.cc).
+void* ArtDlMallocMoreCore(void* mspace, intptr_t increment);
+
+}  // namespace allocator
+}  // namespace gc
+}  // namespace art
+
 #endif  // ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
diff --git a/runtime/gc/allocator/rosalloc-inl.h b/runtime/gc/allocator/rosalloc-inl.h
index c69ca48..f6c9d3c 100644
--- a/runtime/gc/allocator/rosalloc-inl.h
+++ b/runtime/gc/allocator/rosalloc-inl.h
@@ -23,6 +23,10 @@
 namespace gc {
 namespace allocator {
 
+inline ALWAYS_INLINE bool RosAlloc::ShouldCheckZeroMemory() {
+  return kCheckZeroMemory && !running_on_valgrind_;
+}
+
 template<bool kThreadSafe>
 inline ALWAYS_INLINE void* RosAlloc::Alloc(Thread* self, size_t size, size_t* bytes_allocated) {
   if (UNLIKELY(size > kLargeSizeThreshold)) {
@@ -35,8 +39,8 @@
     m = AllocFromRunThreadUnsafe(self, size, bytes_allocated);
   }
   // Check if the returned memory is really all zero.
-  if (kCheckZeroMemory && m != nullptr) {
-    byte* bytes = reinterpret_cast<byte*>(m);
+  if (ShouldCheckZeroMemory() && m != nullptr) {
+    uint8_t* bytes = reinterpret_cast<uint8_t*>(m);
     for (size_t i = 0; i < size; ++i) {
       DCHECK_EQ(bytes[i], 0);
     }
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index ad22a2e..991b956 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -14,24 +14,25 @@
  * limitations under the License.
  */
 
+#include "rosalloc.h"
+
 #include "base/mutex-inl.h"
+#include "gc/space/valgrind_settings.h"
 #include "mirror/class-inl.h"
 #include "mirror/object.h"
 #include "mirror/object-inl.h"
 #include "thread-inl.h"
 #include "thread_list.h"
-#include "rosalloc.h"
 
 #include <map>
 #include <list>
+#include <sstream>
 #include <vector>
 
 namespace art {
 namespace gc {
 namespace allocator {
 
-extern "C" void* art_heap_rosalloc_morecore(RosAlloc* rosalloc, intptr_t increment);
-
 static constexpr bool kUsePrefetchDuringAllocRun = true;
 static constexpr bool kPrefetchNewRunDataByZeroing = false;
 static constexpr size_t kPrefetchStride = 64;
@@ -48,13 +49,15 @@
     reinterpret_cast<RosAlloc::Run*>(dedicated_full_run_storage_);
 
 RosAlloc::RosAlloc(void* base, size_t capacity, size_t max_capacity,
-                   PageReleaseMode page_release_mode, size_t page_release_size_threshold)
-    : base_(reinterpret_cast<byte*>(base)), footprint_(capacity),
+                   PageReleaseMode page_release_mode, bool running_on_valgrind,
+                   size_t page_release_size_threshold)
+    : base_(reinterpret_cast<uint8_t*>(base)), footprint_(capacity),
       capacity_(capacity), max_capacity_(max_capacity),
       lock_("rosalloc global lock", kRosAllocGlobalLock),
       bulk_free_lock_("rosalloc bulk free lock", kRosAllocBulkFreeLock),
       page_release_mode_(page_release_mode),
-      page_release_size_threshold_(page_release_size_threshold) {
+      page_release_size_threshold_(page_release_size_threshold),
+      running_on_valgrind_(running_on_valgrind) {
   DCHECK_EQ(RoundUp(capacity, kPageSize), capacity);
   DCHECK_EQ(RoundUp(max_capacity, kPageSize), max_capacity);
   CHECK_LE(capacity, max_capacity);
@@ -107,7 +110,7 @@
   }
 }
 
-void* RosAlloc::AllocPages(Thread* self, size_t num_pages, byte page_map_type) {
+void* RosAlloc::AllocPages(Thread* self, size_t num_pages, uint8_t page_map_type) {
   lock_.AssertHeld(self);
   DCHECK(page_map_type == kPageMapRun || page_map_type == kPageMapLargeObject);
   FreePageRun* res = NULL;
@@ -128,7 +131,7 @@
       }
       if (req_byte_size < fpr_byte_size) {
         // Split.
-        FreePageRun* remainder = reinterpret_cast<FreePageRun*>(reinterpret_cast<byte*>(fpr) + req_byte_size);
+        FreePageRun* remainder = reinterpret_cast<FreePageRun*>(reinterpret_cast<uint8_t*>(fpr) + req_byte_size);
         if (kIsDebugBuild) {
           remainder->magic_num_ = kMagicNumFree;
         }
@@ -178,7 +181,7 @@
       page_map_size_ = new_num_of_pages;
       DCHECK_LE(page_map_size_, max_page_map_size_);
       free_page_run_size_map_.resize(new_num_of_pages);
-      art_heap_rosalloc_morecore(this, increment);
+      ArtRosAllocMoreCore(this, increment);
       if (last_free_page_run_size > 0) {
         // There was a free page run at the end. Expand its size.
         DCHECK_EQ(last_free_page_run_size, last_free_page_run->ByteSize(this));
@@ -226,7 +229,7 @@
       }
       if (req_byte_size < fpr_byte_size) {
         // Split if there's a remainder.
-        FreePageRun* remainder = reinterpret_cast<FreePageRun*>(reinterpret_cast<byte*>(fpr) + req_byte_size);
+        FreePageRun* remainder = reinterpret_cast<FreePageRun*>(reinterpret_cast<uint8_t*>(fpr) + req_byte_size);
         if (kIsDebugBuild) {
           remainder->magic_num_ = kMagicNumFree;
         }
@@ -264,7 +267,7 @@
       }
       break;
     default:
-      LOG(FATAL) << "Unreachable - page map type: " << page_map_type;
+      LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_type);
       break;
     }
     if (kIsDebugBuild) {
@@ -290,9 +293,9 @@
   lock_.AssertHeld(self);
   size_t pm_idx = ToPageMapIndex(ptr);
   DCHECK_LT(pm_idx, page_map_size_);
-  byte pm_type = page_map_[pm_idx];
+  uint8_t pm_type = page_map_[pm_idx];
   DCHECK(pm_type == kPageMapRun || pm_type == kPageMapLargeObject);
-  byte pm_part_type;
+  uint8_t pm_part_type;
   switch (pm_type) {
   case kPageMapRun:
     pm_part_type = kPageMapRunPart;
@@ -318,9 +321,9 @@
   }
   const size_t byte_size = num_pages * kPageSize;
   if (already_zero) {
-    if (kCheckZeroMemory) {
-      const uword* word_ptr = reinterpret_cast<uword*>(ptr);
-      for (size_t i = 0; i < byte_size / sizeof(uword); ++i) {
+    if (ShouldCheckZeroMemory()) {
+      const uintptr_t* word_ptr = reinterpret_cast<uintptr_t*>(ptr);
+      for (size_t i = 0; i < byte_size / sizeof(uintptr_t); ++i) {
         CHECK_EQ(word_ptr[i], 0U) << "words don't match at index " << i;
       }
     }
@@ -472,10 +475,10 @@
               << "(" << std::dec << (num_pages * kPageSize) << ")";
   }
   // Check if the returned memory is really all zero.
-  if (kCheckZeroMemory) {
-    CHECK_EQ(total_bytes % sizeof(uword), 0U);
-    const uword* words = reinterpret_cast<uword*>(r);
-    for (size_t i = 0; i < total_bytes / sizeof(uword); ++i) {
+  if (ShouldCheckZeroMemory()) {
+    CHECK_EQ(total_bytes % sizeof(uintptr_t), 0U);
+    const uintptr_t* words = reinterpret_cast<uintptr_t*>(r);
+    for (size_t i = 0; i < total_bytes / sizeof(uintptr_t); ++i) {
       CHECK_EQ(words[i], 0U);
     }
   }
@@ -490,7 +493,7 @@
   {
     MutexLock mu(self, lock_);
     DCHECK_LT(pm_idx, page_map_size_);
-    byte page_map_entry = page_map_[pm_idx];
+    uint8_t page_map_entry = page_map_[pm_idx];
     if (kTraceRosAlloc) {
       LOG(INFO) << "RosAlloc::FreeInternal() : " << std::hex << ptr << ", pm_idx=" << std::dec << pm_idx
                 << ", page_map_entry=" << static_cast<int>(page_map_entry);
@@ -499,7 +502,7 @@
       case kPageMapLargeObject:
         return FreePages(self, ptr, false);
       case kPageMapLargeObjectPart:
-        LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx];
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_[pm_idx]);
         return 0;
       case kPageMapRunPart: {
         // Find the beginning of the run.
@@ -507,19 +510,18 @@
           --pm_idx;
           DCHECK_LT(pm_idx, capacity_ / kPageSize);
         } while (page_map_[pm_idx] != kPageMapRun);
-        // Fall-through.
+        FALLTHROUGH_INTENDED;
       case kPageMapRun:
         run = reinterpret_cast<Run*>(base_ + pm_idx * kPageSize);
         DCHECK_EQ(run->magic_num_, kMagicNum);
         break;
       case kPageMapReleased:
-        // Fall-through.
       case kPageMapEmpty:
-        LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx];
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_[pm_idx]);
         return 0;
       }
       default:
-        LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx];
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_[pm_idx]);
         return 0;
     }
   }
@@ -557,7 +559,7 @@
         const size_t num_of_slots = numOfSlots[idx];
         const size_t bracket_size = bracketSizes[idx];
         const size_t num_of_bytes = num_of_slots * bracket_size;
-        byte* begin = reinterpret_cast<byte*>(new_run) + headerSizes[idx];
+        uint8_t* begin = reinterpret_cast<uint8_t*>(new_run) + headerSizes[idx];
         for (size_t i = 0; i < num_of_bytes; i += kPrefetchStride) {
           __builtin_prefetch(begin + i);
         }
@@ -569,7 +571,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();
@@ -745,7 +747,7 @@
   const size_t idx = run->size_bracket_idx_;
   const size_t bracket_size = bracketSizes[idx];
   bool run_was_full = false;
-  MutexLock mu(self, *size_bracket_locks_[idx]);
+  MutexLock brackets_mu(self, *size_bracket_locks_[idx]);
   if (kIsDebugBuild) {
     run_was_full = run->IsFull();
   }
@@ -767,7 +769,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);
@@ -785,7 +787,7 @@
     DCHECK(full_runs_[idx].find(run) == full_runs_[idx].end());
     run->ZeroHeader();
     {
-      MutexLock mu(self, lock_);
+      MutexLock lock_mu(self, lock_);
       FreePages(self, run, true);
     }
   } else {
@@ -793,9 +795,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());
@@ -870,7 +871,7 @@
       DCHECK_EQ(*alloc_bitmap_ptr & mask, 0U);
       *alloc_bitmap_ptr |= mask;
       DCHECK_NE(*alloc_bitmap_ptr & mask, 0U);
-      byte* slot_addr = reinterpret_cast<byte*>(this) + headerSizes[idx] + slot_idx * bracketSizes[idx];
+      uint8_t* slot_addr = reinterpret_cast<uint8_t*>(this) + headerSizes[idx] + slot_idx * bracketSizes[idx];
       if (kTraceRosAlloc) {
         LOG(INFO) << "RosAlloc::Run::AllocSlot() : 0x" << std::hex << reinterpret_cast<intptr_t>(slot_addr)
                   << ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx;
@@ -890,10 +891,10 @@
 
 void RosAlloc::Run::FreeSlot(void* ptr) {
   DCHECK(!IsThreadLocal());
-  const byte idx = size_bracket_idx_;
+  const uint8_t idx = size_bracket_idx_;
   const size_t bracket_size = bracketSizes[idx];
-  const size_t offset_from_slot_base = reinterpret_cast<byte*>(ptr)
-      - (reinterpret_cast<byte*>(this) + headerSizes[idx]);
+  const size_t offset_from_slot_base = reinterpret_cast<uint8_t*>(ptr)
+      - (reinterpret_cast<uint8_t*>(this) + headerSizes[idx]);
   DCHECK_EQ(offset_from_slot_base % bracket_size, static_cast<size_t>(0));
   size_t slot_idx = offset_from_slot_base / bracket_size;
   DCHECK_LT(slot_idx, numOfSlots[idx]);
@@ -1002,9 +1003,9 @@
 
 inline size_t RosAlloc::Run::MarkFreeBitMapShared(void* ptr, uint32_t* free_bit_map_base,
                                                   const char* caller_name) {
-  const byte idx = size_bracket_idx_;
-  const size_t offset_from_slot_base = reinterpret_cast<byte*>(ptr)
-      - (reinterpret_cast<byte*>(this) + headerSizes[idx]);
+  const uint8_t idx = size_bracket_idx_;
+  const size_t offset_from_slot_base = reinterpret_cast<uint8_t*>(ptr)
+      - (reinterpret_cast<uint8_t*>(this) + headerSizes[idx]);
   const size_t bracket_size = bracketSizes[idx];
   memset(ptr, 0, bracket_size);
   DCHECK_EQ(offset_from_slot_base % bracket_size, static_cast<size_t>(0));
@@ -1038,7 +1039,7 @@
 }
 
 inline bool RosAlloc::Run::IsAllFree() {
-  const byte idx = size_bracket_idx_;
+  const uint8_t idx = size_bracket_idx_;
   const size_t num_slots = numOfSlots[idx];
   const size_t num_vec = NumberOfBitmapVectors();
   DCHECK_NE(num_vec, 0U);
@@ -1096,13 +1097,13 @@
 }
 
 inline void RosAlloc::Run::ZeroHeader() {
-  const byte idx = size_bracket_idx_;
+  const uint8_t idx = size_bracket_idx_;
   memset(this, 0, headerSizes[idx]);
 }
 
 inline void RosAlloc::Run::ZeroData() {
-  const byte idx = size_bracket_idx_;
-  byte* slot_begin = reinterpret_cast<byte*>(this) + headerSizes[idx];
+  const uint8_t idx = size_bracket_idx_;
+  uint8_t* slot_begin = reinterpret_cast<uint8_t*>(this) + headerSizes[idx];
   memset(slot_begin, 0, numOfSlots[idx] * bracketSizes[idx]);
 }
 
@@ -1115,10 +1116,10 @@
 void RosAlloc::Run::InspectAllSlots(void (*handler)(void* start, void* end, size_t used_bytes, void* callback_arg),
                                     void* arg) {
   size_t idx = size_bracket_idx_;
-  byte* slot_base = reinterpret_cast<byte*>(this) + headerSizes[idx];
+  uint8_t* slot_base = reinterpret_cast<uint8_t*>(this) + headerSizes[idx];
   size_t num_slots = numOfSlots[idx];
   size_t bracket_size = IndexToBracketSize(idx);
-  DCHECK_EQ(slot_base + num_slots * bracket_size, reinterpret_cast<byte*>(this) + numOfPages[idx] * kPageSize);
+  DCHECK_EQ(slot_base + num_slots * bracket_size, reinterpret_cast<uint8_t*>(this) + numOfPages[idx] * kPageSize);
   size_t num_vec = RoundUp(num_slots, 32) / 32;
   size_t slots = 0;
   for (size_t v = 0; v < num_vec; v++, slots += 32) {
@@ -1127,7 +1128,7 @@
     size_t end = std::min(num_slots - slots, static_cast<size_t>(32));
     for (size_t i = 0; i < end; ++i) {
       bool is_allocated = ((vec >> i) & 0x1) != 0;
-      byte* slot_addr = slot_base + (slots + i) * bracket_size;
+      uint8_t* slot_addr = slot_base + (slots + i) * bracket_size;
       if (is_allocated) {
         handler(slot_addr, slot_addr + bracket_size, bracket_size, arg);
       } else {
@@ -1145,7 +1146,7 @@
 
 size_t RosAlloc::BulkFree(Thread* self, void** ptrs, size_t num_ptrs) {
   size_t freed_bytes = 0;
-  if (false) {
+  if ((false)) {
     // Used only to test Free() as GC uses only BulkFree().
     for (size_t i = 0; i < num_ptrs; ++i) {
       freed_bytes += FreeInternal(self, ptrs[i]);
@@ -1170,7 +1171,7 @@
     Run* run = nullptr;
     if (kReadPageMapEntryWithoutLockInBulkFree) {
       // Read the page map entries without locking the lock.
-      byte page_map_entry = page_map_[pm_idx];
+      uint8_t page_map_entry = page_map_[pm_idx];
       if (kTraceRosAlloc) {
         LOG(INFO) << "RosAlloc::BulkFree() : " << std::hex << ptr << ", pm_idx="
                   << std::dec << pm_idx
@@ -1191,13 +1192,13 @@
         freed_bytes += FreePages(self, ptr, false);
         continue;
       } else {
-        LOG(FATAL) << "Unreachable - page map type: " << page_map_entry;
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_entry);
       }
     } else {
       // Read the page map entries with a lock.
       MutexLock mu(self, lock_);
       DCHECK_LT(pm_idx, page_map_size_);
-      byte page_map_entry = page_map_[pm_idx];
+      uint8_t page_map_entry = page_map_[pm_idx];
       if (kTraceRosAlloc) {
         LOG(INFO) << "RosAlloc::BulkFree() : " << std::hex << ptr << ", pm_idx="
                   << std::dec << pm_idx
@@ -1217,7 +1218,7 @@
         freed_bytes += FreePages(self, ptr, false);
         continue;
       } else {
-        LOG(FATAL) << "Unreachable - page map type: " << page_map_entry;
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_entry);
       }
     }
     DCHECK(run != nullptr);
@@ -1244,7 +1245,7 @@
     run->to_be_bulk_freed_ = false;
 #endif
     size_t idx = run->size_bracket_idx_;
-    MutexLock mu(self, *size_bracket_locks_[idx]);
+    MutexLock brackets_mu(self, *size_bracket_locks_[idx]);
     if (run->IsThreadLocal()) {
       DCHECK_LT(run->size_bracket_idx_, kNumThreadLocalSizeBrackets);
       DCHECK(non_full_runs_[idx].find(run) == non_full_runs_[idx].end());
@@ -1266,9 +1267,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.
@@ -1305,7 +1305,7 @@
         }
         if (!run_was_current) {
           run->ZeroHeader();
-          MutexLock mu(self, lock_);
+          MutexLock lock_mu(self, lock_);
           FreePages(self, run, true);
         }
       } else {
@@ -1356,7 +1356,7 @@
   size_t remaining_curr_fpr_size = 0;
   size_t num_running_empty_pages = 0;
   for (size_t i = 0; i < end; ++i) {
-    byte pm = page_map_[i];
+    uint8_t pm = page_map_[i];
     switch (pm) {
       case kPageMapReleased:
         // Fall-through.
@@ -1437,7 +1437,7 @@
   return stream.str();
 }
 
-size_t RosAlloc::UsableSize(void* ptr) {
+size_t RosAlloc::UsableSize(const void* ptr) {
   DCHECK_LE(base_, ptr);
   DCHECK_LT(ptr, base_ + footprint_);
   size_t pm_idx = RoundDownToPageMapIndex(ptr);
@@ -1474,13 +1474,13 @@
       Run* run = reinterpret_cast<Run*>(base_ + pm_idx * kPageSize);
       DCHECK_EQ(run->magic_num_, kMagicNum);
       size_t idx = run->size_bracket_idx_;
-      size_t offset_from_slot_base = reinterpret_cast<byte*>(ptr)
-          - (reinterpret_cast<byte*>(run) + headerSizes[idx]);
+      size_t offset_from_slot_base = reinterpret_cast<const uint8_t*>(ptr)
+          - (reinterpret_cast<uint8_t*>(run) + headerSizes[idx]);
       DCHECK_EQ(offset_from_slot_base % bracketSizes[idx], static_cast<size_t>(0));
       return IndexToBracketSize(idx);
     }
     default: {
-      LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx];
+      LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_[pm_idx]);
       break;
     }
   }
@@ -1505,8 +1505,8 @@
     size_t new_num_of_pages = new_footprint / kPageSize;
     DCHECK_GE(page_map_size_, new_num_of_pages);
     // Zero out the tail of the page map.
-    byte* zero_begin = const_cast<byte*>(page_map_) + new_num_of_pages;
-    byte* madvise_begin = AlignUp(zero_begin, kPageSize);
+    uint8_t* zero_begin = const_cast<uint8_t*>(page_map_) + new_num_of_pages;
+    uint8_t* madvise_begin = AlignUp(zero_begin, kPageSize);
     DCHECK_LE(madvise_begin, page_map_mem_map_->End());
     size_t madvise_size = page_map_mem_map_->End() - madvise_begin;
     if (madvise_size > 0) {
@@ -1523,7 +1523,7 @@
     page_map_size_ = new_num_of_pages;
     free_page_run_size_map_.resize(new_num_of_pages);
     DCHECK_EQ(free_page_run_size_map_.size(), new_num_of_pages);
-    art_heap_rosalloc_morecore(this, -(static_cast<intptr_t>(decrement)));
+    ArtRosAllocMoreCore(this, -(static_cast<intptr_t>(decrement)));
     if (kTraceRosAlloc) {
       LOG(INFO) << "RosAlloc::Trim() : decreased the footprint from "
                 << footprint_ << " to " << new_footprint;
@@ -1546,7 +1546,7 @@
   size_t pm_end = page_map_size_;
   size_t i = 0;
   while (i < pm_end) {
-    byte pm = page_map_[i];
+    uint8_t pm = page_map_[i];
     switch (pm) {
       case kPageMapReleased:
         // Fall-through.
@@ -1560,9 +1560,9 @@
         if (kIsDebugBuild) {
           // In the debug build, the first page of a free page run
           // contains a magic number for debugging. Exclude it.
-          start = reinterpret_cast<byte*>(fpr) + kPageSize;
+          start = reinterpret_cast<uint8_t*>(fpr) + kPageSize;
         }
-        void* end = reinterpret_cast<byte*>(fpr) + fpr_size;
+        void* end = reinterpret_cast<uint8_t*>(fpr) + fpr_size;
         handler(start, end, 0, arg);
         size_t num_pages = fpr_size / kPageSize;
         if (kIsDebugBuild) {
@@ -1596,7 +1596,7 @@
         break;
       }
       case kPageMapLargeObjectPart:
-        LOG(FATAL) << "Unreachable - page map type: " << pm;
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm);
         break;
       case kPageMapRun: {
         // The start of a run.
@@ -1616,10 +1616,10 @@
         break;
       }
       case kPageMapRunPart:
-        LOG(FATAL) << "Unreachable - page map type: " << pm;
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm);
         break;
       default:
-        LOG(FATAL) << "Unreachable - page map type: " << pm;
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm);
         break;
     }
   }
@@ -1739,14 +1739,14 @@
 void RosAlloc::AssertAllThreadLocalRunsAreRevoked() {
   if (kIsDebugBuild) {
     Thread* self = Thread::Current();
-    MutexLock mu(self, *Locks::runtime_shutdown_lock_);
-    MutexLock mu2(self, *Locks::thread_list_lock_);
+    MutexLock shutdown_mu(self, *Locks::runtime_shutdown_lock_);
+    MutexLock thread_list_mu(self, *Locks::thread_list_lock_);
     std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
     for (Thread* t : thread_list) {
       AssertThreadLocalRunsAreRevoked(t);
     }
     for (size_t idx = 0; idx < kNumThreadLocalSizeBrackets; ++idx) {
-      MutexLock mu(self, *size_bracket_locks_[idx]);
+      MutexLock brackets_mu(self, *size_bracket_locks_[idx]);
       CHECK_EQ(current_runs_[idx], dedicated_full_run_);
     }
   }
@@ -1853,7 +1853,8 @@
   dedicated_full_run_->SetIsThreadLocal(true);
 }
 
-void RosAlloc::BytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) {
+void RosAlloc::BytesAllocatedCallback(void* start ATTRIBUTE_UNUSED, void* end ATTRIBUTE_UNUSED,
+                                      size_t used_bytes, void* arg) {
   if (used_bytes == 0) {
     return;
   }
@@ -1861,7 +1862,8 @@
   *bytes_allocated += used_bytes;
 }
 
-void RosAlloc::ObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) {
+void RosAlloc::ObjectsAllocatedCallback(void* start ATTRIBUTE_UNUSED, void* end ATTRIBUTE_UNUSED,
+                                        size_t used_bytes, void* arg) {
   if (used_bytes == 0) {
     return;
   }
@@ -1873,15 +1875,15 @@
   Thread* self = Thread::Current();
   CHECK(Locks::mutator_lock_->IsExclusiveHeld(self))
       << "The mutator locks isn't exclusively locked at " << __PRETTY_FUNCTION__;
-  MutexLock mu(self, *Locks::thread_list_lock_);
+  MutexLock thread_list_mu(self, *Locks::thread_list_lock_);
   ReaderMutexLock wmu(self, bulk_free_lock_);
   std::vector<Run*> runs;
   {
-    MutexLock mu(self, lock_);
+    MutexLock lock_mu(self, lock_);
     size_t pm_end = page_map_size_;
     size_t i = 0;
     while (i < pm_end) {
-      byte pm = page_map_[i];
+      uint8_t pm = page_map_[i];
       switch (pm) {
         case kPageMapReleased:
           // Fall-through.
@@ -1917,10 +1919,15 @@
             num_pages++;
             idx++;
           }
-          void* start = base_ + i * kPageSize;
+          uint8_t* start = base_ + i * kPageSize;
+          if (running_on_valgrind_) {
+            start += ::art::gc::space::kDefaultValgrindRedZoneBytes;
+          }
           mirror::Object* obj = reinterpret_cast<mirror::Object*>(start);
           size_t obj_size = obj->SizeOf();
-          CHECK_GT(obj_size, kLargeSizeThreshold)
+          CHECK_GT(obj_size +
+                   (running_on_valgrind_ ? 2 * ::art::gc::space::kDefaultValgrindRedZoneBytes : 0),
+                   kLargeSizeThreshold)
               << "A rosalloc large object size must be > " << kLargeSizeThreshold;
           CHECK_EQ(num_pages, RoundUp(obj_size, kPageSize) / kPageSize)
               << "A rosalloc large object size " << obj_size
@@ -1932,7 +1939,7 @@
           break;
         }
         case kPageMapLargeObjectPart:
-          LOG(FATAL) << "Unreachable - page map type: " << pm << std::endl << DumpPageMap();
+          LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm) << std::endl << DumpPageMap();
           break;
         case kPageMapRun: {
           // The start of a run.
@@ -1960,7 +1967,7 @@
         case kPageMapRunPart:
           // Fall-through.
         default:
-          LOG(FATAL) << "Unreachable - page map type: " << pm << std::endl << DumpPageMap();
+          LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm) << std::endl << DumpPageMap();
           break;
       }
     }
@@ -1968,7 +1975,7 @@
   std::list<Thread*> threads = Runtime::Current()->GetThreadList()->GetList();
   for (Thread* thread : threads) {
     for (size_t i = 0; i < kNumThreadLocalSizeBrackets; ++i) {
-      MutexLock mu(self, *size_bracket_locks_[i]);
+      MutexLock brackets_mu(self, *size_bracket_locks_[i]);
       Run* thread_local_run = reinterpret_cast<Run*>(thread->GetRosAllocRun(i));
       CHECK(thread_local_run != nullptr);
       CHECK(thread_local_run->IsThreadLocal());
@@ -1977,7 +1984,7 @@
     }
   }
   for (size_t i = 0; i < kNumOfSizeBrackets; i++) {
-    MutexLock mu(self, *size_bracket_locks_[i]);
+    MutexLock brackets_mu(self, *size_bracket_locks_[i]);
     Run* current_run = current_runs_[i];
     CHECK(current_run != nullptr);
     if (current_run != dedicated_full_run_) {
@@ -1988,21 +1995,21 @@
   }
   // Call Verify() here for the lock order.
   for (auto& run : runs) {
-    run->Verify(self, this);
+    run->Verify(self, this, running_on_valgrind_);
   }
 }
 
-void RosAlloc::Run::Verify(Thread* self, RosAlloc* rosalloc) {
+void RosAlloc::Run::Verify(Thread* self, RosAlloc* rosalloc, bool running_on_valgrind) {
   DCHECK_EQ(magic_num_, kMagicNum) << "Bad magic number : " << Dump();
   const size_t idx = size_bracket_idx_;
   CHECK_LT(idx, kNumOfSizeBrackets) << "Out of range size bracket index : " << Dump();
-  byte* slot_base = reinterpret_cast<byte*>(this) + headerSizes[idx];
+  uint8_t* slot_base = reinterpret_cast<uint8_t*>(this) + headerSizes[idx];
   const size_t num_slots = numOfSlots[idx];
   const size_t num_vec = RoundUp(num_slots, 32) / 32;
   CHECK_GT(num_vec, 0U);
   size_t bracket_size = IndexToBracketSize(idx);
   CHECK_EQ(slot_base + num_slots * bracket_size,
-           reinterpret_cast<byte*>(this) + numOfPages[idx] * kPageSize)
+           reinterpret_cast<uint8_t*>(this) + numOfPages[idx] * kPageSize)
       << "Mismatch in the end address of the run " << Dump();
   // Check that the bulk free bitmap is clean. It's only used during BulkFree().
   CHECK(IsBulkFreeBitmapClean()) << "The bulk free bit map isn't clean " << Dump();
@@ -2056,7 +2063,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 +2073,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();
         }
@@ -2075,6 +2082,9 @@
   }
   // Check each slot.
   size_t slots = 0;
+  size_t valgrind_modifier = running_on_valgrind ?
+      2 * ::art::gc::space::kDefaultValgrindRedZoneBytes :
+      0U;
   for (size_t v = 0; v < num_vec; v++, slots += 32) {
     DCHECK_GE(num_slots, slots) << "Out of bounds";
     uint32_t vec = alloc_bit_map_[v];
@@ -2086,15 +2096,18 @@
       // thread local free bitmap.
       bool is_thread_local_freed = IsThreadLocal() && ((thread_local_free_vec >> i) & 0x1) != 0;
       if (is_allocated && !is_thread_local_freed) {
-        byte* slot_addr = slot_base + (slots + i) * bracket_size;
+        uint8_t* slot_addr = slot_base + (slots + i) * bracket_size;
+        if (running_on_valgrind) {
+          slot_addr += ::art::gc::space::kDefaultValgrindRedZoneBytes;
+        }
         mirror::Object* obj = reinterpret_cast<mirror::Object*>(slot_addr);
         size_t obj_size = obj->SizeOf();
-        CHECK_LE(obj_size, kLargeSizeThreshold)
+        CHECK_LE(obj_size + valgrind_modifier, kLargeSizeThreshold)
             << "A run slot contains a large object " << Dump();
-        CHECK_EQ(SizeToIndex(obj_size), idx)
+        CHECK_EQ(SizeToIndex(obj_size + valgrind_modifier), idx)
             << PrettyTypeOf(obj) << " "
-            << "obj_size=" << obj_size << ", idx=" << idx << " "
-            << "A run slot contains an object with wrong size " << Dump();
+            << "obj_size=" << obj_size << "(" << obj_size + valgrind_modifier << "), idx=" << idx
+            << " A run slot contains an object with wrong size " << Dump();
       }
     }
   }
@@ -2110,7 +2123,7 @@
   while (i < page_map_size_) {
     // Reading the page map without a lock is racy but the race is benign since it should only
     // result in occasionally not releasing pages which we could release.
-    byte pm = page_map_[i];
+    uint8_t pm = page_map_[i];
     switch (pm) {
       case kPageMapReleased:
         // Fall through.
@@ -2131,7 +2144,7 @@
           if (free_page_runs_.find(fpr) != free_page_runs_.end()) {
             size_t fpr_size = fpr->ByteSize(this);
             DCHECK(IsAligned<kPageSize>(fpr_size));
-            byte* start = reinterpret_cast<byte*>(fpr);
+            uint8_t* start = reinterpret_cast<uint8_t*>(fpr);
             reclaimed_bytes += ReleasePageRange(start, start + fpr_size);
             size_t pages = fpr_size / kPageSize;
             CHECK_GT(pages, 0U) << "Infinite loop probable";
@@ -2140,7 +2153,7 @@
             break;
           }
         }
-        // Fall through.
+        FALLTHROUGH_INTENDED;
       }
       case kPageMapLargeObject:      // Fall through.
       case kPageMapLargeObjectPart:  // Fall through.
@@ -2149,14 +2162,14 @@
         ++i;
         break;  // Skip.
       default:
-        LOG(FATAL) << "Unreachable - page map type: " << pm;
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm);
         break;
     }
   }
   return reclaimed_bytes;
 }
 
-size_t RosAlloc::ReleasePageRange(byte* start, byte* end) {
+size_t RosAlloc::ReleasePageRange(uint8_t* start, uint8_t* end) {
   DCHECK_ALIGNED(start, kPageSize);
   DCHECK_ALIGNED(end, kPageSize);
   DCHECK_LT(start, end);
@@ -2164,6 +2177,11 @@
     // In the debug build, the first page of a free page run
     // contains a magic number for debugging. Exclude it.
     start += kPageSize;
+
+    // Single pages won't be released.
+    if (start == end) {
+      return 0;
+    }
   }
   if (!kMadviseZeroes) {
     // TODO: Do this when we resurrect the page instead.
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index b2a5a3c..431686a 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"
@@ -43,23 +44,23 @@
   // Represents a run of free pages.
   class FreePageRun {
    public:
-    byte magic_num_;  // The magic number used for debugging only.
+    uint8_t magic_num_;  // The magic number used for debugging only.
 
     bool IsFree() const {
       return !kIsDebugBuild || magic_num_ == kMagicNumFree;
     }
     size_t ByteSize(RosAlloc* rosalloc) const EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
-      const byte* fpr_base = reinterpret_cast<const byte*>(this);
+      const uint8_t* fpr_base = reinterpret_cast<const uint8_t*>(this);
       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)
         EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
       DCHECK_EQ(byte_size % kPageSize, static_cast<size_t>(0));
-      byte* fpr_base = reinterpret_cast<byte*>(this);
+      uint8_t* fpr_base = reinterpret_cast<uint8_t*>(this);
       size_t pm_idx = rosalloc->ToPageMapIndex(fpr_base);
       rosalloc->free_page_run_size_map_[pm_idx] = byte_size;
     }
@@ -67,8 +68,8 @@
       return reinterpret_cast<void*>(this);
     }
     void* End(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
-      byte* fpr_base = reinterpret_cast<byte*>(this);
-      byte* end = fpr_base + ByteSize(rosalloc);
+      uint8_t* fpr_base = reinterpret_cast<uint8_t*>(this);
+      uint8_t* end = fpr_base + ByteSize(rosalloc);
       return end;
     }
     bool IsLargerThanPageReleaseThreshold(RosAlloc* rosalloc)
@@ -77,7 +78,7 @@
     }
     bool IsAtEndOfSpace(RosAlloc* rosalloc)
         EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
-      return reinterpret_cast<byte*>(this) + ByteSize(rosalloc) == rosalloc->base_ + rosalloc->footprint_;
+      return reinterpret_cast<uint8_t*>(this) + ByteSize(rosalloc) == rosalloc->base_ + rosalloc->footprint_;
     }
     bool ShouldReleasePages(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
       switch (rosalloc->page_release_mode_) {
@@ -97,13 +98,16 @@
       }
     }
     void ReleasePages(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
-      byte* start = reinterpret_cast<byte*>(this);
+      uint8_t* start = reinterpret_cast<uint8_t*>(this);
       size_t byte_size = ByteSize(rosalloc);
       DCHECK_EQ(byte_size % kPageSize, static_cast<size_t>(0));
       if (ShouldReleasePages(rosalloc)) {
         rosalloc->ReleasePageRange(start, start + byte_size);
       }
     }
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(FreePageRun);
   };
 
   // Represents a run of memory slots of the same size.
@@ -150,10 +154,10 @@
   //
   class Run {
    public:
-    byte magic_num_;                 // The magic number used for debugging.
-    byte size_bracket_idx_;          // The index of the size bracket of this run.
-    byte is_thread_local_;           // True if this run is used as a thread-local run.
-    byte to_be_bulk_freed_;          // Used within BulkFree() to flag a run that's involved with a bulk free.
+    uint8_t magic_num_;                 // The magic number used for debugging.
+    uint8_t size_bracket_idx_;          // The index of the size bracket of this run.
+    uint8_t is_thread_local_;           // True if this run is used as a thread-local run.
+    uint8_t to_be_bulk_freed_;          // Used within BulkFree() to flag a run that's involved with a bulk free.
     uint32_t first_search_vec_idx_;  // The index of the first bitmap vector which may contain an available slot.
     uint32_t alloc_bit_map_[0];      // The bit map that allocates if each slot is in use.
 
@@ -174,20 +178,20 @@
     // Returns the byte size of the header except for the bit maps.
     static size_t fixed_header_size() {
       Run temp;
-      size_t size = reinterpret_cast<byte*>(&temp.alloc_bit_map_) - reinterpret_cast<byte*>(&temp);
+      size_t size = reinterpret_cast<uint8_t*>(&temp.alloc_bit_map_) - reinterpret_cast<uint8_t*>(&temp);
       DCHECK_EQ(size, static_cast<size_t>(8));
       return size;
     }
     // Returns the base address of the free bit map.
     uint32_t* BulkFreeBitMap() {
-      return reinterpret_cast<uint32_t*>(reinterpret_cast<byte*>(this) + bulkFreeBitMapOffsets[size_bracket_idx_]);
+      return reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(this) + bulkFreeBitMapOffsets[size_bracket_idx_]);
     }
     // Returns the base address of the thread local free bit map.
     uint32_t* ThreadLocalFreeBitMap() {
-      return reinterpret_cast<uint32_t*>(reinterpret_cast<byte*>(this) + threadLocalFreeBitMapOffsets[size_bracket_idx_]);
+      return reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(this) + threadLocalFreeBitMapOffsets[size_bracket_idx_]);
     }
     void* End() {
-      return reinterpret_cast<byte*>(this) + kPageSize * numOfPages[size_bracket_idx_];
+      return reinterpret_cast<uint8_t*>(this) + kPageSize * numOfPages[size_bracket_idx_];
     }
     // Returns the number of bitmap words per run.
     size_t NumberOfBitmapVectors() const {
@@ -245,7 +249,7 @@
     // Dump the run metadata for debugging.
     std::string Dump();
     // Verify for debugging.
-    void Verify(Thread* self, RosAlloc* rosalloc)
+    void Verify(Thread* self, RosAlloc* rosalloc, bool running_on_valgrind)
         EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
         EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_);
 
@@ -255,16 +259,18 @@
     size_t MarkFreeBitMapShared(void* ptr, uint32_t* free_bit_map_base, const char* caller_name);
     // Turns the bit map into a string for debugging.
     static std::string BitMapToStr(uint32_t* bit_map_base, size_t num_vec);
+
+    // TODO: DISALLOW_COPY_AND_ASSIGN(Run);
   };
 
   // The magic number for a run.
-  static const byte kMagicNum = 42;
+  static constexpr uint8_t kMagicNum = 42;
   // The magic number for free pages.
-  static const byte kMagicNumFree = 43;
+  static constexpr uint8_t kMagicNumFree = 43;
   // The number of size brackets. Sync this with the length of Thread::rosalloc_runs_.
-  static const size_t kNumOfSizeBrackets = kNumRosAllocThreadLocalSizeBrackets;
+  static constexpr size_t kNumOfSizeBrackets = kNumRosAllocThreadLocalSizeBrackets;
   // The number of smaller size brackets that are 16 bytes apart.
-  static const size_t kNumOfQuantumSizeBrackets = 32;
+  static constexpr size_t kNumOfQuantumSizeBrackets = 32;
   // The sizes (the slot sizes, in bytes) of the size brackets.
   static size_t bracketSizes[kNumOfSizeBrackets];
   // The numbers of pages that are used for runs for each size bracket.
@@ -284,7 +290,7 @@
 
   // Returns the byte size of the bracket size from the index.
   static size_t IndexToBracketSize(size_t idx) {
-    DCHECK(idx < kNumOfSizeBrackets);
+    DCHECK_LT(idx, kNumOfSizeBrackets);
     return bracketSizes[idx];
   }
   // Returns the index of the size bracket from the bracket size.
@@ -354,14 +360,15 @@
   // Returns the page map index from an address. Requires that the
   // address is page size aligned.
   size_t ToPageMapIndex(const void* addr) const {
-    DCHECK(base_ <= addr && addr < base_ + capacity_);
-    size_t byte_offset = reinterpret_cast<const byte*>(addr) - base_;
+    DCHECK_LE(base_, addr);
+    DCHECK_LT(addr, base_ + capacity_);
+    size_t byte_offset = reinterpret_cast<const uint8_t*>(addr) - base_;
     DCHECK_EQ(byte_offset % static_cast<size_t>(kPageSize), static_cast<size_t>(0));
     return byte_offset / kPageSize;
   }
   // Returns the page map index from an address with rounding.
-  size_t RoundDownToPageMapIndex(void* addr) const {
-    DCHECK(base_ <= addr && addr < reinterpret_cast<byte*>(base_) + capacity_);
+  size_t RoundDownToPageMapIndex(const void* addr) const {
+    DCHECK(base_ <= addr && addr < reinterpret_cast<uint8_t*>(base_) + capacity_);
     return (reinterpret_cast<uintptr_t>(addr) - reinterpret_cast<uintptr_t>(base_)) / kPageSize;
   }
 
@@ -371,6 +378,10 @@
 
   // If true, check that the returned memory is actually zero.
   static constexpr bool kCheckZeroMemory = kIsDebugBuild;
+  // Valgrind protects memory, so do not check memory when running under valgrind. In a normal
+  // build with kCheckZeroMemory the whole test should be optimized away.
+  // TODO: Unprotect before checks.
+  ALWAYS_INLINE bool ShouldCheckZeroMemory();
 
   // If true, log verbose details of operations.
   static constexpr bool kTraceRosAlloc = false;
@@ -403,11 +414,12 @@
 
   // 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:
   // The base address of the memory region that's managed by this allocator.
-  byte* base_;
+  uint8_t* base_;
 
   // The footprint in bytes of the currently allocated portion of the
   // memory region.
@@ -423,12 +435,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_;
@@ -443,7 +456,7 @@
   // Bracket lock names (since locks only have char* names).
   std::string size_bracket_lock_names_[kNumOfSizeBrackets];
   // The types of page map entries.
-  enum {
+  enum PageMapKind {
     kPageMapReleased = 0,     // Zero and released back to the OS.
     kPageMapEmpty,            // Zero but probably dirty.
     kPageMapRun,              // The beginning of a run.
@@ -452,7 +465,7 @@
     kPageMapLargeObjectPart,  // The non-beginning part of a large object.
   };
   // The table that indicates what pages are currently used for.
-  volatile byte* page_map_;  // No GUARDED_BY(lock_) for kReadPageMapEntryWithoutLockInBulkFree.
+  volatile uint8_t* page_map_;  // No GUARDED_BY(lock_) for kReadPageMapEntryWithoutLockInBulkFree.
   size_t page_map_size_;
   size_t max_page_map_size_;
   std::unique_ptr<MemMap> page_map_mem_map_;
@@ -460,7 +473,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;
@@ -476,13 +490,16 @@
   // greater than or equal to this value, release pages.
   const size_t page_release_size_threshold_;
 
+  // Whether this allocator is running under Valgrind.
+  bool running_on_valgrind_;
+
   // The base address of the memory region that's managed by this allocator.
-  byte* Begin() { return base_; }
+  uint8_t* Begin() { return base_; }
   // The end address of the memory region that's managed by this allocator.
-  byte* End() { return base_ + capacity_; }
+  uint8_t* End() { return base_ + capacity_; }
 
   // Page-granularity alloc/free
-  void* AllocPages(Thread* self, size_t num_pages, byte page_map_type)
+  void* AllocPages(Thread* self, size_t num_pages, uint8_t page_map_type)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
   // Returns how many bytes were freed.
   size_t FreePages(Thread* self, void* ptr, bool already_zero) EXCLUSIVE_LOCKS_REQUIRED(lock_);
@@ -520,13 +537,18 @@
   void RevokeThreadUnsafeCurrentRuns();
 
   // Release a range of pages.
-  size_t ReleasePageRange(byte* start, byte* end) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+  size_t ReleasePageRange(uint8_t* start, uint8_t* end) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+
+  // Dumps the page map for debugging.
+  std::string DumpPageMap() EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
  public:
   RosAlloc(void* base, size_t capacity, size_t max_capacity,
            PageReleaseMode page_release_mode,
+           bool running_on_valgrind,
            size_t page_release_size_threshold = kDefaultPageReleaseSizeThreshold);
   ~RosAlloc();
+
   // If kThreadUnsafe is true then the allocator may avoid acquiring some locks as an optimization.
   // If used, this may cause race conditions if multiple threads are allocating at the same time.
   template<bool kThreadSafe = true>
@@ -536,8 +558,9 @@
       LOCKS_EXCLUDED(bulk_free_lock_);
   size_t BulkFree(Thread* self, void** ptrs, size_t num_ptrs)
       LOCKS_EXCLUDED(bulk_free_lock_);
+
   // Returns the size of the allocated slot for a given allocated memory chunk.
-  size_t UsableSize(void* ptr);
+  size_t UsableSize(const void* ptr);
   // Returns the size of the allocated slot for a given size.
   size_t UsableSize(size_t bytes) {
     if (UNLIKELY(bytes > kLargeSizeThreshold)) {
@@ -553,6 +576,7 @@
   void InspectAll(void (*handler)(void* start, void* end, size_t used_bytes, void* callback_arg),
                   void* arg)
       LOCKS_EXCLUDED(lock_);
+
   // Release empty pages.
   size_t ReleasePages() LOCKS_EXCLUDED(lock_);
   // Returns the current footprint.
@@ -561,6 +585,7 @@
   size_t FootprintLimit() LOCKS_EXCLUDED(lock_);
   // Update the current capacity.
   void SetFootprintLimit(size_t bytes) LOCKS_EXCLUDED(lock_);
+
   // Releases the thread-local runs assigned to the given thread back to the common set of runs.
   void RevokeThreadLocalRuns(Thread* thread);
   // Releases the thread-local runs assigned to all the threads back to the common set of runs.
@@ -569,14 +594,13 @@
   void AssertThreadLocalRunsAreRevoked(Thread* thread);
   // Assert all the thread local runs are revoked.
   void AssertAllThreadLocalRunsAreRevoked() LOCKS_EXCLUDED(Locks::thread_list_lock_);
-  // Dumps the page map for debugging.
-  std::string DumpPageMap() EXCLUSIVE_LOCKS_REQUIRED(lock_);
+
   static Run* GetDedicatedFullRun() {
     return dedicated_full_run_;
   }
   bool IsFreePage(size_t idx) const {
     DCHECK_LT(idx, capacity_ / kPageSize);
-    byte pm_type = page_map_[idx];
+    uint8_t pm_type = page_map_[idx];
     return pm_type == kPageMapReleased || pm_type == kPageMapEmpty;
   }
 
@@ -593,7 +617,17 @@
   void Verify() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes);
+
+ private:
+  friend std::ostream& operator<<(std::ostream& os, const RosAlloc::PageMapKind& rhs);
+
+  DISALLOW_COPY_AND_ASSIGN(RosAlloc);
 };
+std::ostream& operator<<(std::ostream& os, const RosAlloc::PageMapKind& rhs);
+
+// Callback from rosalloc when it needs to increase the footprint. Must be implemented somewhere
+// else (currently rosalloc_space.cc).
+void* ArtRosAllocMoreCore(allocator::RosAlloc* rosalloc, intptr_t increment);
 
 }  // namespace allocator
 }  // namespace gc
diff --git a/runtime/gc/allocator_type.h b/runtime/gc/allocator_type.h
index 938b0f1..c6ebc73 100644
--- a/runtime/gc/allocator_type.h
+++ b/runtime/gc/allocator_type.h
@@ -17,6 +17,8 @@
 #ifndef ART_RUNTIME_GC_ALLOCATOR_TYPE_H_
 #define ART_RUNTIME_GC_ALLOCATOR_TYPE_H_
 
+#include <ostream>
+
 namespace art {
 namespace gc {
 
@@ -29,6 +31,7 @@
   kAllocatorTypeNonMoving,  // Special allocator for non moving objects, doesn't have entrypoints.
   kAllocatorTypeLOS,  // Large object space, also doesn't have entrypoints.
 };
+std::ostream& operator<<(std::ostream& os, const AllocatorType& rhs);
 
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index ce7c75a..ee5a785 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -29,7 +29,9 @@
                              const std::string& name_prefix = "")
       : GarbageCollector(heap,
                          name_prefix + (name_prefix.empty() ? "" : " ") +
-                         "concurrent copying + mark sweep") {}
+                         "concurrent copying + mark sweep") {
+    UNUSED(generational);
+  }
 
   ~ConcurrentCopying() {}
 
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 46d79bf..9e6a800 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -18,6 +18,10 @@
 
 #include "garbage_collector.h"
 
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include "cutils/trace.h"
+
+#include "base/dumpable.h"
 #include "base/histogram-inl.h"
 #include "base/logging.h"
 #include "base/mutex-inl.h"
@@ -55,7 +59,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 +70,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 +101,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 +144,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 +181,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 << Dumpable<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..b2482ac 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -120,7 +120,7 @@
 void MarkCompact::CalculateObjectForwardingAddresses() {
   TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
   // The bump pointer in the space where the next forwarding address will be.
-  bump_pointer_ = reinterpret_cast<byte*>(space_->Begin());
+  bump_pointer_ = reinterpret_cast<uint8_t*>(space_->Begin());
   // Visit all the marked objects in the bitmap.
   CalculateObjectForwardingAddressVisitor visitor(this);
   objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()),
@@ -239,7 +239,7 @@
       accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
       if (table != nullptr) {
         // TODO: Improve naming.
-        TimingLogger::ScopedTiming t(
+        TimingLogger::ScopedTiming t2(
             space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" :
                                      "UpdateAndMarkImageModUnionTable", GetTimings());
         table->UpdateAndMarkReferences(MarkHeapReferenceCallback, this);
@@ -348,7 +348,7 @@
     accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
     if (table != nullptr) {
       // TODO: Improve naming.
-      TimingLogger::ScopedTiming t(
+      TimingLogger::ScopedTiming t2(
           space->IsZygoteSpace() ? "UpdateZygoteModUnionTableReferences" :
                                    "UpdateImageModUnionTableReferences",
                                    GetTimings());
@@ -538,7 +538,7 @@
       if (!ShouldSweepSpace(alloc_space)) {
         continue;
       }
-      TimingLogger::ScopedTiming t(
+      TimingLogger::ScopedTiming t2(
           alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", GetTimings());
       RecordFree(alloc_space->Sweep(swap_bitmaps));
     }
@@ -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_compact.h b/runtime/gc/collector/mark_compact.h
index bb85fa0..f40e870 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -227,7 +227,7 @@
   std::string collector_name_;
 
   // The bump pointer in the space where the next forwarding address will be.
-  byte* bump_pointer_;
+  uint8_t* bump_pointer_;
   // How many live objects we have in the space.
   size_t live_objects_in_space_;
 
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 95530be..6ad44e6 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -21,6 +21,9 @@
 #include <climits>
 #include <vector>
 
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include "cutils/trace.h"
+
 #include "base/bounded_fifo.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -374,7 +377,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 +485,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;
@@ -654,6 +658,7 @@
   // Scans all of the objects
   virtual void Run(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+    UNUSED(self);
     ScanObjectParallelVisitor visitor(this);
     // TODO: Tune this.
     static const size_t kFifoSize = 4;
@@ -662,10 +667,10 @@
       Object* obj = nullptr;
       if (kUseMarkStackPrefetch) {
         while (mark_stack_pos_ != 0 && prefetch_fifo.size() < kFifoSize) {
-          Object* obj = mark_stack_[--mark_stack_pos_];
-          DCHECK(obj != nullptr);
-          __builtin_prefetch(obj);
-          prefetch_fifo.push_back(obj);
+          Object* mark_stack_obj = mark_stack_[--mark_stack_pos_];
+          DCHECK(mark_stack_obj != nullptr);
+          __builtin_prefetch(mark_stack_obj);
+          prefetch_fifo.push_back(mark_stack_obj);
         }
         if (UNLIKELY(prefetch_fifo.empty())) {
           break;
@@ -688,7 +693,7 @@
  public:
   CardScanTask(ThreadPool* thread_pool, MarkSweep* mark_sweep,
                accounting::ContinuousSpaceBitmap* bitmap,
-               byte* begin, byte* end, byte minimum_age, size_t mark_stack_size,
+               uint8_t* begin, uint8_t* end, uint8_t minimum_age, size_t mark_stack_size,
                Object** mark_stack_obj)
       : MarkStackTask<false>(thread_pool, mark_sweep, mark_stack_size, mark_stack_obj),
         bitmap_(bitmap),
@@ -699,9 +704,9 @@
 
  protected:
   accounting::ContinuousSpaceBitmap* const bitmap_;
-  byte* const begin_;
-  byte* const end_;
-  const byte minimum_age_;
+  uint8_t* const begin_;
+  uint8_t* const end_;
+  const uint8_t minimum_age_;
 
   virtual void Finalize() {
     delete this;
@@ -729,7 +734,7 @@
   }
 }
 
-void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) {
+void MarkSweep::ScanGrayObjects(bool paused, uint8_t minimum_age) {
   accounting::CardTable* card_table = GetHeap()->GetCardTable();
   ThreadPool* thread_pool = GetHeap()->GetThreadPool();
   size_t thread_count = GetThreadCount(paused);
@@ -753,8 +758,8 @@
       if (space->GetMarkBitmap() == nullptr) {
         continue;
       }
-      byte* card_begin = space->Begin();
-      byte* card_end = space->End();
+      uint8_t* card_begin = space->Begin();
+      uint8_t* card_end = space->End();
       // Align up the end address. For example, the image space's end
       // may not be card-size-aligned.
       card_end = AlignUp(card_end, accounting::CardTable::kCardSize);
@@ -809,6 +814,7 @@
           break;
         default:
           LOG(FATAL) << "Unreachable";
+          UNREACHABLE();
         }
         TimingLogger::ScopedTiming t(name, GetTimings());
         ScanObjectVisitor visitor(this);
@@ -909,7 +915,7 @@
   return nullptr;
 }
 
-void MarkSweep::RecursiveMarkDirtyObjects(bool paused, byte minimum_age) {
+void MarkSweep::RecursiveMarkDirtyObjects(bool paused, uint8_t minimum_age) {
   ScanGrayObjects(paused, minimum_age);
   ProcessMarkStack(paused);
 }
@@ -922,7 +928,7 @@
                                                           kVisitRootFlagStopLoggingNewRoots |
                                                           kVisitRootFlagClearRootLog));
   if (kVerifyRootsMarked) {
-    TimingLogger::ScopedTiming t("(Paused)VerifyRoots", GetTimings());
+    TimingLogger::ScopedTiming t2("(Paused)VerifyRoots", GetTimings());
     Runtime::Current()->VisitRoots(VerifyRootMarked, this);
   }
 }
@@ -1051,7 +1057,7 @@
         // if needed.
         if (!mark_bitmap->Test(obj)) {
           if (chunk_free_pos >= kSweepArrayChunkFreeSize) {
-            TimingLogger::ScopedTiming t("FreeList", GetTimings());
+            TimingLogger::ScopedTiming t2("FreeList", GetTimings());
             freed.objects += chunk_free_pos;
             freed.bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer);
             chunk_free_pos = 0;
@@ -1063,7 +1069,7 @@
       }
     }
     if (chunk_free_pos > 0) {
-      TimingLogger::ScopedTiming t("FreeList", GetTimings());
+      TimingLogger::ScopedTiming t2("FreeList", GetTimings());
       freed.objects += chunk_free_pos;
       freed.bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer);
       chunk_free_pos = 0;
@@ -1074,27 +1080,29 @@
   }
   // 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);
+      }
     }
   }
   {
-    TimingLogger::ScopedTiming t("RecordFree", GetTimings());
+    TimingLogger::ScopedTiming t2("RecordFree", GetTimings());
     RecordFree(freed);
     RecordFreeLOS(freed_los);
-    t.NewTiming("ResetStack");
+    t2.NewTiming("ResetStack");
     allocations->Reset();
   }
   sweep_array_free_buffer_mem_map_->MadviseDontNeedAndZero();
@@ -1125,8 +1133,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
@@ -1207,10 +1218,10 @@
       Object* obj = NULL;
       if (kUseMarkStackPrefetch) {
         while (!mark_stack_->IsEmpty() && prefetch_fifo.size() < kFifoSize) {
-          Object* obj = mark_stack_->PopBack();
-          DCHECK(obj != NULL);
-          __builtin_prefetch(obj);
-          prefetch_fifo.push_back(obj);
+          Object* mark_stack_obj = mark_stack_->PopBack();
+          DCHECK(mark_stack_obj != NULL);
+          __builtin_prefetch(mark_stack_obj);
+          prefetch_fifo.push_back(mark_stack_obj);
         }
         if (prefetch_fifo.empty()) {
           break;
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 2780099..9ac110d 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -112,7 +112,7 @@
   virtual void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Builds a mark stack with objects on dirty cards and recursively mark until it empties.
-  void RecursiveMarkDirtyObjects(bool paused, byte minimum_age)
+  void RecursiveMarkDirtyObjects(bool paused, uint8_t minimum_age)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -257,7 +257,7 @@
   void PushOnMarkStack(mirror::Object* obj);
 
   // Blackens objects grayed during a garbage collection.
-  void ScanGrayObjects(bool paused, byte minimum_age)
+  void ScanGrayObjects(bool paused, uint8_t minimum_age)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 8fb33ce..cb9f111 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -16,9 +16,10 @@
 
 #include "semi_space-inl.h"
 
+#include <climits>
 #include <functional>
 #include <numeric>
-#include <climits>
+#include <sstream>
 #include <vector>
 
 #include "base/logging.h"
@@ -223,7 +224,7 @@
   // Need to do this before the checkpoint since we don't want any threads to add references to
   // the live stack during the recursive mark.
   if (kUseThreadLocalAllocationStack) {
-    TimingLogger::ScopedTiming t("RevokeAllThreadLocalAllocationStacks", GetTimings());
+    TimingLogger::ScopedTiming t2("RevokeAllThreadLocalAllocationStacks", GetTimings());
     heap_->RevokeAllThreadLocalAllocationStacks(self_);
   }
   heap_->SwapStacks(self_);
@@ -365,23 +366,23 @@
   }
 
   CHECK_EQ(is_large_object_space_immune_, collect_from_space_only_);
-  if (is_large_object_space_immune_) {
-    TimingLogger::ScopedTiming t("VisitLargeObjects", GetTimings());
+  space::LargeObjectSpace* los = GetHeap()->GetLargeObjectsSpace();
+  if (is_large_object_space_immune_ && los != nullptr) {
+    TimingLogger::ScopedTiming t2("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.
@@ -437,15 +438,15 @@
     return 0;
   }
   size_t saved_bytes = 0;
-  byte* byte_dest = reinterpret_cast<byte*>(dest);
+  uint8_t* byte_dest = reinterpret_cast<uint8_t*>(dest);
   if (kIsDebugBuild) {
     for (size_t i = 0; i < size; ++i) {
       CHECK_EQ(byte_dest[i], 0U);
     }
   }
   // Process the start of the page. The page must already be dirty, don't bother with checking.
-  const byte* byte_src = reinterpret_cast<const byte*>(src);
-  const byte* limit = byte_src + size;
+  const uint8_t* byte_src = reinterpret_cast<const uint8_t*>(src);
+  const uint8_t* limit = byte_src + size;
   size_t page_remain = AlignUp(byte_dest, kPageSize) - byte_dest;
   // Copy the bytes until the start of the next page.
   memcpy(dest, src, page_remain);
@@ -481,7 +482,7 @@
   const size_t object_size = obj->SizeOf();
   size_t bytes_allocated;
   mirror::Object* forward_address = nullptr;
-  if (generational_ && reinterpret_cast<byte*>(obj) < last_gc_to_space_end_) {
+  if (generational_ && reinterpret_cast<uint8_t*>(obj) < last_gc_to_space_end_) {
     // If it's allocated before the last GC (older), move
     // (pseudo-promote) it to the main free list space (as sort
     // of an old generation.)
@@ -655,8 +656,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 +755,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 +767,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 +780,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/semi_space.h b/runtime/gc/collector/semi_space.h
index 71a83f2..1c4f1e4 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -228,7 +228,7 @@
 
   // Used for the generational mode. the end/top of the bump
   // pointer space at the end of the last collection.
-  byte* last_gc_to_space_end_;
+  uint8_t* last_gc_to_space_end_;
 
   // Used for the generational mode. During a collection, keeps track
   // of how many bytes of objects have been copied so far from the
diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc
index 5a58446..5be3db7 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() {
@@ -56,6 +58,7 @@
 }
 
 void StickyMarkSweep::Sweep(bool swap_bitmaps) {
+  UNUSED(swap_bitmaps);
   SweepArray(GetHeap()->GetLiveStack(), false);
 }
 
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index f0e1512..6be683d 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -35,8 +35,8 @@
     case kGcCauseTrim: return "HeapTrim";
     default:
       LOG(FATAL) << "Unreachable";
+      UNREACHABLE();
   }
-  return "";
 }
 
 std::ostream& operator<<(std::ostream& os, const GcCause& gc_cause) {
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 0b0200f..0e67978 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -24,6 +24,7 @@
 #include <vector>
 
 #include "base/allocator.h"
+#include "base/dumpable.h"
 #include "base/histogram-inl.h"
 #include "base/stl_util.h"
 #include "common_throws.h"
@@ -84,13 +85,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
@@ -108,8 +102,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,
@@ -135,8 +130,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),
@@ -196,7 +191,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";
@@ -207,7 +201,7 @@
   live_bitmap_.reset(new accounting::HeapBitmap(this));
   mark_bitmap_.reset(new accounting::HeapBitmap(this));
   // Requested begin for the alloc space, to follow the mapped image and oat files
-  byte* requested_alloc_space_begin = nullptr;
+  uint8_t* requested_alloc_space_begin = nullptr;
   if (!image_file_name.empty()) {
     std::string error_msg;
     space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(),
@@ -217,7 +211,7 @@
       AddSpace(image_space);
       // Oat files referenced by image files immediately follow them in memory, ensure alloc space
       // isn't going to get in the middle
-      byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd();
+      uint8_t* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd();
       CHECK_GT(oat_file_end_addr, image_space->End());
       requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
     } else {
@@ -253,7 +247,7 @@
   }
   std::unique_ptr<MemMap> main_mem_map_1;
   std::unique_ptr<MemMap> main_mem_map_2;
-  byte* request_begin = requested_alloc_space_begin;
+  uint8_t* request_begin = requested_alloc_space_begin;
   if (request_begin != nullptr && separate_non_moving_space) {
     request_begin += non_moving_space_capacity;
   }
@@ -267,18 +261,17 @@
                              non_moving_space_capacity, PROT_READ | PROT_WRITE, true, &error_str));
     CHECK(non_moving_space_mem_map != nullptr) << error_str;
     // Try to reserve virtual memory at a lower address if we have a separate non moving space.
-    request_begin = reinterpret_cast<byte*>(300 * MB);
+    request_begin = reinterpret_cast<uint8_t*>(300 * MB);
   }
   // Attempt to create 2 mem maps at or after the requested begin.
   main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_,
-                                                    PROT_READ | PROT_WRITE, &error_str));
+                                                    &error_str));
   CHECK(main_mem_map_1.get() != nullptr) << error_str;
   if (support_homogeneous_space_compaction ||
       background_collector_type_ == kCollectorTypeSS ||
       foreground_collector_type_ == kCollectorTypeSS) {
     main_mem_map_2.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[1], main_mem_map_1->End(),
-                                                      capacity_, PROT_READ | PROT_WRITE,
-                                                      &error_str));
+                                                      capacity_, &error_str));
     CHECK(main_mem_map_2.get() != nullptr) << error_str;
   }
   // Create the non moving space first so that bitmaps don't take up the address range.
@@ -340,18 +333,26 @@
   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.
-  byte* heap_begin = continuous_spaces_.front()->Begin();
-  byte* heap_end = continuous_spaces_.back()->Limit();
+  uint8_t* heap_begin = continuous_spaces_.front()->Begin();
+  uint8_t* heap_end = continuous_spaces_.back()->Limit();
   size_t heap_capacity = heap_end - heap_begin;
   // Remove the main backup space since it slows down the GC to have unused extra spaces.
   if (main_space_backup_.get() != nullptr) {
@@ -413,9 +414,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) {
@@ -431,10 +434,10 @@
   }
 }
 
-MemMap* Heap::MapAnonymousPreferredAddress(const char* name, byte* request_begin, size_t capacity,
-                                           int prot_flags, std::string* out_error_str) {
+MemMap* Heap::MapAnonymousPreferredAddress(const char* name, uint8_t* request_begin,
+                                           size_t capacity, std::string* out_error_str) {
   while (true) {
-    MemMap* map = MemMap::MapAnonymous(kMemMapSpaceName[0], request_begin, capacity,
+    MemMap* map = MemMap::MapAnonymous(name, request_begin, capacity,
                                        PROT_READ | PROT_WRITE, true, out_error_str);
     if (map != nullptr || request_begin == nullptr) {
       return map;
@@ -483,7 +486,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_);
@@ -596,8 +599,8 @@
       }
     }
     // Unprotect all the spaces.
-    for (const auto& space : continuous_spaces_) {
-      mprotect(space->Begin(), space->Capacity(), PROT_READ | PROT_WRITE);
+    for (const auto& con_space : continuous_spaces_) {
+      mprotect(con_space->Begin(), con_space->Capacity(), PROT_READ | PROT_WRITE);
     }
     stream << "Object " << obj;
     if (space != nullptr) {
@@ -683,9 +686,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);
@@ -702,7 +704,6 @@
     }
   }
   GetLiveBitmap()->Walk(callback, arg);
-  self->EndAssertNoThreadSuspension(old_cause);
 }
 
 void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) {
@@ -712,7 +713,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() {
@@ -801,28 +803,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 =
@@ -849,6 +832,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);
@@ -900,7 +886,7 @@
   if (result != NULL) {
     return result;
   }
-  return FindDiscontinuousSpaceFromObject(obj, true);
+  return FindDiscontinuousSpaceFromObject(obj, fail_ok);
 }
 
 space::ImageSpace* Heap::GetImageSpace() const {
@@ -1018,7 +1004,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();
   }
@@ -1028,6 +1017,8 @@
   // We never move things in the native heap, so we can finish the GC at this point.
   FinishGC(self, collector::kGcTypeNone);
   size_t native_reclaimed = 0;
+
+#ifdef HAVE_ANDROID_OS
   // Only trim the native heap if we don't care about pauses.
   if (!CareAboutPauseTimes()) {
 #if defined(USE_DLMALLOC)
@@ -1040,6 +1031,7 @@
     UNIMPLEMENTED(WARNING) << "Add trimming support";
 #endif
   }
+#endif  // HAVE_ANDROID_OS
   uint64_t end_ns = NanoTime();
   VLOG(heap) << "Heap trim of managed (duration=" << PrettyDuration(gc_heap_end_ns - start_ns)
       << ", advised=" << PrettySize(managed_reclaimed) << ") and native (duration="
@@ -1274,12 +1266,12 @@
       continue;
     }
     // Attempt to run the collector, if we succeed, re-try the allocation.
-    const bool gc_ran =
+    const bool plan_gc_ran =
         CollectGarbageInternal(gc_type, kGcCauseForAlloc, false) != collector::kGcTypeNone;
     if (was_default_allocator && allocator != GetCurrentAllocator()) {
       return nullptr;
     }
-    if (gc_ran) {
+    if (plan_gc_ran) {
       // Did we free sufficient memory for the allocation to succeed?
       mirror::Object* ptr = TryToAllocate<true, false>(self, allocator, alloc_size, bytes_allocated,
                                                        usable_size);
@@ -1336,8 +1328,9 @@
               // Throw OOM by default.
               break;
             default: {
-              LOG(FATAL) << "Unimplemented homogeneous space compaction result "
-                         << static_cast<size_t>(result);
+              UNIMPLEMENTED(FATAL) << "homogeneous space compaction result: "
+                  << static_cast<size_t>(result);
+              UNREACHABLE();
             }
           }
           // Always print that we ran homogeneous space compation since this can cause jank.
@@ -1439,12 +1432,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 {
@@ -1457,8 +1448,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);
@@ -1467,8 +1457,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);
 };
@@ -1476,12 +1466,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 {
@@ -1514,8 +1502,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);
 };
@@ -1523,12 +1511,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) {
@@ -1546,7 +1532,7 @@
   ScopedThreadStateChange tsc(self, kWaitingPerformingGc);
   Locks::mutator_lock_->AssertNotHeld(self);
   {
-    ScopedThreadStateChange tsc(self, kWaitingForGcToComplete);
+    ScopedThreadStateChange tsc2(self, kWaitingForGcToComplete);
     MutexLock mu(self, *gc_complete_lock_);
     // Ensure there is only one GC at a time.
     WaitForGcToCompleteLocked(kGcCauseHomogeneousSpaceCompact, self);
@@ -1618,7 +1604,7 @@
   // compacting_gc_disable_count_, this should rarely occurs).
   for (;;) {
     {
-      ScopedThreadStateChange tsc(self, kWaitingForGcToComplete);
+      ScopedThreadStateChange tsc2(self, kWaitingForGcToComplete);
       MutexLock mu(self, *gc_complete_lock_);
       // Ensure there is only one GC at a time.
       WaitForGcToCompleteLocked(kGcCauseCollectorTransition, self);
@@ -1779,7 +1765,8 @@
         break;
       }
       default: {
-        LOG(FATAL) << "Unimplemented";
+        UNIMPLEMENTED(FATAL);
+        UNREACHABLE();
       }
     }
     if (IsGcConcurrent()) {
@@ -1844,6 +1831,7 @@
   virtual bool ShouldSweepSpace(space::ContinuousSpace* space) const {
     // Don't sweep any spaces since we probably blasted the internal accounting of the free list
     // allocator.
+    UNUSED(space);
     return false;
   }
 
@@ -1906,7 +1894,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;
   }
   Runtime::Current()->GetInternTable()->SwapPostZygoteWithPreZygote();
@@ -1983,26 +1972,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.
@@ -2035,6 +2024,7 @@
       } else if (bitmap2->HasAddress(obj)) {
         bitmap2->Set(obj);
       } else {
+        DCHECK(large_objects != nullptr);
         large_objects->Set(obj);
       }
     }
@@ -2072,7 +2062,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;
@@ -2090,7 +2080,7 @@
   bool compacting_gc;
   {
     gc_complete_lock_->AssertNotHeld(self);
-    ScopedThreadStateChange tsc(self, kWaitingForGcToComplete);
+    ScopedThreadStateChange tsc2(self, kWaitingForGcToComplete);
     MutexLock mu(self, *gc_complete_lock_);
     // Ensure there is only one GC at a time.
     WaitForGcToCompleteLocked(gc_cause, self);
@@ -2204,7 +2194,7 @@
               << percent_free << "% free, " << PrettySize(current_heap_size) << "/"
               << PrettySize(total_memory) << ", " << "paused " << pause_string.str()
               << " total " << PrettyDuration((duration / 1000) * 1000);
-    VLOG(heap) << ConstDumpable<TimingLogger>(*current_gc_iteration_.GetTimings());
+    VLOG(heap) << Dumpable<TimingLogger>(*current_gc_iteration_.GetTimings());
   }
   FinishGC(self, gc_type);
   // Inform DDMS that a GC completed.
@@ -2250,6 +2240,7 @@
 
   void operator()(mirror::Class* klass, mirror::Reference* ref) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    UNUSED(klass);
     if (verify_referent_) {
       VerifyReference(ref, ref->GetReferent(), mirror::Reference::ReferentOffset());
     }
@@ -2291,7 +2282,7 @@
       accounting::CardTable* card_table = heap_->GetCardTable();
       accounting::ObjectStack* alloc_stack = heap_->allocation_stack_.get();
       accounting::ObjectStack* live_stack = heap_->live_stack_.get();
-      byte* card_addr = card_table->CardFromAddr(obj);
+      uint8_t* card_addr = card_table->CardFromAddr(obj);
       LOG(ERROR) << "Object " << obj << " references dead object " << ref << " at offset "
                  << offset << "\n card value = " << static_cast<int>(*card_addr);
       if (heap_->IsValidObjectAddress(obj->GetClass())) {
@@ -2321,7 +2312,7 @@
                    << ") is not a valid heap address";
       }
 
-      card_table->CheckAddrIsInCardTable(reinterpret_cast<const byte*>(obj));
+      card_table->CheckAddrIsInCardTable(reinterpret_cast<const uint8_t*>(obj));
       void* cover_begin = card_table->AddrFromCard(card_addr);
       void* cover_end = reinterpret_cast<void*>(reinterpret_cast<size_t>(cover_begin) +
           accounting::CardTable::kCardSize);
@@ -2354,7 +2345,7 @@
         }
         // Attempt to see if the card table missed the reference.
         ScanVisitor scan_visitor;
-        byte* byte_cover_begin = reinterpret_cast<byte*>(card_table->AddrFromCard(card_addr));
+        uint8_t* byte_cover_begin = reinterpret_cast<uint8_t*>(card_table->AddrFromCard(card_addr));
         card_table->Scan(bitmap, byte_cover_begin,
                          byte_cover_begin + accounting::CardTable::kCardSize, scan_visitor);
       }
@@ -2594,6 +2585,7 @@
 }
 
 void Heap::SwapStacks(Thread* self) {
+  UNUSED(self);
   if (kUseThreadLocalAllocationStack) {
     live_stack_->AssertAllZero();
   }
@@ -2611,6 +2603,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) {
@@ -2644,15 +2647,15 @@
     if (table != nullptr) {
       const char* name = space->IsZygoteSpace() ? "ZygoteModUnionClearCards" :
           "ImageModUnionClearCards";
-      TimingLogger::ScopedTiming t(name, timings);
+      TimingLogger::ScopedTiming t2(name, timings);
       table->ClearCards();
     } else if (use_rem_sets && rem_set != nullptr) {
       DCHECK(collector::SemiSpace::kUseRememberedSet && collector_type_ == kCollectorTypeGSS)
           << static_cast<int>(collector_type_);
-      TimingLogger::ScopedTiming t("AllocSpaceRemSetClearCards", timings);
+      TimingLogger::ScopedTiming t2("AllocSpaceRemSetClearCards", timings);
       rem_set->ClearCards();
     } else if (space->GetType() != space::kSpaceTypeBumpPointerSpace) {
-      TimingLogger::ScopedTiming t("AllocSpaceClearCards", timings);
+      TimingLogger::ScopedTiming t2("AllocSpaceClearCards", timings);
       // No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards
       // were dirty before the GC started.
       // TODO: Need to use atomic for the case where aged(cleaning thread) -> dirty(other thread)
@@ -2674,7 +2677,7 @@
   TimingLogger* const timings = current_gc_iteration_.GetTimings();
   TimingLogger::ScopedTiming t(__FUNCTION__, timings);
   if (verify_pre_gc_heap_) {
-    TimingLogger::ScopedTiming t("(Paused)PreGcVerifyHeapReferences", timings);
+    TimingLogger::ScopedTiming t2("(Paused)PreGcVerifyHeapReferences", timings);
     ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     size_t failures = VerifyHeapReferences();
     if (failures > 0) {
@@ -2684,7 +2687,7 @@
   }
   // Check that all objects which reference things in the live stack are on dirty cards.
   if (verify_missing_card_marks_) {
-    TimingLogger::ScopedTiming t("(Paused)PreGcVerifyMissingCardMarks", timings);
+    TimingLogger::ScopedTiming t2("(Paused)PreGcVerifyMissingCardMarks", timings);
     ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     SwapStacks(self);
     // Sort the live stack so that we can quickly binary search it later.
@@ -2693,7 +2696,7 @@
     SwapStacks(self);
   }
   if (verify_mod_union_table_) {
-    TimingLogger::ScopedTiming t("(Paused)PreGcVerifyModUnionTables", timings);
+    TimingLogger::ScopedTiming t2("(Paused)PreGcVerifyModUnionTables", timings);
     ReaderMutexLock reader_lock(self, *Locks::heap_bitmap_lock_);
     for (const auto& table_pair : mod_union_tables_) {
       accounting::ModUnionTable* mod_union_table = table_pair.second;
@@ -2711,6 +2714,7 @@
 }
 
 void Heap::PrePauseRosAllocVerification(collector::GarbageCollector* gc) {
+  UNUSED(gc);
   // TODO: Add a new runtime option for this?
   if (verify_pre_gc_rosalloc_) {
     RosAllocVerification(current_gc_iteration_.GetTimings(), "PreGcRosAllocVerification");
@@ -2724,7 +2728,7 @@
   // Called before sweeping occurs since we want to make sure we are not going so reclaim any
   // reachable objects.
   if (verify_pre_sweeping_heap_) {
-    TimingLogger::ScopedTiming t("(Paused)PostSweepingVerifyHeapReferences", timings);
+    TimingLogger::ScopedTiming t2("(Paused)PostSweepingVerifyHeapReferences", timings);
     CHECK_NE(self->GetState(), kRunnable);
     WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
     // Swapping bound bitmaps does nothing.
@@ -2757,7 +2761,7 @@
     RosAllocVerification(timings, "(Paused)PostGcRosAllocVerification");
   }
   if (verify_post_gc_heap_) {
-    TimingLogger::ScopedTiming t("(Paused)PostGcVerifyHeapReferences", timings);
+    TimingLogger::ScopedTiming t2("(Paused)PostGcVerifyHeapReferences", timings);
     ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     size_t failures = VerifyHeapReferences();
     if (failures > 0) {
@@ -2892,7 +2896,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
@@ -3102,8 +3106,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) {
@@ -3117,7 +3119,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 24f4f17..cf7352e 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -30,8 +30,8 @@
 #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"
 #include "jni.h"
 #include "object_callbacks.h"
@@ -79,6 +79,7 @@
 namespace space {
   class AllocSpace;
   class BumpPointerSpace;
+  class ContinuousMemMapAllocSpace;
   class DiscontinuousSpace;
   class DlMallocSpace;
   class ImageSpace;
@@ -87,12 +88,12 @@
   class RosAllocSpace;
   class Space;
   class SpaceTest;
-  class ContinuousMemMapAllocSpace;
+  class ZygoteSpace;
 }  // namespace space
 
 class AgeCardVisitor {
  public:
-  byte operator()(byte card) const {
+  uint8_t operator()(uint8_t card) const {
     if (card == accounting::CardTable::kCardDirty) {
       return card - 1;
     } else {
@@ -128,8 +129,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 +140,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 +169,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 +468,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 +479,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_);
@@ -590,15 +601,16 @@
   void RemoveRememberedSet(space::Space* space);
 
   bool IsCompilingBoot() const;
-  bool RunningOnValgrind() const {
-    return running_on_valgrind_;
-  }
   bool HasImageSpace() const;
 
   ReferenceProcessor* GetReferenceProcessor() {
     return &reference_processor_;
   }
 
+  bool HasZygoteSpace() const {
+    return zygote_space_ != nullptr;
+  }
+
  private:
   // Compact source space to target space.
   void Compact(space::ContinuousMemMapAllocSpace* target_space,
@@ -609,9 +621,8 @@
   void FinishGC(Thread* self, collector::GcType gc_type) LOCKS_EXCLUDED(gc_complete_lock_);
 
   // Create a mem map with a preferred base address.
-  static MemMap* MapAnonymousPreferredAddress(const char* name, byte* request_begin,
-                                              size_t capacity, int prot_flags,
-                                              std::string* out_error_str);
+  static MemMap* MapAnonymousPreferredAddress(const char* name, uint8_t* request_begin,
+                                              size_t capacity, std::string* out_error_str);
 
   bool SupportHSpaceCompaction() const {
     // Returns true if we can do hspace compaction
@@ -676,7 +687,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 +862,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/heap_test.cc b/runtime/gc/heap_test.cc
index e6b5c75..73196b2 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -48,8 +48,8 @@
     Handle<mirror::Class> c(
         hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")));
     for (size_t i = 0; i < 1024; ++i) {
-      StackHandleScope<1> hs(soa.Self());
-      Handle<mirror::ObjectArray<mirror::Object>> array(hs.NewHandle(
+      StackHandleScope<1> hs2(soa.Self());
+      Handle<mirror::ObjectArray<mirror::Object>> array(hs2.NewHandle(
           mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), c.Get(), 2048)));
       for (size_t j = 0; j < 2048; ++j) {
         mirror::String* string = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!");
@@ -62,7 +62,7 @@
 }
 
 TEST_F(HeapTest, HeapBitmapCapacityTest) {
-  byte* heap_begin = reinterpret_cast<byte*>(0x1000);
+  uint8_t* heap_begin = reinterpret_cast<uint8_t*>(0x1000);
   const size_t heap_capacity = kObjectAlignment * (sizeof(intptr_t) * 8 + 1);
   std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap(
       accounting::ContinuousSpaceBitmap::Create("test bitmap", heap_begin, heap_capacity));
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index bfaa2bb..012f9f9 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -143,7 +143,7 @@
   soft_reference_queue_.ClearWhiteReferences(&cleared_references_, is_marked_callback, arg);
   weak_reference_queue_.ClearWhiteReferences(&cleared_references_, is_marked_callback, arg);
   {
-    TimingLogger::ScopedTiming t(concurrent ? "EnqueueFinalizerReferences" :
+    TimingLogger::ScopedTiming t2(concurrent ? "EnqueueFinalizerReferences" :
         "(Paused)EnqueueFinalizerReferences", timings);
     if (concurrent) {
       StartPreservingReferences(self);
diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h
index dbf4abc..4ef8478 100644
--- a/runtime/gc/reference_queue.h
+++ b/runtime/gc/reference_queue.h
@@ -24,7 +24,6 @@
 #include "atomic.h"
 #include "base/timing_logger.h"
 #include "globals.h"
-#include "gtest/gtest.h"
 #include "jni.h"
 #include "object_callbacks.h"
 #include "offsets.h"
@@ -45,44 +44,56 @@
 class ReferenceQueue {
  public:
   explicit ReferenceQueue(Mutex* lock);
+
   // Enqueue a reference if is not already enqueued. Thread safe to call from multiple threads
   // since it uses a lock to avoid a race between checking for the references presence and adding
   // it.
   void AtomicEnqueueIfNotEnqueued(Thread* self, mirror::Reference* ref)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
+
   // Enqueue a reference, unlike EnqueuePendingReference, enqueue reference checks that the
   // reference IsEnqueueable. Not thread safe, used when mutators are paused to minimize lock
   // overhead.
   void EnqueueReference(mirror::Reference* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   void EnqueuePendingReference(mirror::Reference* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   mirror::Reference* DequeuePendingReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Enqueues finalizer references with white referents.  White referents are blackened, moved to the
   // zombie field, and the referent field is cleared.
   void EnqueueFinalizerReferences(ReferenceQueue* cleared_references,
                                   IsHeapReferenceMarkedCallback* is_marked_callback,
                                   MarkObjectCallback* mark_object_callback, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Walks the reference list marking any references subject to the reference clearing policy.
   // References with a black referent are removed from the list.  References with white referents
   // biased toward saving are blackened and also removed from the list.
   void ForwardSoftReferences(IsHeapReferenceMarkedCallback* preserve_callback, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Unlink the reference list clearing references objects with white referents.  Cleared references
   // registered to a reference queue are scheduled for appending by the heap worker thread.
   void ClearWhiteReferences(ReferenceQueue* cleared_references,
                             IsHeapReferenceMarkedCallback* is_marked_callback, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   void Dump(std::ostream& os) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   bool IsEmpty() const {
     return list_ == nullptr;
   }
+
   void Clear() {
     list_ = nullptr;
   }
+
   mirror::Reference* GetList() {
     return list_;
   }
+
   // Visits list_, currently only used for the mark compact GC.
   void UpdateRoots(IsMarkedCallback* callback, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -90,10 +101,13 @@
  private:
   // Lock, used for parallel GC reference enqueuing. It allows for multiple threads simultaneously
   // calling AtomicEnqueueIfNotEnqueued.
-  Mutex* lock_;
+  Mutex* const lock_;
+
   // The actual reference list. Only a root for the mark compact GC since it will be null for other
   // GC types.
   mirror::Reference* list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReferenceQueue);
 };
 
 }  // namespace gc
diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h
index ee3c979..9f1f953 100644
--- a/runtime/gc/space/bump_pointer_space-inl.h
+++ b/runtime/gc/space/bump_pointer_space-inl.h
@@ -41,7 +41,7 @@
                                                            size_t* usable_size) {
   Locks::mutator_lock_->AssertExclusiveHeld(self);
   num_bytes = RoundUp(num_bytes, kAlignment);
-  byte* end = end_.LoadRelaxed();
+  uint8_t* end = end_.LoadRelaxed();
   if (end + num_bytes > growth_end_) {
     return nullptr;
   }
@@ -59,8 +59,8 @@
 
 inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t num_bytes) {
   DCHECK(IsAligned<kAlignment>(num_bytes));
-  byte* old_end;
-  byte* new_end;
+  uint8_t* old_end;
+  uint8_t* new_end;
   do {
     old_end = end_.LoadRelaxed();
     new_end = old_end + num_bytes;
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index fb6bbac..04b09e9 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -25,7 +25,7 @@
 namespace space {
 
 BumpPointerSpace* BumpPointerSpace::Create(const std::string& name, size_t capacity,
-                                           byte* requested_begin) {
+                                           uint8_t* requested_begin) {
   capacity = RoundUp(capacity, kPageSize);
   std::string error_msg;
   std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity,
@@ -42,7 +42,7 @@
   return new BumpPointerSpace(name, mem_map);
 }
 
-BumpPointerSpace::BumpPointerSpace(const std::string& name, byte* begin, byte* limit)
+BumpPointerSpace::BumpPointerSpace(const std::string& name, uint8_t* begin, uint8_t* limit)
     : ContinuousMemMapAllocSpace(name, nullptr, begin, begin, limit,
                                  kGcRetentionPolicyAlwaysCollect),
       growth_end_(limit),
@@ -134,12 +134,12 @@
 }
 
 // Returns the start of the storage.
-byte* BumpPointerSpace::AllocBlock(size_t bytes) {
+uint8_t* BumpPointerSpace::AllocBlock(size_t bytes) {
   bytes = RoundUp(bytes, kAlignment);
   if (!num_blocks_) {
     UpdateMainBlock();
   }
-  byte* storage = reinterpret_cast<byte*>(
+  uint8_t* storage = reinterpret_cast<uint8_t*>(
       AllocNonvirtualWithoutAccounting(bytes + sizeof(BlockHeader)));
   if (LIKELY(storage != nullptr)) {
     BlockHeader* header = reinterpret_cast<BlockHeader*>(storage);
@@ -151,9 +151,9 @@
 }
 
 void BumpPointerSpace::Walk(ObjectCallback* callback, void* arg) {
-  byte* pos = Begin();
-  byte* end = End();
-  byte* main_end = pos;
+  uint8_t* pos = Begin();
+  uint8_t* end = End();
+  uint8_t* main_end = pos;
   {
     MutexLock mu(Thread::Current(), block_lock_);
     // If we have 0 blocks then we need to update the main header since we have bump pointer style
@@ -179,7 +179,7 @@
       return;
     } else {
       callback(obj, arg);
-      pos = reinterpret_cast<byte*>(GetNextObject(obj));
+      pos = reinterpret_cast<uint8_t*>(GetNextObject(obj));
     }
   }
   // Walk the other blocks (currently only TLABs).
@@ -188,11 +188,11 @@
     size_t block_size = header->size_;
     pos += sizeof(BlockHeader);  // Skip the header so that we know where the objects
     mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
-    const mirror::Object* end = reinterpret_cast<const mirror::Object*>(pos + block_size);
-    CHECK_LE(reinterpret_cast<const byte*>(end), End());
+    const mirror::Object* end_obj = reinterpret_cast<const mirror::Object*>(pos + block_size);
+    CHECK_LE(reinterpret_cast<const uint8_t*>(end_obj), End());
     // We don't know how many objects are allocated in the current block. When we hit a null class
     // assume its the end. TODO: Have a thread update the header when it flushes the block?
-    while (obj < end && obj->GetClass() != nullptr) {
+    while (obj < end_obj && obj->GetClass() != nullptr) {
       callback(obj, arg);
       obj = GetNextObject(obj);
     }
@@ -201,8 +201,8 @@
 }
 
 accounting::ContinuousSpaceBitmap::SweepCallback* BumpPointerSpace::GetSweepCallback() {
-  LOG(FATAL) << "Unimplemented";
-  return nullptr;
+  UNIMPLEMENTED(FATAL);
+  UNREACHABLE();
 }
 
 uint64_t BumpPointerSpace::GetBytesAllocated() {
@@ -250,7 +250,7 @@
 bool BumpPointerSpace::AllocNewTlab(Thread* self, size_t bytes) {
   MutexLock mu(Thread::Current(), block_lock_);
   RevokeThreadLocalBuffersLocked(self);
-  byte* start = AllocBlock(bytes);
+  uint8_t* start = AllocBlock(bytes);
   if (start == nullptr) {
     return false;
   }
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index 71b15ba..089ede4 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -42,7 +42,7 @@
   // Create a bump pointer space with the requested sizes. The requested base address is not
   // guaranteed to be granted, if it is required, the caller should call Begin on the returned
   // space to confirm the request was granted.
-  static BumpPointerSpace* Create(const std::string& name, size_t capacity, byte* requested_begin);
+  static BumpPointerSpace* Create(const std::string& name, size_t capacity, uint8_t* requested_begin);
   static BumpPointerSpace* CreateFromMemMap(const std::string& name, MemMap* mem_map);
 
   // Allocate num_bytes, returns nullptr if the space is full.
@@ -121,12 +121,12 @@
   }
 
   bool Contains(const mirror::Object* obj) const {
-    const byte* byte_obj = reinterpret_cast<const byte*>(obj);
+    const uint8_t* byte_obj = reinterpret_cast<const uint8_t*>(obj);
     return byte_obj >= Begin() && byte_obj < End();
   }
 
   // TODO: Change this? Mainly used for compacting to a particular region of memory.
-  BumpPointerSpace(const std::string& name, byte* begin, byte* limit);
+  BumpPointerSpace(const std::string& name, uint8_t* begin, uint8_t* limit);
 
   // Return the object which comes after obj, while ensuring alignment.
   static mirror::Object* GetNextObject(mirror::Object* obj)
@@ -161,7 +161,7 @@
   BumpPointerSpace(const std::string& name, MemMap* mem_map);
 
   // Allocate a raw block of bytes.
-  byte* AllocBlock(size_t bytes) EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
+  uint8_t* AllocBlock(size_t bytes) EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
   void RevokeThreadLocalBuffersLocked(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
 
   // The main block is an unbounded block where objects go when there are no other blocks. This
@@ -169,7 +169,7 @@
   // allocation. The main block starts at the space Begin().
   void UpdateMainBlock() EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
 
-  byte* growth_end_;
+  uint8_t* growth_end_;
   AtomicInteger objects_allocated_;  // Accumulated from revoked thread local regions.
   AtomicInteger bytes_allocated_;  // Accumulated from revoked thread local regions.
   Mutex block_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -186,8 +186,8 @@
     size_t unused_;  // Ensures alignment of kAlignment.
   };
 
-  COMPILE_ASSERT(sizeof(BlockHeader) % kAlignment == 0,
-                 continuous_block_must_be_kAlignment_aligned);
+  static_assert(sizeof(BlockHeader) % kAlignment == 0,
+                "continuous block must be kAlignment aligned");
 
   friend class collector::MarkSweep;
   DISALLOW_COPY_AND_ASSIGN(BumpPointerSpace);
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index 456d1b3..b8a9dd6 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -33,12 +33,9 @@
 
 static constexpr bool kPrefetchDuringDlMallocFreeList = true;
 
-template class ValgrindMallocSpace<DlMallocSpace, void*>;
-
-DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin,
-                             byte* end, byte* limit, size_t growth_limit,
-                             bool can_move_objects, size_t starting_size,
-                             size_t initial_size)
+DlMallocSpace::DlMallocSpace(MemMap* mem_map, size_t initial_size, const std::string& name,
+                             void* mspace, uint8_t* begin, uint8_t* end, uint8_t* limit,
+                             size_t growth_limit, bool can_move_objects, size_t starting_size)
     : MallocSpace(name, mem_map, begin, end, limit, growth_limit, true, can_move_objects,
                   starting_size, initial_size),
       mspace_(mspace) {
@@ -57,25 +54,25 @@
   }
 
   // Protect memory beyond the starting size. morecore will add r/w permissions when necessory
-  byte* end = mem_map->Begin() + starting_size;
+  uint8_t* end = mem_map->Begin() + starting_size;
   if (capacity - starting_size > 0) {
     CHECK_MEMORY_CALL(mprotect, (end, capacity - starting_size, PROT_NONE), name);
   }
 
   // Everything is set so record in immutable structure and leave
-  byte* begin = mem_map->Begin();
+  uint8_t* begin = mem_map->Begin();
   if (Runtime::Current()->RunningOnValgrind()) {
-    return new ValgrindMallocSpace<DlMallocSpace, void*>(
-        name, mem_map, mspace, begin, end, begin + capacity, growth_limit, initial_size,
+    return new ValgrindMallocSpace<DlMallocSpace, kDefaultValgrindRedZoneBytes, true, false>(
+        mem_map, initial_size, name, mspace, begin, end, begin + capacity, growth_limit,
         can_move_objects, starting_size);
   } else {
-    return new DlMallocSpace(name, mem_map, mspace, begin, end, begin + capacity, growth_limit,
-                             can_move_objects, starting_size, initial_size);
+    return new DlMallocSpace(mem_map, initial_size, name, mspace, begin, end, begin + capacity,
+                             growth_limit, can_move_objects, starting_size);
   }
 }
 
 DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_size,
-                                     size_t growth_limit, size_t capacity, byte* requested_begin,
+                                     size_t growth_limit, size_t capacity, uint8_t* requested_begin,
                                      bool can_move_objects) {
   uint64_t start_time = 0;
   if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
@@ -148,12 +145,18 @@
   return result;
 }
 
-MallocSpace* DlMallocSpace::CreateInstance(const std::string& name, MemMap* mem_map,
-                                           void* allocator, byte* begin, byte* end,
-                                           byte* limit, size_t growth_limit,
+MallocSpace* DlMallocSpace::CreateInstance(MemMap* mem_map, const std::string& name,
+                                           void* allocator, uint8_t* begin, uint8_t* end,
+                                           uint8_t* limit, size_t growth_limit,
                                            bool can_move_objects) {
-  return new DlMallocSpace(name, mem_map, allocator, begin, end, limit, growth_limit,
-                           can_move_objects, starting_size_, initial_size_);
+  if (Runtime::Current()->RunningOnValgrind()) {
+    return new ValgrindMallocSpace<DlMallocSpace, kDefaultValgrindRedZoneBytes, true, false>(
+        mem_map, initial_size_, name, allocator, begin, end, limit, growth_limit,
+        can_move_objects, starting_size_);
+  } else {
+    return new DlMallocSpace(mem_map, initial_size_, name, allocator, begin, end, limit,
+                             growth_limit, can_move_objects, starting_size_);
+  }
 }
 
 size_t DlMallocSpace::Free(Thread* self, mirror::Object* ptr) {
@@ -213,27 +216,6 @@
   }
 }
 
-// Callback from dlmalloc when it needs to increase the footprint
-extern "C" void* art_heap_morecore(void* mspace, intptr_t increment) {
-  Heap* heap = Runtime::Current()->GetHeap();
-  DlMallocSpace* dlmalloc_space = heap->GetDlMallocSpace();
-  // Support for multiple DlMalloc provided by a slow path.
-  if (UNLIKELY(dlmalloc_space == nullptr || dlmalloc_space->GetMspace() != mspace)) {
-    dlmalloc_space = nullptr;
-    for (space::ContinuousSpace* space : heap->GetContinuousSpaces()) {
-      if (space->IsDlMallocSpace()) {
-        DlMallocSpace* cur_dlmalloc_space = space->AsDlMallocSpace();
-        if (cur_dlmalloc_space->GetMspace() == mspace) {
-          dlmalloc_space = cur_dlmalloc_space;
-          break;
-        }
-      }
-    }
-    CHECK(dlmalloc_space != nullptr) << "Couldn't find DlmMallocSpace with mspace=" << mspace;
-  }
-  return dlmalloc_space->MoreCore(increment);
-}
-
 size_t DlMallocSpace::Trim() {
   MutexLock mu(Thread::Current(), lock_);
   // Trim to release memory at the end of the space.
@@ -314,6 +296,7 @@
 }
 
 void DlMallocSpace::LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) {
+  UNUSED(failed_alloc_bytes);
   Thread* self = Thread::Current();
   size_t max_contiguous_allocation = 0;
   // To allow the Walk/InspectAll() to exclusively-lock the mutator
@@ -329,5 +312,31 @@
 }
 
 }  // namespace space
+
+namespace allocator {
+
+// Implement the dlmalloc morecore callback.
+void* ArtDlMallocMoreCore(void* mspace, intptr_t increment) {
+  Heap* heap = Runtime::Current()->GetHeap();
+  ::art::gc::space::DlMallocSpace* dlmalloc_space = heap->GetDlMallocSpace();
+  // Support for multiple DlMalloc provided by a slow path.
+  if (UNLIKELY(dlmalloc_space == nullptr || dlmalloc_space->GetMspace() != mspace)) {
+    dlmalloc_space = nullptr;
+    for (space::ContinuousSpace* space : heap->GetContinuousSpaces()) {
+      if (space->IsDlMallocSpace()) {
+        ::art::gc::space::DlMallocSpace* cur_dlmalloc_space = space->AsDlMallocSpace();
+        if (cur_dlmalloc_space->GetMspace() == mspace) {
+          dlmalloc_space = cur_dlmalloc_space;
+          break;
+        }
+      }
+    }
+    CHECK(dlmalloc_space != nullptr) << "Couldn't find DlmMallocSpace with mspace=" << mspace;
+  }
+  return dlmalloc_space->MoreCore(increment);
+}
+
+}  // namespace allocator
+
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h
index 7aff14b..6ce138c 100644
--- a/runtime/gc/space/dlmalloc_space.h
+++ b/runtime/gc/space/dlmalloc_space.h
@@ -44,7 +44,7 @@
   // the caller should call Begin on the returned space to confirm the
   // request was granted.
   static DlMallocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit,
-                               size_t capacity, byte* requested_begin, bool can_move_objects);
+                               size_t capacity, uint8_t* requested_begin, bool can_move_objects);
 
   // Virtual to allow ValgrindMallocSpace to intercept.
   virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated,
@@ -107,8 +107,8 @@
   // allocations fail we GC before increasing the footprint limit and allowing the mspace to grow.
   void SetFootprintLimit(size_t limit) OVERRIDE;
 
-  MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator,
-                              byte* begin, byte* end, byte* limit, size_t growth_limit,
+  MallocSpace* CreateInstance(MemMap* mem_map, const std::string& name, void* allocator,
+                              uint8_t* begin, uint8_t* end, uint8_t* limit, size_t growth_limit,
                               bool can_move_objects);
 
   uint64_t GetBytesAllocated() OVERRIDE;
@@ -128,9 +128,9 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  protected:
-  DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end,
-                byte* limit, size_t growth_limit, bool can_move_objects, size_t starting_size,
-                size_t initial_size);
+  DlMallocSpace(MemMap* mem_map, size_t initial_size, const std::string& name, void* mspace,
+                uint8_t* begin, uint8_t* end, uint8_t* limit, size_t growth_limit,
+                bool can_move_objects, size_t starting_size);
 
  private:
   mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated,
@@ -144,7 +144,7 @@
   static void* CreateMspace(void* base, size_t morecore_start, size_t initial_size);
 
   // The boundary tag overhead.
-  static const size_t kChunkOverhead = kWordSize;
+  static const size_t kChunkOverhead = sizeof(intptr_t);
 
   // Underlying malloc space.
   void* mspace_;
diff --git a/runtime/gc/space/dlmalloc_space_base_test.cc b/runtime/gc/space/dlmalloc_space_base_test.cc
index 02fc4a5..93fe155 100644
--- a/runtime/gc/space/dlmalloc_space_base_test.cc
+++ b/runtime/gc/space/dlmalloc_space_base_test.cc
@@ -24,7 +24,7 @@
 namespace space {
 
 MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
-                                 size_t capacity, byte* requested_begin) {
+                                 size_t capacity, uint8_t* requested_begin) {
   return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, false);
 }
 
diff --git a/runtime/gc/space/dlmalloc_space_random_test.cc b/runtime/gc/space/dlmalloc_space_random_test.cc
index 4b1a1b1..f9b41da 100644
--- a/runtime/gc/space/dlmalloc_space_random_test.cc
+++ b/runtime/gc/space/dlmalloc_space_random_test.cc
@@ -23,7 +23,7 @@
 namespace space {
 
 MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
-                                 size_t capacity, byte* requested_begin) {
+                                 size_t capacity, uint8_t* requested_begin) {
   return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, false);
 }
 
diff --git a/runtime/gc/space/dlmalloc_space_static_test.cc b/runtime/gc/space/dlmalloc_space_static_test.cc
index d17d0a7..5758e0c 100644
--- a/runtime/gc/space/dlmalloc_space_static_test.cc
+++ b/runtime/gc/space/dlmalloc_space_static_test.cc
@@ -23,7 +23,7 @@
 namespace space {
 
 MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
-                                 size_t capacity, byte* requested_begin) {
+                                 size_t capacity, uint8_t* requested_begin) {
   return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, false);
 }
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 9683a80..b232128 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -22,6 +22,7 @@
 
 #include <random>
 
+#include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "base/scoped_flock.h"
@@ -121,7 +122,7 @@
                           std::string* error_msg) {
   const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
   std::vector<std::string> boot_class_path;
-  Split(boot_class_path_string, ':', boot_class_path);
+  Split(boot_class_path_string, ':', &boot_class_path);
   if (boot_class_path.empty()) {
     *error_msg = "Failed to generate image because no boot class path specified";
     return false;
@@ -583,7 +584,7 @@
 }
 
 void ImageSpace::VerifyImageAllocations() {
-  byte* current = Begin() + RoundUp(sizeof(ImageHeader), kObjectAlignment);
+  uint8_t* current = Begin() + RoundUp(sizeof(ImageHeader), kObjectAlignment);
   while (current < End()) {
     DCHECK_ALIGNED(current, kObjectAlignment);
     mirror::Object* obj = reinterpret_cast<mirror::Object*>(current);
@@ -652,7 +653,7 @@
                                        bitmap_index));
   std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap(
       accounting::ContinuousSpaceBitmap::CreateFromMemMap(bitmap_name, image_map.release(),
-                                                          reinterpret_cast<byte*>(map->Begin()),
+                                                          reinterpret_cast<uint8_t*>(map->Begin()),
                                                           map->Size()));
   if (bitmap.get() == nullptr) {
     *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 2a43712..c0c6444 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -45,7 +45,7 @@
     mirror::Object* object_without_rdz = reinterpret_cast<mirror::Object*>(
         reinterpret_cast<uintptr_t>(obj) + kValgrindRedZoneBytes);
     VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast<void*>(obj), kValgrindRedZoneBytes);
-    VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast<byte*>(object_without_rdz) + num_bytes,
+    VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast<uint8_t*>(object_without_rdz) + num_bytes,
                                kValgrindRedZoneBytes);
     if (usable_size != nullptr) {
       *usable_size = num_bytes;  // Since we have redzones, shrink the usable size.
@@ -84,7 +84,7 @@
   mark_bitmap_->SetName(temp_name);
 }
 
-LargeObjectSpace::LargeObjectSpace(const std::string& name, byte* begin, byte* end)
+LargeObjectSpace::LargeObjectSpace(const std::string& name, uint8_t* begin, uint8_t* end)
     : DiscontinuousSpace(name, kGcRetentionPolicyAlwaysCollect),
       num_bytes_allocated_(0), num_objects_allocated_(0), total_bytes_allocated_(0),
       total_objects_allocated_(0), begin_(begin), end_(end) {
@@ -120,10 +120,10 @@
   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;
+  begin_ = std::min(begin_, reinterpret_cast<uint8_t*>(obj));
+  uint8_t* obj_end = reinterpret_cast<uint8_t*>(obj) + allocation_size;
   if (end_ == nullptr || obj_end > end_) {
     end_ = obj_end;
   }
@@ -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,11 @@
   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();
+  size_t alloc_size = found->second->BaseSize();
+  if (usable_size != nullptr) {
+    *usable_size = alloc_size;
+  }
+  return alloc_size;
 }
 
 size_t LargeObjectSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
@@ -282,7 +287,7 @@
   return reinterpret_cast<uintptr_t>(a) < reinterpret_cast<uintptr_t>(b);
 }
 
-FreeListSpace* FreeListSpace::Create(const std::string& name, byte* requested_begin, size_t size) {
+FreeListSpace* FreeListSpace::Create(const std::string& name, uint8_t* requested_begin, size_t size) {
   CHECK_EQ(size % kAlignment, 0U);
   std::string error_msg;
   MemMap* mem_map = MemMap::MapAnonymous(name.c_str(), requested_begin, size,
@@ -291,7 +296,7 @@
   return new FreeListSpace(name, mem_map, mem_map->Begin(), mem_map->End());
 }
 
-FreeListSpace::FreeListSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end)
+FreeListSpace::FreeListSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end)
     : LargeObjectSpace(name, begin, end),
       mem_map_(mem_map),
       lock_("free list space lock", kAllocSpaceLock) {
@@ -318,8 +323,8 @@
   while (cur_info < end_info) {
     if (!cur_info->IsFree()) {
       size_t alloc_size = cur_info->ByteSize();
-      byte* byte_start = reinterpret_cast<byte*>(GetAddressForAllocationInfo(cur_info));
-      byte* byte_end = byte_start + alloc_size;
+      uint8_t* byte_start = reinterpret_cast<uint8_t*>(GetAddressForAllocationInfo(cur_info));
+      uint8_t* byte_end = byte_start + alloc_size;
       callback(byte_start, byte_end, alloc_size, arg);
       callback(nullptr, nullptr, 0, arg);
     }
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index 9d5e090..850a006 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:
@@ -71,11 +77,11 @@
     return false;
   }
   // Current address at which the space begins, which may vary as the space is filled.
-  byte* Begin() const {
+  uint8_t* Begin() const {
     return begin_;
   }
   // Current address at which the space ends, which may vary as the space is filled.
-  byte* End() const {
+  uint8_t* End() const {
     return end_;
   }
   // Current size of space
@@ -84,14 +90,14 @@
   }
   // Return true if we contain the specified address.
   bool Contains(const mirror::Object* obj) const {
-    const byte* byte_obj = reinterpret_cast<const byte*>(obj);
+    const uint8_t* byte_obj = reinterpret_cast<const uint8_t*>(obj);
     return Begin() <= byte_obj && byte_obj < End();
   }
   void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  protected:
-  explicit LargeObjectSpace(const std::string& name, byte* begin, byte* end);
+  explicit LargeObjectSpace(const std::string& name, uint8_t* begin, uint8_t* end);
   static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg);
 
   // Approximate number of bytes which have been allocated into the space.
@@ -100,8 +106,8 @@
   uint64_t total_bytes_allocated_;
   uint64_t total_objects_allocated_;
   // Begin and end, may change as more large objects are allocated.
-  byte* begin_;
-  byte* end_;
+  uint8_t* begin_;
+  uint8_t* end_;
 
   friend class Space;
 
@@ -143,7 +149,7 @@
   static constexpr size_t kAlignment = kPageSize;
 
   virtual ~FreeListSpace();
-  static FreeListSpace* Create(const std::string& name, byte* requested_begin, size_t capacity);
+  static FreeListSpace* Create(const std::string& name, uint8_t* requested_begin, size_t capacity);
   size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
   mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
@@ -153,7 +159,7 @@
   void Dump(std::ostream& os) const;
 
  protected:
-  FreeListSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end);
+  FreeListSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end);
   size_t GetSlotIndexForAddress(uintptr_t address) const {
     DCHECK(Contains(reinterpret_cast<mirror::Object*>(address)));
     return (address - reinterpret_cast<uintptr_t>(Begin())) / kAlignment;
diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc
index c5d8abc..e17bad8 100644
--- a/runtime/gc/space/large_object_space_test.cc
+++ b/runtime/gc/space/large_object_space_test.cc
@@ -55,7 +55,7 @@
         ASSERT_EQ(allocation_size, los->AllocationSize(obj, nullptr));
         ASSERT_GE(allocation_size, request_size);
         // Fill in our magic value.
-        byte magic = (request_size & 0xFF) | 1;
+        uint8_t magic = (request_size & 0xFF) | 1;
         memset(obj, magic, request_size);
         requests.push_back(std::make_pair(obj, request_size));
       }
@@ -73,9 +73,9 @@
         mirror::Object* obj = requests.back().first;
         size_t request_size = requests.back().second;
         requests.pop_back();
-        byte magic = (request_size & 0xFF) | 1;
+        uint8_t magic = (request_size & 0xFF) | 1;
         for (size_t k = 0; k < request_size; ++k) {
-          ASSERT_EQ(reinterpret_cast<const byte*>(obj)[k], magic);
+          ASSERT_EQ(reinterpret_cast<const uint8_t*>(obj)[k], magic);
         }
         ASSERT_GE(los->Free(Thread::Current(), obj), request_size);
       }
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index ba7e5c1..43a2c59 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -36,7 +36,7 @@
 size_t MallocSpace::bitmap_index_ = 0;
 
 MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map,
-                         byte* begin, byte* end, byte* limit, size_t growth_limit,
+                         uint8_t* begin, uint8_t* end, uint8_t* limit, size_t growth_limit,
                          bool create_bitmaps, bool can_move_objects, size_t starting_size,
                          size_t initial_size)
     : ContinuousMemMapAllocSpace(name, mem_map, begin, end, limit, kGcRetentionPolicyAlwaysCollect),
@@ -66,7 +66,7 @@
 }
 
 MemMap* MallocSpace::CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size,
-                                  size_t* growth_limit, size_t* capacity, byte* requested_begin) {
+                                  size_t* growth_limit, size_t* capacity, uint8_t* requested_begin) {
   // Sanity check arguments
   if (starting_size > *initial_size) {
     *initial_size = starting_size;
@@ -129,10 +129,10 @@
 
 void* MallocSpace::MoreCore(intptr_t increment) {
   CheckMoreCoreForPrecondition();
-  byte* original_end = End();
+  uint8_t* original_end = End();
   if (increment != 0) {
     VLOG(heap) << "MallocSpace::MoreCore " << PrettySize(increment);
-    byte* new_end = original_end + increment;
+    uint8_t* new_end = original_end + increment;
     if (increment > 0) {
       // Should never be asked to increase the allocation beyond the capacity of the space. Enforced
       // by mspace_set_footprint_limit.
@@ -163,7 +163,7 @@
   // alloc space so that we won't mix thread local runs from different
   // alloc spaces.
   RevokeAllThreadLocalBuffers();
-  SetEnd(reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(End()), kPageSize)));
+  SetEnd(reinterpret_cast<uint8_t*>(RoundUp(reinterpret_cast<uintptr_t>(End()), kPageSize)));
   DCHECK(IsAligned<accounting::CardTable::kCardSize>(begin_));
   DCHECK(IsAligned<accounting::CardTable::kCardSize>(End()));
   DCHECK(IsAligned<kPageSize>(begin_));
@@ -194,11 +194,11 @@
   void* allocator = CreateAllocator(End(), starting_size_, initial_size_, capacity,
                                     low_memory_mode);
   // Protect memory beyond the initial size.
-  byte* end = mem_map->Begin() + starting_size_;
+  uint8_t* end = mem_map->Begin() + starting_size_;
   if (capacity > initial_size_) {
     CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size_, PROT_NONE), alloc_space_name);
   }
-  *out_malloc_space = CreateInstance(alloc_space_name, mem_map.release(), allocator, End(), end,
+  *out_malloc_space = CreateInstance(mem_map.release(), alloc_space_name, allocator, End(), end,
                                      limit_, growth_limit, CanMoveObjects());
   SetLimit(End());
   live_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End()));
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index bace3f6..2fbd5f0 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -19,7 +19,7 @@
 
 #include "space.h"
 
-#include <iostream>
+#include <ostream>
 #include <valgrind.h>
 #include <memcheck/memcheck.h>
 
@@ -114,9 +114,9 @@
 
   void SetGrowthLimit(size_t growth_limit);
 
-  virtual MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator,
-                                      byte* begin, byte* end, byte* limit, size_t growth_limit,
-                                      bool can_move_objects) = 0;
+  virtual MallocSpace* CreateInstance(MemMap* mem_map, const std::string& name, void* allocator,
+                                      uint8_t* begin, uint8_t* end, uint8_t* limit,
+                                      size_t growth_limit, bool can_move_objects) = 0;
 
   // Splits ourself into a zygote space and new malloc space which has our unused memory. When true,
   // the low memory mode argument specifies that the heap wishes the created space to be more
@@ -138,12 +138,12 @@
   }
 
  protected:
-  MallocSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end,
-              byte* limit, size_t growth_limit, bool create_bitmaps, bool can_move_objects,
+  MallocSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end,
+              uint8_t* limit, size_t growth_limit, bool create_bitmaps, bool can_move_objects,
               size_t starting_size, size_t initial_size);
 
   static MemMap* CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size,
-                              size_t* growth_limit, size_t* capacity, byte* requested_begin);
+                              size_t* growth_limit, size_t* capacity, uint8_t* requested_begin);
 
   // When true the low memory mode argument specifies that the heap wishes the created allocator to
   // be more aggressive in releasing unused pages.
diff --git a/runtime/gc/space/rosalloc_space-inl.h b/runtime/gc/space/rosalloc_space-inl.h
index fbfef45..5d6642d 100644
--- a/runtime/gc/space/rosalloc_space-inl.h
+++ b/runtime/gc/space/rosalloc_space-inl.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_
 
 #include "gc/allocator/rosalloc-inl.h"
+#include "gc/space/valgrind_settings.h"
 #include "rosalloc_space.h"
 #include "thread.h"
 
@@ -26,13 +27,19 @@
 namespace space {
 
 inline size_t RosAllocSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size) {
-  void* obj_ptr = const_cast<void*>(reinterpret_cast<const void*>(obj));
   // obj is a valid object. Use its class in the header to get the size.
   // Don't use verification since the object may be dead if we are sweeping.
   size_t size = obj->SizeOf<kVerifyNone>();
+  bool running_on_valgrind = RUNNING_ON_VALGRIND != 0;
+  if (running_on_valgrind) {
+    size += 2 * kDefaultValgrindRedZoneBytes;
+  }
   size_t size_by_size = rosalloc_->UsableSize(size);
   if (kIsDebugBuild) {
-    size_t size_by_ptr = rosalloc_->UsableSize(obj_ptr);
+    // On valgrind, the red zone has an impact...
+    const uint8_t* obj_ptr = reinterpret_cast<const uint8_t*>(obj);
+    size_t size_by_ptr = rosalloc_->UsableSize(
+        obj_ptr - (running_on_valgrind ? kDefaultValgrindRedZoneBytes : 0));
     if (size_by_size != size_by_ptr) {
       LOG(INFO) << "Found a bad sized obj of size " << size
                 << " at " << std::hex << reinterpret_cast<intptr_t>(obj_ptr) << std::dec
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 92c6f53..74d1a2b 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -17,6 +17,9 @@
 
 #include "rosalloc_space-inl.h"
 
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include "cutils/trace.h"
+
 #include "gc/accounting/card_table.h"
 #include "gc/accounting/space_bitmap-inl.h"
 #include "gc/heap.h"
@@ -41,10 +44,10 @@
 // TODO: Fix
 // template class ValgrindMallocSpace<RosAllocSpace, allocator::RosAlloc*>;
 
-RosAllocSpace::RosAllocSpace(const std::string& name, MemMap* mem_map,
-                             art::gc::allocator::RosAlloc* rosalloc, byte* begin, byte* end,
-                             byte* limit, size_t growth_limit, bool can_move_objects,
-                             size_t starting_size, size_t initial_size, bool low_memory_mode)
+RosAllocSpace::RosAllocSpace(MemMap* mem_map, size_t initial_size, const std::string& name,
+                             art::gc::allocator::RosAlloc* rosalloc, uint8_t* begin, uint8_t* end,
+                             uint8_t* limit, size_t growth_limit, bool can_move_objects,
+                             size_t starting_size, bool low_memory_mode)
     : MallocSpace(name, mem_map, begin, end, limit, growth_limit, true, can_move_objects,
                   starting_size, initial_size),
       rosalloc_(rosalloc), low_memory_mode_(low_memory_mode) {
@@ -56,28 +59,33 @@
                                                size_t growth_limit, size_t capacity,
                                                bool low_memory_mode, bool can_move_objects) {
   DCHECK(mem_map != nullptr);
+
+  bool running_on_valgrind = Runtime::Current()->RunningOnValgrind();
+
   allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size,
-                                                 capacity, low_memory_mode);
+                                                 capacity, low_memory_mode, running_on_valgrind);
   if (rosalloc == NULL) {
     LOG(ERROR) << "Failed to initialize rosalloc for alloc space (" << name << ")";
     return NULL;
   }
 
   // Protect memory beyond the starting size. MoreCore will add r/w permissions when necessory
-  byte* end = mem_map->Begin() + starting_size;
+  uint8_t* end = mem_map->Begin() + starting_size;
   if (capacity - starting_size > 0) {
     CHECK_MEMORY_CALL(mprotect, (end, capacity - starting_size, PROT_NONE), name);
   }
 
   // Everything is set so record in immutable structure and leave
-  byte* begin = mem_map->Begin();
+  uint8_t* begin = mem_map->Begin();
   // TODO: Fix RosAllocSpace to support valgrind. There is currently some issues with
   // AllocationSize caused by redzones. b/12944686
-  if (false && Runtime::Current()->GetHeap()->RunningOnValgrind()) {
-    LOG(FATAL) << "Unimplemented";
+  if (running_on_valgrind) {
+    return new ValgrindMallocSpace<RosAllocSpace, kDefaultValgrindRedZoneBytes, false, true>(
+        mem_map, initial_size, name, rosalloc, begin, end, begin + capacity, growth_limit,
+        can_move_objects, starting_size, low_memory_mode);
   } else {
-    return new RosAllocSpace(name, mem_map, rosalloc, begin, end, begin + capacity, growth_limit,
-                             can_move_objects, starting_size, initial_size, low_memory_mode);
+    return new RosAllocSpace(mem_map, initial_size, name, rosalloc, begin, end, begin + capacity,
+                             growth_limit, can_move_objects, starting_size, low_memory_mode);
   }
 }
 
@@ -86,7 +94,7 @@
 }
 
 RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_size,
-                                     size_t growth_limit, size_t capacity, byte* requested_begin,
+                                     size_t growth_limit, size_t capacity, uint8_t* requested_begin,
                                      bool low_memory_mode, bool can_move_objects) {
   uint64_t start_time = 0;
   if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
@@ -124,7 +132,8 @@
 
 allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start,
                                                    size_t initial_size,
-                                                   size_t maximum_size, bool low_memory_mode) {
+                                                   size_t maximum_size, bool low_memory_mode,
+                                                   bool running_on_valgrind) {
   // clear errno to allow PLOG on error
   errno = 0;
   // create rosalloc using our backing storage starting at begin and
@@ -134,7 +143,8 @@
       begin, morecore_start, maximum_size,
       low_memory_mode ?
           art::gc::allocator::RosAlloc::kPageReleaseModeAll :
-          art::gc::allocator::RosAlloc::kPageReleaseModeSizeAndEnd);
+          art::gc::allocator::RosAlloc::kPageReleaseModeSizeAndEnd,
+      running_on_valgrind);
   if (rosalloc != NULL) {
     rosalloc->SetFootprintLimit(initial_size);
   } else {
@@ -163,12 +173,19 @@
   return result;
 }
 
-MallocSpace* RosAllocSpace::CreateInstance(const std::string& name, MemMap* mem_map, void* allocator,
-                                           byte* begin, byte* end, byte* limit, size_t growth_limit,
+MallocSpace* RosAllocSpace::CreateInstance(MemMap* mem_map, const std::string& name,
+                                           void* allocator, uint8_t* begin, uint8_t* end,
+                                           uint8_t* limit, size_t growth_limit,
                                            bool can_move_objects) {
-  return new RosAllocSpace(name, mem_map, reinterpret_cast<allocator::RosAlloc*>(allocator),
-                           begin, end, limit, growth_limit, can_move_objects, starting_size_,
-                           initial_size_, low_memory_mode_);
+  if (Runtime::Current()->RunningOnValgrind()) {
+    return new ValgrindMallocSpace<RosAllocSpace, kDefaultValgrindRedZoneBytes, false, true>(
+        mem_map, initial_size_, name, reinterpret_cast<allocator::RosAlloc*>(allocator), begin, end,
+        limit, growth_limit, can_move_objects, starting_size_, low_memory_mode_);
+  } else {
+    return new RosAllocSpace(mem_map, initial_size_, name,
+                             reinterpret_cast<allocator::RosAlloc*>(allocator), begin, end, limit,
+                             growth_limit, can_move_objects, starting_size_, low_memory_mode_);
+  }
 }
 
 size_t RosAllocSpace::Free(Thread* self, mirror::Object* ptr) {
@@ -224,15 +241,6 @@
   return bytes_freed;
 }
 
-// Callback from rosalloc when it needs to increase the footprint
-extern "C" void* art_heap_rosalloc_morecore(allocator::RosAlloc* rosalloc, intptr_t increment) {
-  Heap* heap = Runtime::Current()->GetHeap();
-  RosAllocSpace* rosalloc_space = heap->GetRosAllocSpace(rosalloc);
-  DCHECK(rosalloc_space != nullptr);
-  DCHECK_EQ(rosalloc_space->GetRosAlloc(), rosalloc);
-  return rosalloc_space->MoreCore(increment);
-}
-
 size_t RosAllocSpace::Trim() {
   VLOG(heap) << "RosAllocSpace::Trim() ";
   {
@@ -338,6 +346,12 @@
   rosalloc_->RevokeAllThreadLocalRuns();
 }
 
+void RosAllocSpace::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
+  if (kIsDebugBuild) {
+    rosalloc_->AssertThreadLocalRunsAreRevoked(thread);
+  }
+}
+
 void RosAllocSpace::AssertAllThreadLocalBuffersAreRevoked() {
   if (kIsDebugBuild) {
     rosalloc_->AssertAllThreadLocalRunsAreRevoked();
@@ -352,10 +366,24 @@
   SetEnd(begin_ + starting_size_);
   delete rosalloc_;
   rosalloc_ = CreateRosAlloc(mem_map_->Begin(), starting_size_, initial_size_, Capacity(),
-                             low_memory_mode_);
+                             low_memory_mode_, Runtime::Current()->RunningOnValgrind());
   SetFootprintLimit(footprint_limit);
 }
 
 }  // namespace space
+
+namespace allocator {
+
+// Callback from rosalloc when it needs to increase the footprint.
+void* ArtRosAllocMoreCore(allocator::RosAlloc* rosalloc, intptr_t increment) {
+  Heap* heap = Runtime::Current()->GetHeap();
+  art::gc::space::RosAllocSpace* rosalloc_space = heap->GetRosAllocSpace(rosalloc);
+  DCHECK(rosalloc_space != nullptr);
+  DCHECK_EQ(rosalloc_space->GetRosAlloc(), rosalloc);
+  return rosalloc_space->MoreCore(increment);
+}
+
+}  // namespace allocator
+
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index f505305..c856e95 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -39,7 +39,7 @@
   // the caller should call Begin on the returned space to confirm the
   // request was granted.
   static RosAllocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit,
-                               size_t capacity, byte* requested_begin, bool low_memory_mode,
+                               size_t capacity, uint8_t* requested_begin, bool low_memory_mode,
                                bool can_move_objects);
   static RosAllocSpace* CreateFromMemMap(MemMap* mem_map, const std::string& name,
                                          size_t starting_size, size_t initial_size,
@@ -92,8 +92,8 @@
 
   void Clear() OVERRIDE;
 
-  MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator,
-                              byte* begin, byte* end, byte* limit, size_t growth_limit,
+  MallocSpace* CreateInstance(MemMap* mem_map, const std::string& name, void* allocator,
+                              uint8_t* begin, uint8_t* end, uint8_t* limit, size_t growth_limit,
                               bool can_move_objects) OVERRIDE;
 
   uint64_t GetBytesAllocated() OVERRIDE;
@@ -101,6 +101,7 @@
 
   void RevokeThreadLocalBuffers(Thread* thread);
   void RevokeAllThreadLocalBuffers();
+  void AssertThreadLocalBuffersAreRevoked(Thread* thread);
   void AssertAllThreadLocalBuffersAreRevoked();
 
   // Returns the class of a recently freed object.
@@ -125,9 +126,10 @@
   }
 
  protected:
-  RosAllocSpace(const std::string& name, MemMap* mem_map, allocator::RosAlloc* rosalloc,
-                byte* begin, byte* end, byte* limit, size_t growth_limit, bool can_move_objects,
-                size_t starting_size, size_t initial_size, bool low_memory_mode);
+  RosAllocSpace(MemMap* mem_map, size_t initial_size, const std::string& name,
+                allocator::RosAlloc* rosalloc, uint8_t* begin, uint8_t* end, uint8_t* limit,
+                size_t growth_limit, bool can_move_objects, size_t starting_size,
+                bool low_memory_mode);
 
  private:
   template<bool kThreadSafe = true>
@@ -136,10 +138,12 @@
 
   void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size,
                         size_t maximum_size, bool low_memory_mode) OVERRIDE {
-    return CreateRosAlloc(base, morecore_start, initial_size, maximum_size, low_memory_mode);
+    return CreateRosAlloc(base, morecore_start, initial_size, maximum_size, low_memory_mode,
+                          RUNNING_ON_VALGRIND != 0);
   }
   static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size,
-                                             size_t maximum_size, bool low_memory_mode);
+                                             size_t maximum_size, bool low_memory_mode,
+                                             bool running_on_valgrind);
 
   void InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
                           void* arg, bool do_null_callback_at_end)
diff --git a/runtime/gc/space/rosalloc_space_base_test.cc b/runtime/gc/space/rosalloc_space_base_test.cc
index c3157fa..0c5be03 100644
--- a/runtime/gc/space/rosalloc_space_base_test.cc
+++ b/runtime/gc/space/rosalloc_space_base_test.cc
@@ -21,7 +21,7 @@
 namespace space {
 
 MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
-                                 size_t capacity, byte* requested_begin) {
+                                 size_t capacity, uint8_t* requested_begin) {
   return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin,
                                Runtime::Current()->GetHeap()->IsLowMemoryMode(), false);
 }
diff --git a/runtime/gc/space/rosalloc_space_random_test.cc b/runtime/gc/space/rosalloc_space_random_test.cc
index 864bbc9..ca3aff4 100644
--- a/runtime/gc/space/rosalloc_space_random_test.cc
+++ b/runtime/gc/space/rosalloc_space_random_test.cc
@@ -21,7 +21,7 @@
 namespace space {
 
 MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
-                                 size_t capacity, byte* requested_begin) {
+                                 size_t capacity, uint8_t* requested_begin) {
   return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin,
                                Runtime::Current()->GetHeap()->IsLowMemoryMode(), false);
 }
diff --git a/runtime/gc/space/rosalloc_space_static_test.cc b/runtime/gc/space/rosalloc_space_static_test.cc
index c0e2ac8..a78623e 100644
--- a/runtime/gc/space/rosalloc_space_static_test.cc
+++ b/runtime/gc/space/rosalloc_space_static_test.cc
@@ -21,7 +21,7 @@
 namespace space {
 
 MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
-                                 size_t capacity, byte* requested_begin) {
+                                 size_t capacity, uint8_t* requested_begin) {
   return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin,
                                Runtime::Current()->GetHeap()->IsLowMemoryMode(), false);
 }
diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc
index bff28f6..486d79a 100644
--- a/runtime/gc/space/space.cc
+++ b/runtime/gc/space/space.cc
@@ -39,33 +39,33 @@
 }
 
 DlMallocSpace* Space::AsDlMallocSpace() {
-  LOG(FATAL) << "Unreachable";
-  return nullptr;
+  UNIMPLEMENTED(FATAL) << "Unreachable";
+  UNREACHABLE();
 }
 
 RosAllocSpace* Space::AsRosAllocSpace() {
-  LOG(FATAL) << "Unreachable";
-  return nullptr;
+  UNIMPLEMENTED(FATAL) << "Unreachable";
+  UNREACHABLE();
 }
 
 ZygoteSpace* Space::AsZygoteSpace() {
-  LOG(FATAL) << "Unreachable";
-  return nullptr;
+  UNIMPLEMENTED(FATAL) << "Unreachable";
+  UNREACHABLE();
 }
 
 BumpPointerSpace* Space::AsBumpPointerSpace() {
-  LOG(FATAL) << "Unreachable";
-  return nullptr;
+  UNIMPLEMENTED(FATAL) << "Unreachable";
+  UNREACHABLE();
 }
 
 AllocSpace* Space::AsAllocSpace() {
-  LOG(FATAL) << "Unimplemented";
-  return nullptr;
+  UNIMPLEMENTED(FATAL) << "Unreachable";
+  UNREACHABLE();
 }
 
 ContinuousMemMapAllocSpace* Space::AsContinuousMemMapAllocSpace() {
-  LOG(FATAL) << "Unimplemented";
-  return nullptr;
+  UNIMPLEMENTED(FATAL) << "Unreachable";
+  UNREACHABLE();
 }
 
 DiscontinuousSpace::DiscontinuousSpace(const std::string& name,
@@ -133,8 +133,8 @@
   mark_bitmap_->SetName(temp_name);
 }
 
-AllocSpace::SweepCallbackContext::SweepCallbackContext(bool swap_bitmaps, space::Space* space)
-    : swap_bitmaps(swap_bitmaps), space(space), self(Thread::Current()) {
+AllocSpace::SweepCallbackContext::SweepCallbackContext(bool swap_bitmaps_in, space::Space* space_in)
+    : swap_bitmaps(swap_bitmaps_in), space(space_in), self(Thread::Current()) {
 }
 
 }  // namespace space
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 523d4fe..860a4c9 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -246,27 +246,27 @@
 class ContinuousSpace : public Space {
  public:
   // Address at which the space begins.
-  byte* Begin() const {
+  uint8_t* Begin() const {
     return begin_;
   }
 
   // Current address at which the space ends, which may vary as the space is filled.
-  byte* End() const {
+  uint8_t* End() const {
     return end_.LoadRelaxed();
   }
 
   // The end of the address range covered by the space.
-  byte* Limit() const {
+  uint8_t* Limit() const {
     return limit_;
   }
 
   // Change the end of the space. Be careful with use since changing the end of a space to an
   // invalid value may break the GC.
-  void SetEnd(byte* end) {
+  void SetEnd(uint8_t* end) {
     end_.StoreRelaxed(end);
   }
 
-  void SetLimit(byte* limit) {
+  void SetLimit(uint8_t* limit) {
     limit_ = limit;
   }
 
@@ -286,7 +286,7 @@
   // Is object within this space? We check to see if the pointer is beyond the end first as
   // continuous spaces are iterated over from low to high.
   bool HasAddress(const mirror::Object* obj) const {
-    const byte* byte_ptr = reinterpret_cast<const byte*>(obj);
+    const uint8_t* byte_ptr = reinterpret_cast<const uint8_t*>(obj);
     return byte_ptr >= Begin() && byte_ptr < Limit();
   }
 
@@ -302,18 +302,18 @@
 
  protected:
   ContinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy,
-                  byte* begin, byte* end, byte* limit) :
+                  uint8_t* begin, uint8_t* end, uint8_t* limit) :
       Space(name, gc_retention_policy), begin_(begin), end_(end), limit_(limit) {
   }
 
   // The beginning of the storage for fast access.
-  byte* begin_;
+  uint8_t* begin_;
 
   // Current end of the space.
-  Atomic<byte*> end_;
+  Atomic<uint8_t*> end_;
 
   // Limit of the space.
-  byte* limit_;
+  uint8_t* limit_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ContinuousSpace);
@@ -369,7 +369,7 @@
   }
 
  protected:
-  MemMapSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end, byte* limit,
+  MemMapSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end, uint8_t* limit,
               GcRetentionPolicy gc_retention_policy)
       : ContinuousSpace(name, gc_retention_policy, begin, end, limit),
         mem_map_(mem_map) {
@@ -425,8 +425,8 @@
   std::unique_ptr<accounting::ContinuousSpaceBitmap> mark_bitmap_;
   std::unique_ptr<accounting::ContinuousSpaceBitmap> temp_bitmap_;
 
-  ContinuousMemMapAllocSpace(const std::string& name, MemMap* mem_map, byte* begin,
-                             byte* end, byte* limit, GcRetentionPolicy gc_retention_policy)
+  ContinuousMemMapAllocSpace(const std::string& name, MemMap* mem_map, uint8_t* begin,
+                             uint8_t* end, uint8_t* limit, GcRetentionPolicy gc_retention_policy)
       : MemMapSpace(name, mem_map, begin, end, limit, gc_retention_policy) {
   }
 
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index 0291155..9f39b80 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -110,7 +110,7 @@
   }
 
   typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit,
-                                        size_t capacity, byte* requested_begin);
+                                        size_t capacity, uint8_t* requested_begin);
   void InitTestBody(CreateSpaceFn create_space);
   void ZygoteSpaceTestBody(CreateSpaceFn create_space);
   void AllocAndFreeTestBody(CreateSpaceFn create_space);
@@ -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/space/valgrind_malloc_space-inl.h b/runtime/gc/space/valgrind_malloc_space-inl.h
index 966c276..793d798 100644
--- a/runtime/gc/space/valgrind_malloc_space-inl.h
+++ b/runtime/gc/space/valgrind_malloc_space-inl.h
@@ -21,68 +21,165 @@
 
 #include <memcheck/memcheck.h>
 
+#include "valgrind_settings.h"
+
 namespace art {
 namespace gc {
 namespace space {
 
-// Number of bytes to use as a red zone (rdz). A red zone of this size will be placed before and
-// after each allocation. 8 bytes provides long/double alignment.
-static constexpr size_t kValgrindRedZoneBytes = 8;
+namespace valgrind_details {
 
-template <typename S, typename A>
-mirror::Object* ValgrindMallocSpace<S, A>::AllocWithGrowth(Thread* self, size_t num_bytes,
-                                                           size_t* bytes_allocated,
-                                                           size_t* usable_size) {
+template <size_t kValgrindRedZoneBytes, bool kUseObjSizeForUsable>
+inline mirror::Object* AdjustForValgrind(void* obj_with_rdz, size_t num_bytes,
+                                         size_t bytes_allocated, size_t usable_size,
+                                         size_t* bytes_allocated_out, size_t* usable_size_out) {
+  if (bytes_allocated_out != nullptr) {
+    *bytes_allocated_out = bytes_allocated;
+  }
+
+  // This cuts over-provision and is a trade-off between testing the over-provisioning code paths
+  // vs checking overflows in the regular paths.
+  if (usable_size_out != nullptr) {
+    if (kUseObjSizeForUsable) {
+      *usable_size_out = num_bytes;
+    } else {
+      *usable_size_out = usable_size - 2 * kValgrindRedZoneBytes;
+    }
+  }
+
+  // Left redzone.
+  VALGRIND_MAKE_MEM_NOACCESS(obj_with_rdz, kValgrindRedZoneBytes);
+
+  // Make requested memory readable.
+  // (If the allocator assumes memory is zeroed out, we might get UNDEFINED warnings, so make
+  //  everything DEFINED initially.)
+  mirror::Object* result = reinterpret_cast<mirror::Object*>(
+      reinterpret_cast<uint8_t*>(obj_with_rdz) + kValgrindRedZoneBytes);
+  VALGRIND_MAKE_MEM_DEFINED(result, num_bytes);
+
+  // Right redzone. Assumes that if bytes_allocated > usable_size, then the difference is
+  // management data at the upper end, and for simplicity we will not protect that.
+  // At the moment, this fits RosAlloc (no management data in a slot, usable_size == alloc_size)
+  // and DlMalloc (allocation_size = (usable_size == num_bytes) + 4, 4 is management)
+  VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast<uint8_t*>(result) + num_bytes,
+                             usable_size - (num_bytes + kValgrindRedZoneBytes));
+
+  return result;
+}
+
+inline size_t GetObjSizeNoThreadSafety(mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS {
+  return obj->SizeOf<kVerifyNone>();
+}
+
+}  // namespace valgrind_details
+
+template <typename S,
+          size_t kValgrindRedZoneBytes,
+          bool kAdjustForRedzoneInAllocSize,
+          bool kUseObjSizeForUsable>
+mirror::Object*
+ValgrindMallocSpace<S,
+                    kValgrindRedZoneBytes,
+                    kAdjustForRedzoneInAllocSize,
+                    kUseObjSizeForUsable>::AllocWithGrowth(
+    Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out) {
+  size_t bytes_allocated;
+  size_t usable_size;
   void* obj_with_rdz = S::AllocWithGrowth(self, num_bytes + 2 * kValgrindRedZoneBytes,
-                                          bytes_allocated, usable_size);
+                                          &bytes_allocated, &usable_size);
   if (obj_with_rdz == nullptr) {
     return nullptr;
   }
-  mirror::Object* result = reinterpret_cast<mirror::Object*>(
-      reinterpret_cast<byte*>(obj_with_rdz) + kValgrindRedZoneBytes);
-  // Make redzones as no access.
-  VALGRIND_MAKE_MEM_NOACCESS(obj_with_rdz, kValgrindRedZoneBytes);
-  VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast<byte*>(result) + num_bytes, kValgrindRedZoneBytes);
-  return result;
+
+  return valgrind_details::AdjustForValgrind<kValgrindRedZoneBytes,
+                                             kUseObjSizeForUsable>(obj_with_rdz, num_bytes,
+                                                                   bytes_allocated, usable_size,
+                                                                   bytes_allocated_out,
+                                                                   usable_size_out);
 }
 
-template <typename S, typename A>
-mirror::Object* ValgrindMallocSpace<S, A>::Alloc(Thread* self, size_t num_bytes,
-                                                 size_t* bytes_allocated,
-                                                 size_t* usable_size) {
-  void* obj_with_rdz = S::Alloc(self, num_bytes + 2 * kValgrindRedZoneBytes, bytes_allocated,
-                                usable_size);
+template <typename S,
+          size_t kValgrindRedZoneBytes,
+          bool kAdjustForRedzoneInAllocSize,
+          bool kUseObjSizeForUsable>
+mirror::Object* ValgrindMallocSpace<S,
+                                    kValgrindRedZoneBytes,
+                                    kAdjustForRedzoneInAllocSize,
+                                    kUseObjSizeForUsable>::Alloc(
+    Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out) {
+  size_t bytes_allocated;
+  size_t usable_size;
+  void* obj_with_rdz = S::Alloc(self, num_bytes + 2 * kValgrindRedZoneBytes,
+                                &bytes_allocated, &usable_size);
   if (obj_with_rdz == nullptr) {
     return nullptr;
   }
-  mirror::Object* result = reinterpret_cast<mirror::Object*>(
-      reinterpret_cast<byte*>(obj_with_rdz) + kValgrindRedZoneBytes);
-  // Make redzones as no access.
-  VALGRIND_MAKE_MEM_NOACCESS(obj_with_rdz, kValgrindRedZoneBytes);
-  VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast<byte*>(result) + num_bytes, kValgrindRedZoneBytes);
-  return result;
+
+  return valgrind_details::AdjustForValgrind<kValgrindRedZoneBytes,
+                                             kUseObjSizeForUsable>(obj_with_rdz, num_bytes,
+                                                                   bytes_allocated, usable_size,
+                                                                   bytes_allocated_out,
+                                                                   usable_size_out);
 }
 
-template <typename S, typename A>
-size_t ValgrindMallocSpace<S, A>::AllocationSize(mirror::Object* obj, size_t* usable_size) {
+template <typename S,
+          size_t kValgrindRedZoneBytes,
+          bool kAdjustForRedzoneInAllocSize,
+          bool kUseObjSizeForUsable>
+size_t ValgrindMallocSpace<S,
+                           kValgrindRedZoneBytes,
+                           kAdjustForRedzoneInAllocSize,
+                           kUseObjSizeForUsable>::AllocationSize(
+    mirror::Object* obj, size_t* usable_size) {
   size_t result = S::AllocationSize(reinterpret_cast<mirror::Object*>(
-      reinterpret_cast<byte*>(obj) - kValgrindRedZoneBytes), usable_size);
+      reinterpret_cast<uint8_t*>(obj) - (kAdjustForRedzoneInAllocSize ? kValgrindRedZoneBytes : 0)),
+      usable_size);
+  if (usable_size != nullptr) {
+    if (kUseObjSizeForUsable) {
+      *usable_size = valgrind_details::GetObjSizeNoThreadSafety(obj);
+    } else {
+      *usable_size = *usable_size - 2 * kValgrindRedZoneBytes;
+    }
+  }
   return result;
 }
 
-template <typename S, typename A>
-size_t ValgrindMallocSpace<S, A>::Free(Thread* self, mirror::Object* ptr) {
+template <typename S,
+          size_t kValgrindRedZoneBytes,
+          bool kAdjustForRedzoneInAllocSize,
+          bool kUseObjSizeForUsable>
+size_t ValgrindMallocSpace<S,
+                           kValgrindRedZoneBytes,
+                           kAdjustForRedzoneInAllocSize,
+                           kUseObjSizeForUsable>::Free(
+    Thread* self, mirror::Object* ptr) {
   void* obj_after_rdz = reinterpret_cast<void*>(ptr);
-  void* obj_with_rdz = reinterpret_cast<byte*>(obj_after_rdz) - kValgrindRedZoneBytes;
+  uint8_t* obj_with_rdz = reinterpret_cast<uint8_t*>(obj_after_rdz) - kValgrindRedZoneBytes;
   // Make redzones undefined.
-  size_t usable_size = 0;
-  AllocationSize(ptr, &usable_size);
-  VALGRIND_MAKE_MEM_UNDEFINED(obj_with_rdz, usable_size);
+  size_t usable_size;
+  size_t allocation_size = AllocationSize(ptr, &usable_size);
+
+  // Unprotect the allocation.
+  // Use the obj-size-for-usable flag to determine whether usable_size is the more important one,
+  // e.g., whether there's data in the allocation_size (and usable_size can't be trusted).
+  if (kUseObjSizeForUsable) {
+    VALGRIND_MAKE_MEM_UNDEFINED(obj_with_rdz, allocation_size);
+  } else {
+    VALGRIND_MAKE_MEM_UNDEFINED(obj_with_rdz, usable_size + 2 * kValgrindRedZoneBytes);
+  }
+
   return S::Free(self, reinterpret_cast<mirror::Object*>(obj_with_rdz));
 }
 
-template <typename S, typename A>
-size_t ValgrindMallocSpace<S, A>::FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
+template <typename S,
+          size_t kValgrindRedZoneBytes,
+          bool kAdjustForRedzoneInAllocSize,
+          bool kUseObjSizeForUsable>
+size_t ValgrindMallocSpace<S,
+                           kValgrindRedZoneBytes,
+                           kAdjustForRedzoneInAllocSize,
+                           kUseObjSizeForUsable>::FreeList(
+    Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
   size_t freed = 0;
   for (size_t i = 0; i < num_ptrs; i++) {
     freed += Free(self, ptrs[i]);
@@ -91,15 +188,18 @@
   return freed;
 }
 
-template <typename S, typename A>
-ValgrindMallocSpace<S, A>::ValgrindMallocSpace(const std::string& name, MemMap* mem_map,
-                                               A allocator, byte* begin,
-                                               byte* end, byte* limit, size_t growth_limit,
-                                               size_t initial_size,
-                                               bool can_move_objects, size_t starting_size) :
-    S(name, mem_map, allocator, begin, end, limit, growth_limit, can_move_objects, starting_size,
-      initial_size) {
-  VALGRIND_MAKE_MEM_UNDEFINED(mem_map->Begin() + initial_size, mem_map->Size() - initial_size);
+template <typename S,
+          size_t kValgrindRedZoneBytes,
+          bool kAdjustForRedzoneInAllocSize,
+          bool kUseObjSizeForUsable>
+template <typename... Params>
+ValgrindMallocSpace<S,
+                    kValgrindRedZoneBytes,
+                    kAdjustForRedzoneInAllocSize,
+                    kUseObjSizeForUsable>::ValgrindMallocSpace(
+    MemMap* mem_map, size_t initial_size, Params... params) : S(mem_map, initial_size, params...) {
+  VALGRIND_MAKE_MEM_UNDEFINED(mem_map->Begin() + initial_size,
+                              mem_map->Size() - initial_size);
 }
 
 }  // namespace space
diff --git a/runtime/gc/space/valgrind_malloc_space.h b/runtime/gc/space/valgrind_malloc_space.h
index 200ad83..d102f49 100644
--- a/runtime/gc/space/valgrind_malloc_space.h
+++ b/runtime/gc/space/valgrind_malloc_space.h
@@ -27,7 +27,10 @@
 
 // A specialization of DlMallocSpace/RosAllocSpace that places valgrind red zones around
 // allocations.
-template <typename BaseMallocSpaceType, typename AllocatorType>
+template <typename BaseMallocSpaceType,
+          size_t kValgrindRedZoneBytes,
+          bool kAdjustForRedzoneInAllocSize,
+          bool kUseObjSizeForUsable>
 class ValgrindMallocSpace FINAL : public BaseMallocSpaceType {
  public:
   mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated,
@@ -44,11 +47,11 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void RegisterRecentFree(mirror::Object* ptr) OVERRIDE {
+    UNUSED(ptr);
   }
 
-  ValgrindMallocSpace(const std::string& name, MemMap* mem_map, AllocatorType allocator,
-                      byte* begin, byte* end, byte* limit, size_t growth_limit,
-                      size_t initial_size, bool can_move_objects, size_t starting_size);
+  template <typename... Params>
+  explicit ValgrindMallocSpace(MemMap* mem_map, size_t initial_size, Params... params);
   virtual ~ValgrindMallocSpace() {}
 
  private:
diff --git a/runtime/gc/space/valgrind_settings.h b/runtime/gc/space/valgrind_settings.h
new file mode 100644
index 0000000..73da0fd
--- /dev/null
+++ b/runtime/gc/space/valgrind_settings.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_RUNTIME_GC_SPACE_VALGRIND_SETTINGS_H_
+#define ART_RUNTIME_GC_SPACE_VALGRIND_SETTINGS_H_
+
+namespace art {
+namespace gc {
+namespace space {
+
+// Default number of bytes to use as a red zone (rdz). A red zone of this size will be placed before
+// and after each allocation. 8 bytes provides long/double alignment.
+static constexpr size_t kDefaultValgrindRedZoneBytes = 8;
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_SPACE_VALGRIND_SETTINGS_H_
diff --git a/runtime/gc/space/zygote_space.cc b/runtime/gc/space/zygote_space.cc
index 51d84f5..a868e68 100644
--- a/runtime/gc/space/zygote_space.cc
+++ b/runtime/gc/space/zygote_space.cc
@@ -32,6 +32,7 @@
       : objects_allocated_(objects_allocated) {}
 
   void operator()(mirror::Object* obj) const {
+    UNUSED(obj);
     ++*objects_allocated_;
   }
 
@@ -58,7 +59,8 @@
 }
 
 void ZygoteSpace::Clear() {
-  LOG(FATAL) << "Unimplemented";
+  UNIMPLEMENTED(FATAL);
+  UNREACHABLE();
 }
 
 ZygoteSpace::ZygoteSpace(const std::string& name, MemMap* mem_map, size_t objects_allocated)
@@ -75,30 +77,29 @@
       << ",name=\"" << GetName() << "\"]";
 }
 
-mirror::Object* ZygoteSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
-                                   size_t* usable_size) {
+mirror::Object* ZygoteSpace::Alloc(Thread*, size_t, size_t*, size_t*) {
   UNIMPLEMENTED(FATAL);
-  return nullptr;
+  UNREACHABLE();
 }
 
-size_t ZygoteSpace::AllocationSize(mirror::Object* obj, size_t* usable_size) {
+size_t ZygoteSpace::AllocationSize(mirror::Object*, size_t*) {
   UNIMPLEMENTED(FATAL);
-  return 0;
+  UNREACHABLE();
 }
 
-size_t ZygoteSpace::Free(Thread* self, mirror::Object* ptr) {
+size_t ZygoteSpace::Free(Thread*, mirror::Object*) {
   UNIMPLEMENTED(FATAL);
-  return 0;
+  UNREACHABLE();
 }
 
-size_t ZygoteSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
+size_t ZygoteSpace::FreeList(Thread*, size_t, mirror::Object**) {
   UNIMPLEMENTED(FATAL);
-  return 0;
+  UNREACHABLE();
 }
 
-void ZygoteSpace::LogFragmentationAllocFailure(std::ostream& /*os*/,
-                                               size_t /*failed_alloc_bytes*/) {
+void ZygoteSpace::LogFragmentationAllocFailure(std::ostream&, size_t) {
   UNIMPLEMENTED(FATAL);
+  UNREACHABLE();
 }
 
 void ZygoteSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) {
diff --git a/runtime/globals.h b/runtime/globals.h
index 107e064..4d33196 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -24,22 +24,14 @@
 
 namespace art {
 
-typedef uint8_t byte;
-typedef intptr_t word;
-typedef uintptr_t uword;
-
 static constexpr size_t KB = 1024;
 static constexpr size_t MB = KB * KB;
 static constexpr size_t GB = KB * KB * KB;
 
 // Runtime sizes.
-static constexpr size_t kWordSize = sizeof(word);
-static constexpr size_t kPointerSize = sizeof(void*);
-
 static constexpr size_t kBitsPerByte = 8;
 static constexpr size_t kBitsPerByteLog2 = 3;
-static constexpr int kBitsPerWord = kWordSize * kBitsPerByte;
-static constexpr size_t kWordHighBitMask = static_cast<size_t>(1) << (kBitsPerWord - 1);
+static constexpr int kBitsPerIntPtrT = sizeof(intptr_t) * kBitsPerByte;
 
 // Required stack alignment
 static constexpr size_t kStackAlignment = 16;
@@ -120,6 +112,8 @@
 
 static constexpr bool kDefaultMustRelocate = true;
 
+static constexpr bool kArm32QuickCodeUseSoftFloat = false;
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_GLOBALS_H_
diff --git a/runtime/handle.h b/runtime/handle.h
index f9864dc..6af3220 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -20,6 +20,7 @@
 #include "base/casts.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/value_object.h"
 #include "stack.h"
 
 namespace art {
@@ -30,23 +31,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 ValueObject {
  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_) {
@@ -71,11 +72,11 @@
 
  protected:
   template<typename S>
-  explicit ConstHandle(StackReference<S>* reference)
+  explicit Handle(StackReference<S>* reference)
       : reference_(reference) {
   }
   template<typename S>
-  explicit ConstHandle(const ConstHandle<S>& handle)
+  explicit Handle(const Handle<S>& handle)
       : reference_(handle.reference_) {
   }
 
@@ -91,7 +92,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,24 +100,25 @@
 
 // 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_) {
@@ -127,13 +129,13 @@
   }
 
   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) {
   }
 
   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-inl.h b/runtime/handle_scope-inl.h
index da28ed7..9ddaf61 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -26,9 +26,10 @@
 
 template<size_t kNumReferences>
 inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
-    : HandleScope(kNumReferences), self_(self), pos_(0) {
+    : HandleScope(self->GetTopHandleScope(), kNumReferences), self_(self), pos_(0) {
+  static_assert(kNumReferences >= 1, "StackHandleScope must contain at least 1 reference");
   // TODO: Figure out how to use a compile assert.
-  DCHECK_EQ(&references_[0], &references_storage_[0]);
+  CHECK_EQ(&storage_[0], GetReferences());
   for (size_t i = 0; i < kNumReferences; ++i) {
     SetReference(i, fill_value);
   }
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 5050872..2c4f0f9 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -66,33 +66,28 @@
     return link_;
   }
 
-  void SetLink(HandleScope* link) {
-    DCHECK_NE(this, link);
-    link_ = link;
-  }
-
-  // Sets the number_of_references_ field for constructing tables out of raw memory. Warning: will
-  // not resize anything.
-  void SetNumberOfReferences(uint32_t num_references) {
-    number_of_references_ = num_references;
-  }
-
-  mirror::Object* GetReference(size_t i) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      ALWAYS_INLINE {
+  ALWAYS_INLINE mirror::Object* GetReference(size_t i) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_LT(i, number_of_references_);
-    return references_[i].AsMirrorPtr();
+    return GetReferences()[i].AsMirrorPtr();
   }
 
-  Handle<mirror::Object> GetHandle(size_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      ALWAYS_INLINE {
+  ALWAYS_INLINE Handle<mirror::Object> GetHandle(size_t i)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_LT(i, number_of_references_);
-    return Handle<mirror::Object>(&references_[i]);
+    return Handle<mirror::Object>(&GetReferences()[i]);
   }
 
-  void SetReference(size_t i, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      ALWAYS_INLINE {
+  ALWAYS_INLINE MutableHandle<mirror::Object> GetMutableHandle(size_t i)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_LT(i, number_of_references_);
-    references_[i].Assign(object);
+    return MutableHandle<mirror::Object>(&GetReferences()[i]);
+  }
+
+  ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK_LT(i, number_of_references_);
+    GetReferences()[i].Assign(object);
   }
 
   bool Contains(StackReference<mirror::Object>* handle_scope_entry) const {
@@ -100,25 +95,31 @@
     // jni_compiler should have a jobject/jclass as a native method is
     // passed in a this pointer or a class
     DCHECK_GT(number_of_references_, 0U);
-    return &references_[0] <= handle_scope_entry &&
-        handle_scope_entry <= &references_[number_of_references_ - 1];
+    return &GetReferences()[0] <= handle_scope_entry &&
+        handle_scope_entry <= &GetReferences()[number_of_references_ - 1];
   }
 
-  // Offset of link within HandleScope, used by generated code
-  static size_t LinkOffset(size_t pointer_size) {
+  // Offset of link within HandleScope, used by generated code.
+  static size_t LinkOffset(size_t pointer_size ATTRIBUTE_UNUSED) {
     return 0;
   }
 
-  // Offset of length within handle scope, used by generated code
+  // Offset of length within handle scope, used by generated code.
   static size_t NumberOfReferencesOffset(size_t pointer_size) {
     return pointer_size;
   }
 
-  // Offset of link within handle scope, used by generated code
+  // Offset of link within handle scope, used by generated code.
   static size_t ReferencesOffset(size_t pointer_size) {
     return pointer_size + sizeof(number_of_references_);
   }
 
+  // Placement new creation.
+  static HandleScope* Create(void* storage, HandleScope* link, uint32_t num_references)
+      WARN_UNUSED {
+    return new (storage) HandleScope(link, num_references);
+  }
+
  protected:
   // Return backing storage used for references.
   ALWAYS_INLINE StackReference<mirror::Object>* GetReferences() const {
@@ -130,29 +131,35 @@
       link_(nullptr), number_of_references_(number_of_references) {
   }
 
-  HandleScope* link_;
-  uint32_t number_of_references_;
+  // Semi-hidden constructor. Construction expected by generated code and StackHandleScope.
+  explicit HandleScope(HandleScope* link, uint32_t num_references) :
+      link_(link), number_of_references_(num_references) {
+  }
 
-  // number_of_references_ are available if this is allocated and filled in by jni_compiler.
-  StackReference<mirror::Object> references_[0];
+  // Link-list of handle scopes. The root is held by a Thread.
+  HandleScope* const link_;
+
+  // Number of handlerized references.
+  const uint32_t number_of_references_;
+
+  // Storage for references.
+  // StackReference<mirror::Object> references_[number_of_references_]
 
  private:
-  template<size_t kNumReferences> friend class StackHandleScope;
-
   DISALLOW_COPY_AND_ASSIGN(HandleScope);
 };
 
 // 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:
@@ -167,9 +174,9 @@
   ALWAYS_INLINE ~StackHandleScope();
 
   template<class T>
-  ALWAYS_INLINE Handle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE MutableHandle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     SetReference(pos_, object);
-    Handle<T> h(GetHandle<T>(pos_));
+    MutableHandle<T> h(GetHandle<T>(pos_));
     pos_++;
     return h;
   }
@@ -178,7 +185,7 @@
   ALWAYS_INLINE HandleWrapper<T> NewHandleWrapper(T** object)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     SetReference(pos_, *object);
-    Handle<T> h(GetHandle<T>(pos_));
+    MutableHandle<T> h(GetHandle<T>(pos_));
     pos_++;
     return HandleWrapper<T>(object, h);
   }
@@ -191,14 +198,13 @@
 
  private:
   template<class T>
-  ALWAYS_INLINE Handle<T> GetHandle(size_t i)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE MutableHandle<T> GetHandle(size_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_LT(i, kNumReferences);
-    return Handle<T>(&GetReferences()[i]);
+    return MutableHandle<T>(&GetReferences()[i]);
   }
 
   // Reference storage needs to be first as expected by the HandleScope layout.
-  StackReference<mirror::Object> references_storage_[kNumReferences];
+  StackReference<mirror::Object> storage_[kNumReferences];
 
   // The thread that the stack handle scope is a linked list upon. The stack handle scope will
   // push and pop itself from this thread.
diff --git a/runtime/handle_scope_test.cc b/runtime/handle_scope_test.cc
index de563c1..dc99987 100644
--- a/runtime/handle_scope_test.cc
+++ b/runtime/handle_scope_test.cc
@@ -25,7 +25,7 @@
 template<size_t kNumReferences>
 class NoThreadStackHandleScope : public HandleScope {
  public:
-  explicit NoThreadStackHandleScope() : HandleScope(kNumReferences) {
+  explicit NoThreadStackHandleScope(HandleScope* link) : HandleScope(link, kNumReferences) {
   }
   ~NoThreadStackHandleScope() {
   }
@@ -41,22 +41,20 @@
 TEST(HandleScopeTest, Offsets) NO_THREAD_SAFETY_ANALYSIS {
   // As the members of HandleScope are private, we cannot use OFFSETOF_MEMBER
   // here. So do the inverse: set some data, and access it through pointers created from the offsets.
-  NoThreadStackHandleScope<1> test_table;
+  NoThreadStackHandleScope<0x9ABC> test_table(reinterpret_cast<HandleScope*>(0x5678));
   test_table.SetReference(0, reinterpret_cast<mirror::Object*>(0x1234));
-  test_table.SetLink(reinterpret_cast<HandleScope*>(0x5678));
-  test_table.SetNumberOfReferences(0x9ABC);
 
-  byte* table_base_ptr = reinterpret_cast<byte*>(&test_table);
+  uint8_t* table_base_ptr = reinterpret_cast<uint8_t*>(&test_table);
 
   {
     uintptr_t* link_ptr = reinterpret_cast<uintptr_t*>(table_base_ptr +
-        HandleScope::LinkOffset(kPointerSize));
+        HandleScope::LinkOffset(sizeof(void*)));
     EXPECT_EQ(*link_ptr, static_cast<size_t>(0x5678));
   }
 
   {
     uint32_t* num_ptr = reinterpret_cast<uint32_t*>(table_base_ptr +
-        HandleScope::NumberOfReferencesOffset(kPointerSize));
+        HandleScope::NumberOfReferencesOffset(sizeof(void*)));
     EXPECT_EQ(*num_ptr, static_cast<size_t>(0x9ABC));
   }
 
@@ -66,7 +64,7 @@
     EXPECT_EQ(sizeof(StackReference<mirror::Object>), sizeof(uint32_t));
 
     uint32_t* ref_ptr = reinterpret_cast<uint32_t*>(table_base_ptr +
-        HandleScope::ReferencesOffset(kPointerSize));
+        HandleScope::ReferencesOffset(sizeof(void*)));
     EXPECT_EQ(*ref_ptr, static_cast<uint32_t>(0x1234));
   }
 }
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index fd67197..14d7432 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -637,8 +637,8 @@
     // U4: size of identifiers.  We're using addresses as IDs and our heap references are stored
     // as uint32_t.
     // Note of warning: hprof-conv hard-codes the size of identifiers to 4.
-    COMPILE_ASSERT(sizeof(mirror::HeapReference<mirror::Object>) == sizeof(uint32_t),
-      UnexpectedHeapReferenceSize);
+    static_assert(sizeof(mirror::HeapReference<mirror::Object>) == sizeof(uint32_t),
+                  "Unexpected HeapReference size");
     U4_TO_BUF_BE(buf, 0, sizeof(uint32_t));
     fwrite(buf, 1, sizeof(uint32_t), header_fp_);
 
@@ -719,9 +719,9 @@
   case 'D': ret = hprof_basic_double;  size = 8; break;
   case 'B': ret = hprof_basic_byte;    size = 1; break;
   case 'S': ret = hprof_basic_short;   size = 2; break;
-  default: CHECK(false);
   case 'I': ret = hprof_basic_int;     size = 4; break;
   case 'J': ret = hprof_basic_long;    size = 8; break;
+  default: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE();
   }
 
   if (sizeOut != NULL) {
@@ -742,9 +742,9 @@
   case Primitive::kPrimDouble:  ret = hprof_basic_double;  size = 8; break;
   case Primitive::kPrimByte:    ret = hprof_basic_byte;    size = 1; break;
   case Primitive::kPrimShort:   ret = hprof_basic_short;   size = 2; break;
-  default: CHECK(false);
   case Primitive::kPrimInt:     ret = hprof_basic_int;     size = 4; break;
   case Primitive::kPrimLong:    ret = hprof_basic_long;    size = 8; break;
+  default: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE();
   }
 
   if (sizeOut != NULL) {
diff --git a/runtime/image.cc b/runtime/image.cc
index 81044df..aee84bc3 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -23,8 +23,8 @@
 
 namespace art {
 
-const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const byte ImageHeader::kImageVersion[] = { '0', '1', '1', '\0' };
+const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '1', '2', '\0' };
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index a77aec4..7e2b847 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -45,8 +45,8 @@
   bool IsValid() const;
   const char* GetMagic() const;
 
-  byte* GetImageBegin() const {
-    return reinterpret_cast<byte*>(image_begin_);
+  uint8_t* GetImageBegin() const {
+    return reinterpret_cast<uint8_t*>(image_begin_);
   }
 
   size_t GetImageSize() const {
@@ -69,20 +69,20 @@
     oat_checksum_ = oat_checksum;
   }
 
-  byte* GetOatFileBegin() const {
-    return reinterpret_cast<byte*>(oat_file_begin_);
+  uint8_t* GetOatFileBegin() const {
+    return reinterpret_cast<uint8_t*>(oat_file_begin_);
   }
 
-  byte* GetOatDataBegin() const {
-    return reinterpret_cast<byte*>(oat_data_begin_);
+  uint8_t* GetOatDataBegin() const {
+    return reinterpret_cast<uint8_t*>(oat_data_begin_);
   }
 
-  byte* GetOatDataEnd() const {
-    return reinterpret_cast<byte*>(oat_data_end_);
+  uint8_t* GetOatDataEnd() const {
+    return reinterpret_cast<uint8_t*>(oat_data_end_);
   }
 
-  byte* GetOatFileEnd() const {
-    return reinterpret_cast<byte*>(oat_file_end_);
+  uint8_t* GetOatFileEnd() const {
+    return reinterpret_cast<uint8_t*>(oat_file_end_);
   }
 
   off_t GetPatchDelta() const {
@@ -127,11 +127,11 @@
   }
 
  private:
-  static const byte kImageMagic[4];
-  static const byte kImageVersion[4];
+  static const uint8_t kImageMagic[4];
+  static const uint8_t kImageVersion[4];
 
-  byte magic_[4];
-  byte version_[4];
+  uint8_t magic_[4];
+  uint8_t version_[4];
 
   // Required base address for mapping the image.
   uint32_t image_begin_;
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index 2bf6ab9..e571a0e 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 {
@@ -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].GetReference()->Read<kWithoutReadBarrier>();
-  if (LIKELY(obj != kClearedJniWeakGlobal)) {
-    // The read barrier or VerifyObject won't handle kClearedJniWeakGlobal.
-    obj = table_[idx].GetReference()->Read();
-    VerifyObject(obj);
-  }
+  mirror::Object* obj = table_[idx].GetReference()->Read<kReadBarrierOption>();
+  VerifyObject(obj);
   return obj;
 }
 
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 2b1a257..4d177a3 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -56,7 +56,8 @@
 
 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.";
   }
@@ -125,7 +126,7 @@
   }
   table_[index].Add(obj);
   result = ToIndirectRef(index);
-  if (false) {
+  if ((false)) {
     LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex
               << " holes=" << segment_state_.parts.numHoles;
   }
@@ -135,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);
+    }
   }
 }
 
@@ -190,7 +193,7 @@
     int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
     if (numHoles != 0) {
       while (--topIndex > bottomIndex && numHoles != 0) {
-        if (false) {
+        if ((false)) {
           LOG(INFO) << "+++ checking for hole at " << topIndex - 1
                     << " (cookie=" << cookie << ") val="
                     << table_[topIndex - 1].GetReference()->Read<kWithoutReadBarrier>();
@@ -198,7 +201,7 @@
         if (!table_[topIndex - 1].GetReference()->IsNull()) {
           break;
         }
-        if (false) {
+        if ((false)) {
           LOG(INFO) << "+++ ate hole at " << (topIndex - 1);
         }
         numHoles--;
@@ -207,7 +210,7 @@
       segment_state_.parts.topIndex = topIndex;
     } else {
       segment_state_.parts.topIndex = topIndex-1;
-      if (false) {
+      if ((false)) {
         LOG(INFO) << "+++ ate last entry " << topIndex - 1;
       }
     }
@@ -225,7 +228,7 @@
 
     *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
     segment_state_.parts.numHoles++;
-    if (false) {
+    if ((false)) {
       LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segment_state_.parts.numHoles;
     }
   }
@@ -236,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);
   }
@@ -248,10 +256,6 @@
     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].GetReference()->Read();
       entries.push_back(GcRoot<mirror::Object>(obj));
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index 5a178ea..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.
  *
@@ -224,12 +222,10 @@
   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;
   }
 
@@ -243,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_].GetReference()->IsNull() ||
-            table_[i_].GetReference()->Read<kWithoutReadBarrier>() == kClearedJniWeakGlobal)) {
-      ++i_;
-    }
-  }
-
   IrtEntry* const table_;
   size_t i_;
-  size_t capacity_;
+  const size_t capacity_;
 };
 
 bool inline operator==(const IrtIterator& lhs, const IrtIterator& rhs) {
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.cc b/runtime/instruction_set.cc
index 644e055..e165a75 100644
--- a/runtime/instruction_set.cc
+++ b/runtime/instruction_set.cc
@@ -16,6 +16,13 @@
 
 #include "instruction_set.h"
 
+#include <signal.h>
+#include <fstream>
+
+#include "base/casts.h"
+#include "base/stringprintf.h"
+#include "utils.h"
+
 namespace art {
 
 const char* GetInstructionSetString(const InstructionSet isa) {
@@ -35,7 +42,7 @@
       return "none";
     default:
       LOG(FATAL) << "Unknown ISA " << isa;
-      return nullptr;
+      UNREACHABLE();
   }
 }
 
@@ -117,15 +124,384 @@
   }
 }
 
-std::string InstructionSetFeatures::GetFeatureString() const {
-  std::string result;
-  if ((mask_ & kHwDiv) != 0) {
-    result += "div";
+const InstructionSetFeatures* InstructionSetFeatures::FromVariant(InstructionSet isa,
+                                                                  const std::string& variant,
+                                                                  std::string* error_msg) {
+  const InstructionSetFeatures* result;
+  switch (isa) {
+    case kArm:
+    case kThumb2:
+      result = ArmInstructionSetFeatures::FromVariant(variant, error_msg);
+      break;
+    default:
+      result = UnknownInstructionSetFeatures::Unknown(isa);
+      break;
   }
-  if (result.size() == 0) {
-    result = "none";
+  CHECK_EQ(result == nullptr, error_msg->size() != 0);
+  return result;
+}
+
+const InstructionSetFeatures* InstructionSetFeatures::FromFeatureString(InstructionSet isa,
+                                                                        const std::string& feature_list,
+                                                                        std::string* error_msg) {
+  const InstructionSetFeatures* result;
+  switch (isa) {
+    case kArm:
+    case kThumb2:
+      result = ArmInstructionSetFeatures::FromFeatureString(feature_list, error_msg);
+      break;
+    default:
+      result = UnknownInstructionSetFeatures::Unknown(isa);
+      break;
+  }
+  // TODO: warn if feature_list doesn't agree with result's GetFeatureList().
+  CHECK_EQ(result == nullptr, error_msg->size() != 0);
+  return result;
+}
+
+const InstructionSetFeatures* InstructionSetFeatures::FromBitmap(InstructionSet isa,
+                                                                 uint32_t bitmap) {
+  const InstructionSetFeatures* result;
+  switch (isa) {
+    case kArm:
+    case kThumb2:
+      result = ArmInstructionSetFeatures::FromBitmap(bitmap);
+      break;
+    default:
+      result = UnknownInstructionSetFeatures::Unknown(isa);
+      break;
+  }
+  CHECK_EQ(bitmap, result->AsBitmap());
+  return result;
+}
+
+const InstructionSetFeatures* InstructionSetFeatures::FromCppDefines() {
+  const InstructionSetFeatures* result;
+  switch (kRuntimeISA) {
+    case kArm:
+    case kThumb2:
+      result = ArmInstructionSetFeatures::FromCppDefines();
+      break;
+    default:
+      result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
+      break;
   }
   return result;
 }
 
+
+const InstructionSetFeatures* InstructionSetFeatures::FromCpuInfo() {
+  const InstructionSetFeatures* result;
+  switch (kRuntimeISA) {
+    case kArm:
+    case kThumb2:
+      result = ArmInstructionSetFeatures::FromCpuInfo();
+      break;
+    default:
+      result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
+      break;
+  }
+  return result;
+}
+
+const InstructionSetFeatures* InstructionSetFeatures::FromHwcap() {
+  const InstructionSetFeatures* result;
+  switch (kRuntimeISA) {
+    case kArm:
+    case kThumb2:
+      result = ArmInstructionSetFeatures::FromHwcap();
+      break;
+    default:
+      result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
+      break;
+  }
+  return result;
+}
+
+const InstructionSetFeatures* InstructionSetFeatures::FromAssembly() {
+  const InstructionSetFeatures* result;
+  switch (kRuntimeISA) {
+    case kArm:
+    case kThumb2:
+      result = ArmInstructionSetFeatures::FromAssembly();
+      break;
+    default:
+      result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
+      break;
+  }
+  return result;
+}
+
+const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const {
+  DCHECK_EQ(kArm, GetInstructionSet());
+  return down_cast<const ArmInstructionSetFeatures*>(this);
+}
+
+std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs) {
+  os << "ISA: " << rhs.GetInstructionSet() << " Feature string: " << rhs.GetFeatureString();
+  return os;
+}
+
+const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromFeatureString(
+    const std::string& feature_list, std::string* error_msg) {
+  std::vector<std::string> features;
+  Split(feature_list, ',', &features);
+  bool has_lpae = false;
+  bool has_div = false;
+  for (auto i = features.begin(); i != features.end(); i++) {
+    std::string feature = Trim(*i);
+    if (feature == "default" || feature == "none") {
+      // Nothing to do.
+    } else if (feature == "div") {
+      has_div = true;
+    } else if (feature == "nodiv") {
+      has_div = false;
+    } else if (feature == "lpae") {
+      has_lpae = true;
+    } else if (feature == "nolpae") {
+      has_lpae = false;
+    } else {
+      *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
+      return nullptr;
+    }
+  }
+  return new ArmInstructionSetFeatures(has_lpae, has_div);
+}
+
+const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromVariant(
+    const std::string& variant, std::string* error_msg) {
+  // Look for variants that have divide support.
+  bool has_div = false;
+  {
+    static const char* arm_variants_with_div[] = {
+        "cortex-a7", "cortex-a12", "cortex-a15", "cortex-a17", "cortex-a53", "cortex-a57",
+        "cortex-m3", "cortex-m4", "cortex-r4", "cortex-r5",
+        "cyclone", "denver", "krait", "swift"
+    };
+    for (const char* div_variant : arm_variants_with_div) {
+      if (variant == div_variant) {
+        has_div = true;
+        break;
+      }
+    }
+  }
+  // Look for variants that have LPAE support.
+  bool has_lpae = false;
+  {
+    static const char* arm_variants_with_lpae[] = {
+        "cortex-a7", "cortex-a15", "krait", "denver"
+    };
+    for (const char* lpae_variant : arm_variants_with_lpae) {
+      if (variant == lpae_variant) {
+        has_lpae = true;
+        break;
+      }
+    }
+  }
+  if (has_div == false && has_lpae == false) {
+    // Avoid unsupported variants.
+    static const char* unsupported_arm_variants[] = {
+        // ARM processors that aren't ARMv7 compatible aren't supported.
+        "arm2", "arm250", "arm3", "arm6", "arm60", "arm600", "arm610", "arm620",
+        "cortex-m0", "cortex-m0plus", "cortex-m1",
+        "fa526", "fa626", "fa606te", "fa626te", "fmp626", "fa726te",
+        "iwmmxt", "iwmmxt2",
+        "strongarm", "strongarm110", "strongarm1100", "strongarm1110",
+        "xscale"
+    };
+    for (const char* us_variant : unsupported_arm_variants) {
+      if (variant == us_variant) {
+        *error_msg = StringPrintf("Attempt to use unsupported ARM variant: %s", us_variant);
+        return nullptr;
+      }
+    }
+    // Warn if the variant is unknown.
+    // TODO: some of the variants below may have feature support, but that support is currently
+    //       unknown so we'll choose conservative (sub-optimal) defaults without warning.
+    // TODO: some of the architectures may not support all features required by ART and should be
+    //       moved to unsupported_arm_variants[] above.
+    static const char* arm_variants_without_known_features[] = {
+        "arm7", "arm7m", "arm7d", "arm7dm", "arm7di", "arm7dmi", "arm70", "arm700", "arm700i",
+        "arm710", "arm710c", "arm7100", "arm720", "arm7500", "arm7500fe", "arm7tdmi", "arm7tdmi-s",
+        "arm710t", "arm720t", "arm740t",
+        "arm8", "arm810",
+        "arm9", "arm9e", "arm920", "arm920t", "arm922t", "arm946e-s", "arm966e-s", "arm968e-s",
+        "arm926ej-s", "arm940t", "arm9tdmi",
+        "arm10tdmi", "arm1020t", "arm1026ej-s", "arm10e", "arm1020e", "arm1022e",
+        "arm1136j-s", "arm1136jf-s",
+        "arm1156t2-s", "arm1156t2f-s", "arm1176jz-s", "arm1176jzf-s",
+        "cortex-a5", "cortex-a8", "cortex-a9", "cortex-a9-mp", "cortex-r4f",
+        "marvell-pj4", "mpcore", "mpcorenovfp"
+    };
+    bool found = false;
+    for (const char* ff_variant : arm_variants_without_known_features) {
+      if (variant == ff_variant) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      LOG(WARNING) << "Unknown instruction set features for ARM CPU variant (" << variant
+          << ") using conservative defaults";
+    }
+  }
+  return new ArmInstructionSetFeatures(has_lpae, has_div);
+}
+
+const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromBitmap(uint32_t bitmap) {
+  bool has_lpae = (bitmap & kLpaeBitfield) != 0;
+  bool has_div = (bitmap & kDivBitfield) != 0;
+  return new ArmInstructionSetFeatures(has_lpae, has_div);
+}
+
+const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCppDefines() {
+#if defined(__ARM_ARCH_EXT_IDIV__)
+  bool has_div = true;
+#else
+  bool has_div = false;
+#endif
+#if defined(__ARM_FEATURE_LPAE)
+  bool has_lpae = true;
+#else
+  bool has_lpae = false;
+#endif
+  return new ArmInstructionSetFeatures(has_lpae, has_div);
+}
+
+const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCpuInfo() {
+  // Look in /proc/cpuinfo for features we need.  Only use this when we can guarantee that
+  // the kernel puts the appropriate feature flags in here.  Sometimes it doesn't.
+  bool has_lpae = false;
+  bool has_div = false;
+
+  std::ifstream in("/proc/cpuinfo");
+  if (!in.fail()) {
+    while (!in.eof()) {
+      std::string line;
+      std::getline(in, line);
+      if (!in.eof()) {
+        LOG(INFO) << "cpuinfo line: " << line;
+        if (line.find("Features") != std::string::npos) {
+          LOG(INFO) << "found features";
+          if (line.find("idivt") != std::string::npos) {
+            // We always expect both ARM and Thumb divide instructions to be available or not
+            // available.
+            CHECK_NE(line.find("idiva"), std::string::npos);
+            has_div = true;
+          }
+          if (line.find("lpae") != std::string::npos) {
+            has_lpae = true;
+          }
+        }
+      }
+    }
+    in.close();
+  } else {
+    LOG(INFO) << "Failed to open /proc/cpuinfo";
+  }
+  return new ArmInstructionSetFeatures(has_lpae, has_div);
+}
+
+#if defined(HAVE_ANDROID_OS) && defined(__arm__)
+#include <sys/auxv.h>
+#include <asm/hwcap.h>
+#endif
+
+const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromHwcap() {
+  bool has_lpae = false;
+  bool has_div = false;
+
+#if defined(HAVE_ANDROID_OS) && defined(__arm__)
+  uint64_t hwcaps = getauxval(AT_HWCAP);
+  LOG(INFO) << "hwcaps=" << hwcaps;
+  if ((hwcaps & HWCAP_IDIVT) != 0) {
+    // We always expect both ARM and Thumb divide instructions to be available or not
+    // available.
+    CHECK_NE(hwcaps & HWCAP_IDIVA, 0U);
+    has_div = true;
+  }
+  if ((hwcaps & HWCAP_LPAE) != 0) {
+    has_lpae = true;
+  }
+#endif
+
+  return new ArmInstructionSetFeatures(has_lpae, has_div);
+}
+
+// A signal handler called by a fault for an illegal instruction.  We record the fact in r0
+// and then increment the PC in the signal context to return to the next instruction.  We know the
+// instruction is an sdiv (4 bytes long).
+static void bad_divide_inst_handle(int signo ATTRIBUTE_UNUSED, siginfo_t* si ATTRIBUTE_UNUSED,
+                                   void* data) {
+#if defined(__arm__)
+  struct ucontext *uc = (struct ucontext *)data;
+  struct sigcontext *sc = &uc->uc_mcontext;
+  sc->arm_r0 = 0;     // Set R0 to #0 to signal error.
+  sc->arm_pc += 4;    // Skip offending instruction.
+#else
+  UNUSED(data);
+#endif
+}
+
+#if defined(__arm__)
+extern "C" bool artCheckForARMSDIVInstruction();
+#endif
+
+const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromAssembly() {
+  // See if have a sdiv instruction.  Register a signal handler and try to execute an sdiv
+  // instruction.  If we get a SIGILL then it's not supported.
+  struct sigaction sa, osa;
+  sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
+  sa.sa_sigaction = bad_divide_inst_handle;
+  sigaction(SIGILL, &sa, &osa);
+
+  bool has_div = false;
+#if defined(__arm__)
+  if (artCheckForARMSDIVInstruction()) {
+    has_div = true;
+  }
+#endif
+
+  // Restore the signal handler.
+  sigaction(SIGILL, &osa, nullptr);
+
+  // Use compile time features to "detect" LPAE support.
+  // TODO: write an assembly LPAE support test.
+#if defined(__ARM_FEATURE_LPAE)
+  bool has_lpae = true;
+#else
+  bool has_lpae = false;
+#endif
+  return new ArmInstructionSetFeatures(has_lpae, has_div);
+}
+
+
+bool ArmInstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
+  if (kArm != other->GetInstructionSet()) {
+    return false;
+  }
+  const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures();
+  return has_lpae_ == other_as_arm->has_lpae_ && has_div_ == other_as_arm->has_div_;
+}
+
+uint32_t ArmInstructionSetFeatures::AsBitmap() const {
+  return (has_lpae_ ? kLpaeBitfield : 0) | (has_div_ ? kDivBitfield : 0);
+}
+
+std::string ArmInstructionSetFeatures::GetFeatureString() const {
+  std::string result;
+  if (has_div_) {
+    result += ",div";
+  }
+  if (has_lpae_) {
+    result += ",lpae";
+  }
+  if (result.size() == 0) {
+    return "none";
+  } else {
+    // Strip leading comma.
+    return result.substr(1, result.size());
+  }
+}
+
 }  // namespace art
diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h
index ae8eeac..84a3e80 100644
--- a/runtime/instruction_set.h
+++ b/runtime/instruction_set.h
@@ -22,6 +22,7 @@
 
 #include "base/logging.h"  // Logging is required for FATAL in the helper functions.
 #include "base/macros.h"
+#include "base/value_object.h"
 #include "globals.h"       // For KB.
 
 namespace art {
@@ -56,6 +57,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 +95,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 +118,7 @@
 
     case kArm64:
     case kX86_64:
+    case kMips64:
       return true;
 
     case kNone:
@@ -173,53 +178,163 @@
 
 size_t GetStackOverflowReservedBytes(InstructionSet isa);
 
-enum InstructionFeatures {
-  kHwDiv  = 0x1,              // Supports hardware divide.
-  kHwLpae = 0x2,              // Supports Large Physical Address Extension.
-};
+class ArmInstructionSetFeatures;
 
-// This is a bitmask of supported features per architecture.
-class PACKED(4) InstructionSetFeatures {
+// Abstraction used to describe features of a different instruction sets.
+class InstructionSetFeatures {
  public:
-  InstructionSetFeatures() : mask_(0) {}
-  explicit InstructionSetFeatures(uint32_t mask) : mask_(mask) {}
+  // Process a CPU variant string for the given ISA and create an InstructionSetFeatures.
+  static const InstructionSetFeatures* FromVariant(InstructionSet isa,
+                                                   const std::string& variant,
+                                                   std::string* error_msg);
 
-  static InstructionSetFeatures GuessInstructionSetFeatures();
+  // Parse a string of the form "div,lpae" and create an InstructionSetFeatures.
+  static const InstructionSetFeatures* FromFeatureString(InstructionSet isa,
+                                                         const std::string& feature_list,
+                                                         std::string* error_msg);
 
-  bool HasDivideInstruction() const {
-      return (mask_ & kHwDiv) != 0;
-  }
+  // Parse a bitmap for the given isa and create an InstructionSetFeatures.
+  static const InstructionSetFeatures* FromBitmap(InstructionSet isa, uint32_t bitmap);
 
-  void SetHasDivideInstruction(bool v) {
-    mask_ = (mask_ & ~kHwDiv) | (v ? kHwDiv : 0);
-  }
+  // Turn C pre-processor #defines into the equivalent instruction set features for kRuntimeISA.
+  static const InstructionSetFeatures* FromCppDefines();
 
-  bool HasLpae() const {
-    return (mask_ & kHwLpae) != 0;
-  }
+  // Process /proc/cpuinfo and use kRuntimeISA to produce InstructionSetFeatures.
+  static const InstructionSetFeatures* FromCpuInfo();
 
-  void SetHasLpae(bool v) {
-    mask_ = (mask_ & ~kHwLpae) | (v ? kHwLpae : 0);
-  }
+  // Process the auxiliary vector AT_HWCAP entry and use kRuntimeISA to produce
+  // InstructionSetFeatures.
+  static const InstructionSetFeatures* FromHwcap();
 
-  std::string GetFeatureString() const;
+  // Use assembly tests of the current runtime (ie kRuntimeISA) to determine the
+  // InstructionSetFeatures. This works around kernel bugs in AT_HWCAP and /proc/cpuinfo.
+  static const InstructionSetFeatures* FromAssembly();
 
-  // Other features in here.
+  // Are these features the same as the other given features?
+  virtual bool Equals(const InstructionSetFeatures* other) const = 0;
 
-  bool operator==(const InstructionSetFeatures &peer) const {
-    return mask_ == peer.mask_;
-  }
+  // Return the ISA these features relate to.
+  virtual InstructionSet GetInstructionSet() const = 0;
 
-  bool operator!=(const InstructionSetFeatures &peer) const {
-    return mask_ != peer.mask_;
-  }
+  // Return a bitmap that represents the features. ISA specific.
+  virtual uint32_t AsBitmap() const = 0;
 
-  bool operator<=(const InstructionSetFeatures &peer) const {
-    return (mask_ & peer.mask_) == mask_;
-  }
+  // Return a string of the form "div,lpae" or "none".
+  virtual std::string GetFeatureString() const = 0;
+
+  // Down cast this ArmInstructionFeatures.
+  const ArmInstructionSetFeatures* AsArmInstructionSetFeatures() const;
+
+  virtual ~InstructionSetFeatures() {}
+
+ protected:
+  InstructionSetFeatures() {}
 
  private:
-  uint32_t mask_;
+  DISALLOW_COPY_AND_ASSIGN(InstructionSetFeatures);
+};
+std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs);
+
+// Instruction set features relevant to the ARM architecture.
+class ArmInstructionSetFeatures FINAL : public InstructionSetFeatures {
+ public:
+  // Process a CPU variant string like "krait" or "cortex-a15" and create InstructionSetFeatures.
+  static const ArmInstructionSetFeatures* FromVariant(const std::string& variant,
+                                                      std::string* error_msg);
+
+  // Parse a string of the form "div,lpae" and create an InstructionSetFeatures.
+  static const ArmInstructionSetFeatures* FromFeatureString(const std::string& feature_list,
+                                                            std::string* error_msg);
+
+  // Parse a bitmap and create an InstructionSetFeatures.
+  static const ArmInstructionSetFeatures* FromBitmap(uint32_t bitmap);
+
+  // Turn C pre-processor #defines into the equivalent instruction set features.
+  static const ArmInstructionSetFeatures* FromCppDefines();
+
+  // Process /proc/cpuinfo and use kRuntimeISA to produce InstructionSetFeatures.
+  static const ArmInstructionSetFeatures* FromCpuInfo();
+
+  // Process the auxiliary vector AT_HWCAP entry and use kRuntimeISA to produce
+  // InstructionSetFeatures.
+  static const ArmInstructionSetFeatures* FromHwcap();
+
+  // Use assembly tests of the current runtime (ie kRuntimeISA) to determine the
+  // InstructionSetFeatures. This works around kernel bugs in AT_HWCAP and /proc/cpuinfo.
+  static const ArmInstructionSetFeatures* FromAssembly();
+
+  bool Equals(const InstructionSetFeatures* other) const OVERRIDE;
+
+  InstructionSet GetInstructionSet() const OVERRIDE {
+    return kArm;
+  }
+
+  uint32_t AsBitmap() const OVERRIDE;
+
+  // Return a string of the form "div,lpae" or "none".
+  std::string GetFeatureString() const OVERRIDE;
+
+  // Is the divide instruction feature enabled?
+  bool HasDivideInstruction() const {
+      return has_div_;
+  }
+
+  // Is the Large Physical Address Extension (LPAE) instruction feature enabled? When true code can
+  // be used that assumes double register loads and stores (ldrd, strd) don't tear.
+  bool HasLpae() const {
+    return has_lpae_;
+  }
+
+  virtual ~ArmInstructionSetFeatures() {}
+
+ private:
+  ArmInstructionSetFeatures(bool has_lpae, bool has_div)
+      : has_lpae_(has_lpae), has_div_(has_div) {
+  }
+
+  // Bitmap positions for encoding features as a bitmap.
+  enum {
+    kDivBitfield = 1,
+    kLpaeBitfield = 2,
+  };
+
+  const bool has_lpae_;
+  const bool has_div_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArmInstructionSetFeatures);
+};
+
+// A class used for instruction set features on ISAs that don't yet have any features defined.
+class UnknownInstructionSetFeatures FINAL : public InstructionSetFeatures {
+ public:
+  static const UnknownInstructionSetFeatures* Unknown(InstructionSet isa) {
+    return new UnknownInstructionSetFeatures(isa);
+  }
+
+  bool Equals(const InstructionSetFeatures* other) const OVERRIDE {
+    return isa_ == other->GetInstructionSet();
+  }
+
+  InstructionSet GetInstructionSet() const OVERRIDE {
+    return isa_;
+  }
+
+  uint32_t AsBitmap() const OVERRIDE {
+    return 0;
+  }
+
+  std::string GetFeatureString() const OVERRIDE {
+    return "none";
+  }
+
+  virtual ~UnknownInstructionSetFeatures() {}
+
+ private:
+  explicit UnknownInstructionSetFeatures(InstructionSet isa) : isa_(isa) {}
+
+  const InstructionSet isa_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnknownInstructionSetFeatures);
 };
 
 // The following definitions create return types for two word-sized entities that will be passed
@@ -255,7 +370,8 @@
 
 // Use the lower 32b for the method pointer and the upper 32b for the code pointer.
 static inline TwoWordReturn GetTwoWordSuccessValue(uintptr_t hi, uintptr_t lo) {
-  uint32_t lo32 = static_cast<uint32_t>(lo);
+  static_assert(sizeof(uint32_t) == sizeof(uintptr_t), "Unexpected size difference");
+  uint32_t lo32 = lo;
   uint64_t hi64 = static_cast<uint64_t>(hi);
   return ((hi64 << 32) | lo32);
 }
diff --git a/runtime/instruction_set_test.cc b/runtime/instruction_set_test.cc
index ac17c4f..3f2d16b 100644
--- a/runtime/instruction_set_test.cc
+++ b/runtime/instruction_set_test.cc
@@ -16,6 +16,7 @@
 
 #include "instruction_set.h"
 
+#include "base/stringprintf.h"
 #include "common_runtime_test.h"
 
 namespace art {
@@ -47,7 +48,232 @@
 }
 
 TEST_F(InstructionSetTest, PointerSize) {
-  EXPECT_EQ(kPointerSize, GetInstructionSetPointerSize(kRuntimeISA));
+  EXPECT_EQ(sizeof(void*), GetInstructionSetPointerSize(kRuntimeISA));
+}
+
+TEST_F(InstructionSetTest, X86Features) {
+  // Build features for a 32-bit x86 atom processor.
+  std::string error_msg;
+  std::unique_ptr<const InstructionSetFeatures> x86_features(
+      InstructionSetFeatures::FromVariant(kX86, "atom", &error_msg));
+  ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
+  EXPECT_EQ(x86_features->GetInstructionSet(), kX86);
+  EXPECT_TRUE(x86_features->Equals(x86_features.get()));
+  EXPECT_STREQ("none", x86_features->GetFeatureString().c_str());
+  EXPECT_EQ(x86_features->AsBitmap(), 0U);
+
+  // Build features for a 32-bit x86 default processor.
+  std::unique_ptr<const InstructionSetFeatures> x86_default_features(
+      InstructionSetFeatures::FromFeatureString(kX86, "default", &error_msg));
+  ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg;
+  EXPECT_EQ(x86_default_features->GetInstructionSet(), kX86);
+  EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get()));
+  EXPECT_STREQ("none", x86_default_features->GetFeatureString().c_str());
+  EXPECT_EQ(x86_default_features->AsBitmap(), 0U);
+
+  // Build features for a 64-bit x86-64 atom processor.
+  std::unique_ptr<const InstructionSetFeatures> x86_64_features(
+      InstructionSetFeatures::FromVariant(kX86_64, "atom", &error_msg));
+  ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
+  EXPECT_EQ(x86_64_features->GetInstructionSet(), kX86_64);
+  EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
+  EXPECT_STREQ("none", x86_64_features->GetFeatureString().c_str());
+  EXPECT_EQ(x86_64_features->AsBitmap(), 0U);
+
+  EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
+  EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get()));
+  EXPECT_TRUE(x86_features->Equals(x86_default_features.get()));
+}
+
+TEST_F(InstructionSetTest, ArmFeaturesFromVariant) {
+  // Build features for a 32-bit ARM krait processor.
+  std::string error_msg;
+  std::unique_ptr<const InstructionSetFeatures> krait_features(
+      InstructionSetFeatures::FromVariant(kArm, "krait", &error_msg));
+  ASSERT_TRUE(krait_features.get() != nullptr) << error_msg;
+
+  ASSERT_EQ(krait_features->GetInstructionSet(), kArm);
+  EXPECT_TRUE(krait_features->Equals(krait_features.get()));
+  EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+  EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasLpae());
+  EXPECT_STREQ("div,lpae", krait_features->GetFeatureString().c_str());
+  EXPECT_EQ(krait_features->AsBitmap(), 3U);
+
+  // Build features for a 32-bit ARM denver processor.
+  std::unique_ptr<const InstructionSetFeatures> denver_features(
+      InstructionSetFeatures::FromVariant(kArm, "denver", &error_msg));
+  ASSERT_TRUE(denver_features.get() != nullptr) << error_msg;
+
+  EXPECT_TRUE(denver_features->Equals(denver_features.get()));
+  EXPECT_TRUE(denver_features->Equals(krait_features.get()));
+  EXPECT_TRUE(krait_features->Equals(denver_features.get()));
+  EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+  EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasLpae());
+  EXPECT_STREQ("div,lpae", denver_features->GetFeatureString().c_str());
+  EXPECT_EQ(denver_features->AsBitmap(), 3U);
+
+  // Build features for a 32-bit ARMv7 processor.
+  std::unique_ptr<const InstructionSetFeatures> arm7_features(
+      InstructionSetFeatures::FromVariant(kArm, "arm7", &error_msg));
+  ASSERT_TRUE(arm7_features.get() != nullptr) << error_msg;
+
+  EXPECT_TRUE(arm7_features->Equals(arm7_features.get()));
+  EXPECT_FALSE(arm7_features->Equals(krait_features.get()));
+  EXPECT_FALSE(krait_features->Equals(arm7_features.get()));
+  EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+  EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasLpae());
+  EXPECT_STREQ("none", arm7_features->GetFeatureString().c_str());
+  EXPECT_EQ(arm7_features->AsBitmap(), 0U);
+
+  // ARM6 is not a supported architecture variant.
+  std::unique_ptr<const InstructionSetFeatures> arm6_features(
+      InstructionSetFeatures::FromVariant(kArm, "arm6", &error_msg));
+  EXPECT_TRUE(arm6_features.get() == nullptr);
+  EXPECT_NE(error_msg.size(), 0U);
+}
+
+TEST_F(InstructionSetTest, ArmFeaturesFromString) {
+  // Build features for a 32-bit ARM with LPAE and div processor.
+  std::string error_msg;
+  std::unique_ptr<const InstructionSetFeatures> krait_features(
+      InstructionSetFeatures::FromFeatureString(kArm, "lpae,div", &error_msg));
+  ASSERT_TRUE(krait_features.get() != nullptr) << error_msg;
+
+  ASSERT_EQ(krait_features->GetInstructionSet(), kArm);
+  EXPECT_TRUE(krait_features->Equals(krait_features.get()));
+  EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+  EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasLpae());
+  EXPECT_STREQ("div,lpae", krait_features->GetFeatureString().c_str());
+  EXPECT_EQ(krait_features->AsBitmap(), 3U);
+
+  // Build features for a 32-bit ARM processor with LPAE and div flipped.
+  std::unique_ptr<const InstructionSetFeatures> denver_features(
+      InstructionSetFeatures::FromFeatureString(kArm, "div,lpae", &error_msg));
+  ASSERT_TRUE(denver_features.get() != nullptr) << error_msg;
+
+  EXPECT_TRUE(denver_features->Equals(denver_features.get()));
+  EXPECT_TRUE(denver_features->Equals(krait_features.get()));
+  EXPECT_TRUE(krait_features->Equals(denver_features.get()));
+  EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+  EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasLpae());
+  EXPECT_STREQ("div,lpae", denver_features->GetFeatureString().c_str());
+  EXPECT_EQ(denver_features->AsBitmap(), 3U);
+
+  // Build features for a 32-bit default ARM processor.
+  std::unique_ptr<const InstructionSetFeatures> arm7_features(
+      InstructionSetFeatures::FromFeatureString(kArm, "default", &error_msg));
+  ASSERT_TRUE(arm7_features.get() != nullptr) << error_msg;
+
+  EXPECT_TRUE(arm7_features->Equals(arm7_features.get()));
+  EXPECT_FALSE(arm7_features->Equals(krait_features.get()));
+  EXPECT_FALSE(krait_features->Equals(arm7_features.get()));
+  EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+  EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasLpae());
+  EXPECT_STREQ("none", arm7_features->GetFeatureString().c_str());
+  EXPECT_EQ(arm7_features->AsBitmap(), 0U);
+}
+
+#ifdef HAVE_ANDROID_OS
+#include "cutils/properties.h"
+
+TEST_F(InstructionSetTest, FeaturesFromSystemPropertyVariant) {
+  // Take the default set of instruction features from the build.
+  std::unique_ptr<const InstructionSetFeatures> instruction_set_features(
+      InstructionSetFeatures::FromCppDefines());
+
+  // Read the features property.
+  std::string key = StringPrintf("dalvik.vm.isa.%s.variant", GetInstructionSetString(kRuntimeISA));
+  char dex2oat_isa_variant[PROPERTY_VALUE_MAX];
+  if (property_get(key.c_str(), dex2oat_isa_variant, nullptr) > 0) {
+    // Use features from property to build InstructionSetFeatures and check against build's
+    // features.
+    std::string error_msg;
+    std::unique_ptr<const InstructionSetFeatures> property_features(
+        InstructionSetFeatures::FromVariant(kRuntimeISA, dex2oat_isa_variant, &error_msg));
+    ASSERT_TRUE(property_features.get() != nullptr) << error_msg;
+
+    EXPECT_TRUE(property_features->Equals(instruction_set_features.get()))
+      << "System property features: " << *property_features.get()
+      << "\nFeatures from build: " << *instruction_set_features.get();
+  }
+}
+
+TEST_F(InstructionSetTest, FeaturesFromSystemPropertyString) {
+  // Take the default set of instruction features from the build.
+  std::unique_ptr<const InstructionSetFeatures> instruction_set_features(
+      InstructionSetFeatures::FromCppDefines());
+
+  // Read the features property.
+  std::string key = StringPrintf("dalvik.vm.isa.%s.features", GetInstructionSetString(kRuntimeISA));
+  char dex2oat_isa_features[PROPERTY_VALUE_MAX];
+  if (property_get(key.c_str(), dex2oat_isa_features, nullptr) > 0) {
+    // Use features from property to build InstructionSetFeatures and check against build's
+    // features.
+    std::string error_msg;
+    std::unique_ptr<const InstructionSetFeatures> property_features(
+        InstructionSetFeatures::FromFeatureString(kRuntimeISA, dex2oat_isa_features, &error_msg));
+    ASSERT_TRUE(property_features.get() != nullptr) << error_msg;
+
+    EXPECT_TRUE(property_features->Equals(instruction_set_features.get()))
+      << "System property features: " << *property_features.get()
+      << "\nFeatures from build: " << *instruction_set_features.get();
+  }
+}
+#endif
+
+#if defined(__arm__)
+TEST_F(InstructionSetTest, DISABLED_FeaturesFromCpuInfo) {
+  LOG(WARNING) << "Test disabled due to buggy ARM kernels";
+#else
+TEST_F(InstructionSetTest, FeaturesFromCpuInfo) {
+#endif
+  // Take the default set of instruction features from the build.
+  std::unique_ptr<const InstructionSetFeatures> instruction_set_features(
+      InstructionSetFeatures::FromCppDefines());
+
+  // Check we get the same instruction set features using /proc/cpuinfo.
+  std::unique_ptr<const InstructionSetFeatures> cpuinfo_features(
+      InstructionSetFeatures::FromCpuInfo());
+  EXPECT_TRUE(cpuinfo_features->Equals(instruction_set_features.get()))
+      << "CPU Info features: " << *cpuinfo_features.get()
+      << "\nFeatures from build: " << *instruction_set_features.get();
+}
+
+#if defined(__arm__)
+TEST_F(InstructionSetTest, DISABLED_FeaturesFromHwcap) {
+  LOG(WARNING) << "Test disabled due to buggy ARM kernels";
+#else
+TEST_F(InstructionSetTest, FeaturesFromHwcap) {
+#endif
+  // Take the default set of instruction features from the build.
+  std::unique_ptr<const InstructionSetFeatures> instruction_set_features(
+      InstructionSetFeatures::FromCppDefines());
+
+  // Check we get the same instruction set features using AT_HWCAP.
+  std::unique_ptr<const InstructionSetFeatures> hwcap_features(
+      InstructionSetFeatures::FromHwcap());
+  EXPECT_TRUE(hwcap_features->Equals(instruction_set_features.get()))
+      << "Hwcap features: " << *hwcap_features.get()
+      << "\nFeatures from build: " << *instruction_set_features.get();
+}
+
+
+#if defined(__arm__)
+TEST_F(InstructionSetTest, DISABLED_FeaturesFromAssembly) {
+  LOG(WARNING) << "Test disabled due to buggy ARM kernels";
+#else
+TEST_F(InstructionSetTest, FeaturesFromAssembly) {
+#endif
+  // Take the default set of instruction features from the build.
+  std::unique_ptr<const InstructionSetFeatures> instruction_set_features(
+      InstructionSetFeatures::FromCppDefines());
+
+  // Check we get the same instruction set features using assembly tests.
+  std::unique_ptr<const InstructionSetFeatures> assembly_features(
+      InstructionSetFeatures::FromAssembly());
+  EXPECT_TRUE(assembly_features->Equals(instruction_set_features.get()))
+      << "Assembly features: " << *assembly_features.get()
+      << "\nFeatures from build: " << *instruction_set_features.get();
 }
 
 }  // namespace art
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 652d29b..003e160 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -18,13 +18,17 @@
 
 #include <sys/uio.h>
 
+#include <sstream>
+
 #include "arch/context.h"
 #include "atomic.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
+#include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc_root-inl.h"
 #include "interpreter/interpreter.h"
 #include "mirror/art_method-inl.h"
@@ -33,9 +37,6 @@
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
 #include "nth_caller_visitor.h"
-#if !defined(ART_USE_PORTABLE_COMPILER)
-#include "entrypoints/quick/quick_entrypoints.h"
-#endif
 #include "os.h"
 #include "scoped_thread_state_change.h"
 #include "thread.h"
@@ -85,9 +86,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) {
@@ -97,25 +96,20 @@
   }
   if (!method->IsResolutionMethod()) {
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    if (quick_code == GetQuickToInterpreterBridge() ||
-        quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() ||
-        (quick_code == class_linker->GetQuickResolutionTrampoline() &&
-         Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly()
-         && !method->IsNative() && !method->IsProxyMethod())) {
+    if (class_linker->IsQuickToInterpreterBridge(quick_code) ||
+        (class_linker->IsQuickResolutionStub(quick_code) &&
+         Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly() &&
+         !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
+        } else if (class_linker->IsQuickResolutionStub(quick_code)) {
+          DCHECK(class_linker->IsPortableResolutionStub(portable_code));
         }
       }
       DCHECK(!method->IsNative()) << PrettyMethod(method);
       DCHECK(!method->IsProxyMethod()) << PrettyMethod(method);
-      method->SetEntryPointFromInterpreter(art::interpreter::artInterpreterToInterpreterBridge);
+      method->SetEntryPointFromInterpreter(art::artInterpreterToInterpreterBridge);
     } else {
       method->SetEntryPointFromInterpreter(art::artInterpreterToCompiledCodeBridge);
     }
@@ -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();
+      new_portable_code = GetPortableResolutionStub();
+      new_quick_code = GetQuickResolutionStub();
     }
   } 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,22 +154,16 @@
       // 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());
+          DCHECK(!class_linker->IsQuickToInterpreterBridge(new_quick_code));
         }
       } else {
-#if defined(ART_USE_PORTABLE_COMPILER)
-        new_portable_code = class_linker->GetPortableResolutionTrampoline();
-#endif
-        new_quick_code = class_linker->GetQuickResolutionTrampoline();
+        new_portable_code = GetPortableResolutionStub();
+        new_quick_code = GetQuickResolutionStub();
       }
     }
   }
@@ -200,8 +177,9 @@
 static void InstrumentationInstallStack(Thread* thread, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   struct InstallStackVisitor : public StackVisitor {
-    InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc)
-        : StackVisitor(thread, context),  instrumentation_stack_(thread->GetInstrumentationStack()),
+    InstallStackVisitor(Thread* thread_in, Context* context, uintptr_t instrumentation_exit_pc)
+        : StackVisitor(thread_in, context),
+          instrumentation_stack_(thread_in->GetInstrumentationStack()),
           instrumentation_exit_pc_(instrumentation_exit_pc),
           reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0),
           last_return_pc_(0) {
@@ -310,7 +288,7 @@
 
   Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
   std::unique_ptr<Context> context(Context::Create());
-  uintptr_t instrumentation_exit_pc = GetQuickInstrumentationExitPc();
+  uintptr_t instrumentation_exit_pc = reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc());
   InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc);
   visitor.WalkStack(true);
   CHECK_EQ(visitor.dex_pcs_.size(), thread->GetInstrumentationStack()->size());
@@ -339,12 +317,12 @@
 static void InstrumentationRestoreStack(Thread* thread, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   struct RestoreStackVisitor : public StackVisitor {
-    RestoreStackVisitor(Thread* thread, uintptr_t instrumentation_exit_pc,
+    RestoreStackVisitor(Thread* thread_in, uintptr_t instrumentation_exit_pc,
                         Instrumentation* instrumentation)
-        : StackVisitor(thread, NULL), thread_(thread),
+        : StackVisitor(thread_in, NULL), thread_(thread_in),
           instrumentation_exit_pc_(instrumentation_exit_pc),
           instrumentation_(instrumentation),
-          instrumentation_stack_(thread->GetInstrumentationStack()),
+          instrumentation_stack_(thread_in->GetInstrumentationStack()),
           frames_removed_(0) {}
 
     virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -411,7 +389,8 @@
   std::deque<instrumentation::InstrumentationStackFrame>* stack = thread->GetInstrumentationStack();
   if (stack->size() > 0) {
     Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
-    uintptr_t instrumentation_exit_pc = GetQuickInstrumentationExitPc();
+    uintptr_t instrumentation_exit_pc =
+        reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc());
     RestoreStackVisitor visitor(thread, instrumentation_exit_pc, instrumentation);
     visitor.WalkStack(true);
     CHECK_EQ(visitor.frames_removed_, stack->size());
@@ -617,6 +596,7 @@
 }
 
 static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg) {
+  UNUSED(arg);
   thread->ResetQuickAllocEntryPointsForThread();
 }
 
@@ -655,7 +635,6 @@
     SetEntrypointsInstrumented(true);
   }
   ++quick_alloc_entry_points_instrumentation_counter_;
-  LOG(INFO) << "Counter: " << quick_alloc_entry_points_instrumentation_counter_;
 }
 
 void Instrumentation::UninstrumentQuickAllocEntryPointsLocked() {
@@ -665,7 +644,6 @@
   if (quick_alloc_entry_points_instrumentation_counter_ == 0) {
     SetEntrypointsInstrumented(false);
   }
-  LOG(INFO) << "Counter: " << quick_alloc_entry_points_instrumentation_counter_;
 }
 
 void Instrumentation::ResetQuickAllocEntryPoints() {
@@ -687,32 +665,21 @@
     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 {
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      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
+      if (class_linker->IsQuickResolutionStub(quick_code) ||
+          class_linker->IsQuickToInterpreterBridge(quick_code)) {
+        DCHECK(class_linker->IsPortableResolutionStub(portable_code) ||
+               class_linker->IsPortableToInterpreterBridge(portable_code));
         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
@@ -831,22 +793,11 @@
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     if (method->IsStatic() && !method->IsConstructor() &&
         !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);
+      UpdateEntrypoints(method, GetQuickResolutionStub(), GetPortableResolutionStub(), 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);
     }
 
@@ -924,9 +875,10 @@
     const void* code = method->GetEntryPointFromQuickCompiledCode();
     DCHECK(code != nullptr);
     ClassLinker* class_linker = runtime->GetClassLinker();
-    if (LIKELY(code != class_linker->GetQuickResolutionTrampoline()) &&
-        LIKELY(code != class_linker->GetQuickToInterpreterBridgeTrampoline()) &&
-        LIKELY(code != GetQuickToInterpreterBridge())) {
+    if (LIKELY(!class_linker->IsQuickResolutionStub(code) &&
+               !class_linker->IsQuickToInterpreterBridge(code)) &&
+               !class_linker->IsQuickResolutionStub(code) &&
+               !class_linker->IsQuickToInterpreterBridge(code)) {
       return code;
     }
   }
@@ -1064,6 +1016,7 @@
   // Set return PC and check the sanity of the stack.
   *return_pc = instrumentation_frame.return_pc_;
   CheckStackDepth(self, instrumentation_frame, 0);
+  self->VerifyStack();
 
   mirror::ArtMethod* method = instrumentation_frame.method_;
   uint32_t length;
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 3017bf6..646c7ae 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -103,13 +103,13 @@
 class Instrumentation {
  public:
   enum InstrumentationEvent {
-    kMethodEntered =   1 << 0,
-    kMethodExited =    1 << 1,
-    kMethodUnwind =    1 << 2,
-    kDexPcMoved =      1 << 3,
-    kFieldRead =       1 << 4,
-    kFieldWritten =    1 << 5,
-    kExceptionCaught = 1 << 6,
+    kMethodEntered   = 1,  // 1 << 0
+    kMethodExited    = 2,  // 1 << 1
+    kMethodUnwind    = 4,  // 1 << 2
+    kDexPcMoved      = 8,  // 1 << 3
+    kFieldRead       = 16,  // 1 << 4,
+    kFieldWritten    = 32,  // 1 << 5
+    kExceptionCaught = 64,  // 1 << 6
   };
 
   Instrumentation();
@@ -464,6 +464,7 @@
 
   DISALLOW_COPY_AND_ASSIGN(Instrumentation);
 };
+std::ostream& operator<<(std::ostream& os, const Instrumentation::InstrumentationEvent& rhs);
 
 // An element in the instrumentation side stack maintained in art::Thread.
 struct InstrumentationStackFrame {
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 95c622a..56a6d2c 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -191,9 +191,9 @@
     const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
     if (string_id != nullptr) {
       uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
-      mirror::String* image = dex_cache->GetResolvedString(string_idx);
-      if (image != nullptr) {
-        return image;
+      mirror::String* image_string = dex_cache->GetResolvedString(string_idx);
+      if (image_string != NULL) {
+        return image_string;
       }
     }
   }
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 47a7f0d..18de133 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()") {
@@ -315,6 +315,10 @@
   kSwitchImpl,            // Switch-based interpreter implementation.
   kComputedGotoImplKind   // Computed-goto-based interpreter implementation.
 };
+static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs) {
+  os << ((rhs == kSwitchImpl) ? "Switch-based interpreter" : "Computed-goto-based interpreter");
+  return os;
+}
 
 #if !defined(__clang__)
 static constexpr InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKind;
@@ -322,10 +326,9 @@
 // Clang 3.4 fails to build the goto interpreter implementation.
 static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImpl;
 template<bool do_access_check, bool transaction_active>
-JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
-                       ShadowFrame& shadow_frame, JValue result_register) {
+JValue ExecuteGotoImpl(Thread*, MethodHelper&, const DexFile::CodeItem*, ShadowFrame&, JValue) {
   LOG(FATAL) << "UNREACHABLE";
-  exit(0);
+  UNREACHABLE();
 }
 // Explicit definitions of ExecuteGotoImpl.
 template<> SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -462,7 +465,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;
@@ -507,8 +510,9 @@
   ret_val->SetJ(value.GetJ());
 }
 
-JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
-                                ShadowFrame& shadow_frame) {
+JValue EnterInterpreterFromEntryPoint(Thread* self, MethodHelper* mh,
+                                      const DexFile::CodeItem* code_item,
+                                      ShadowFrame* shadow_frame) {
   DCHECK_EQ(self, Thread::Current());
   bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
   if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
@@ -516,10 +520,10 @@
     return JValue();
   }
 
-  return Execute(self, mh, code_item, shadow_frame, JValue());
+  return Execute(self, *mh, code_item, *shadow_frame, JValue());
 }
 
-extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh,
+extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper* mh,
                                                   const DexFile::CodeItem* code_item,
                                                   ShadowFrame* shadow_frame, JValue* result) {
   bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
@@ -529,15 +533,15 @@
   }
 
   self->PushShadowFrame(shadow_frame);
-  ArtMethod* method = shadow_frame->GetMethod();
+  DCHECK_EQ(shadow_frame->GetMethod(), mh->Get());
   // Ensure static methods are initialized.
-  if (method->IsStatic()) {
-    mirror::Class* declaring_class = method->GetDeclaringClass();
+  if (mh->Get()->IsStatic()) {
+    mirror::Class* declaring_class = mh->Get()->GetDeclaringClass();
     if (UNLIKELY(!declaring_class->IsInitialized())) {
       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;
@@ -546,15 +550,15 @@
     }
   }
 
-  if (LIKELY(!method->IsNative())) {
-    result->SetJ(Execute(self, mh, code_item, *shadow_frame, JValue()).GetJ());
+  if (LIKELY(!mh->Get()->IsNative())) {
+    result->SetJ(Execute(self, *mh, code_item, *shadow_frame, JValue()).GetJ());
   } else {
     // We don't expect to be asked to interpret native code (which is entered via a JNI compiler
     // generated stub) except during testing and image writing.
     CHECK(!Runtime::Current()->IsStarted());
-    Object* receiver = method->IsStatic() ? nullptr : shadow_frame->GetVRegReference(0);
-    uint32_t* args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
-    UnstartedRuntimeJni(self, method, receiver, args, result);
+    Object* receiver = mh->Get()->IsStatic() ? nullptr : shadow_frame->GetVRegReference(0);
+    uint32_t* args = shadow_frame->GetVRegArgs(mh->Get()->IsStatic() ? 0 : 1);
+    UnstartedRuntimeJni(self, mh->Get(), receiver, args, result);
   }
 
   self->PopShadowFrame();
diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h
index 0750eb5..d327a71 100644
--- a/runtime/interpreter/interpreter.h
+++ b/runtime/interpreter/interpreter.h
@@ -42,19 +42,20 @@
                                            JValue* ret_val)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-extern JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh,
-                                       const DexFile::CodeItem* code_item,
-                                       ShadowFrame& shadow_frame)
+extern JValue EnterInterpreterFromEntryPoint(Thread* self, MethodHelper* mh,
+                                             const DexFile::CodeItem* code_item,
+                                             ShadowFrame* shadow_frame)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh,
+
+}  // namespace interpreter
+
+extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper* mh,
                                                   const DexFile::CodeItem* code_item,
                                                   ShadowFrame* shadow_frame, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-}  // namespace interpreter
-
-extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh,
+extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper* mh,
                                                    const DexFile::CodeItem* code_item,
                                                    ShadowFrame* shadow_frame, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 6705695..eb80c30 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;
@@ -80,6 +80,7 @@
       break;
     default:
       LOG(FATAL) << "Unreachable: " << field_type;
+      UNREACHABLE();
   }
   return true;
 }
@@ -96,22 +97,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
@@ -153,6 +154,7 @@
       break;
     default:
       LOG(FATAL) << "Unreachable: " << field_type;
+      UNREACHABLE();
   }
   return true;
 }
@@ -195,7 +197,7 @@
       break;
     default:
       LOG(FATAL) << "Unreachable: " << field_type;
-      break;
+      UNREACHABLE();
   }
   return field_value;
 }
@@ -208,7 +210,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;
@@ -285,6 +287,7 @@
     }
     default:
       LOG(FATAL) << "Unreachable: " << field_type;
+      UNREACHABLE();
   }
   return true;
 }
@@ -301,22 +304,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 +349,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;
@@ -357,6 +372,7 @@
       break;
     default:
       LOG(FATAL) << "Unreachable: " << field_type;
+      UNREACHABLE();
   }
   return true;
 }
@@ -371,9 +387,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 +459,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_;
 
@@ -490,7 +510,7 @@
   exit(0);  // Unreachable, keep GCC happy.
 }
 
-static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
+static void UnstartedRuntimeInvoke(Thread* self, MethodHelper* mh,
                                    const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
                                    JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -553,9 +573,9 @@
 
     // We need to do runtime check on reference assignment. We need to load the shorty
     // to get the exact type of each reference argument.
-    const DexFile::TypeList* params = method->GetParameterTypeList();
+    const DexFile::TypeList* params = mh.Get()->GetParameterTypeList();
     uint32_t shorty_len = 0;
-    const char* shorty = method->GetShorty(&shorty_len);
+    const char* shorty = mh.Get()->GetShorty(&shorty_len);
 
     // TODO: find a cleaner way to separate non-range and range information without duplicating code.
     uint32_t arg[5];  // only used in invoke-XXX.
@@ -569,7 +589,7 @@
     // Handle receiver apart since it's not part of the shorty.
     size_t dest_reg = first_dest_reg;
     size_t arg_offset = 0;
-    if (!method->IsStatic()) {
+    if (!mh.Get()->IsStatic()) {
       size_t receiver_reg = is_range ? vregC : arg[0];
       new_shadow_frame->SetVRegReference(dest_reg, shadow_frame.GetVRegReference(receiver_reg));
       ++dest_reg;
@@ -593,7 +613,7 @@
               self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
                                        "Ljava/lang/VirtualMachineError;",
                                        "Invoking %s with bad arg %d, type '%s' not instance of '%s'",
-                                       method->GetName(), shorty_pos,
+                                       mh.Get()->GetName(), shorty_pos,
                                        o->GetClass()->GetDescriptor(&temp1),
                                        arg_type->GetDescriptor(&temp2));
               return false;
@@ -642,17 +662,17 @@
 
   // Do the call now.
   if (LIKELY(Runtime::Current()->IsStarted())) {
-    if (kIsDebugBuild && method->GetEntryPointFromInterpreter() == nullptr) {
-      LOG(FATAL) << "Attempt to invoke non-executable method: " << PrettyMethod(method);
+    if (kIsDebugBuild && mh.Get()->GetEntryPointFromInterpreter() == nullptr) {
+      LOG(FATAL) << "Attempt to invoke non-executable method: " << PrettyMethod(mh.Get());
     }
     if (kIsDebugBuild && Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly() &&
-        !method->IsNative() && !method->IsProxyMethod() &&
-        method->GetEntryPointFromInterpreter() == artInterpreterToCompiledCodeBridge) {
-      LOG(FATAL) << "Attempt to call compiled code when -Xint: " << PrettyMethod(method);
+        !mh.Get()->IsNative() && !mh.Get()->IsProxyMethod() &&
+        mh.Get()->GetEntryPointFromInterpreter() == artInterpreterToCompiledCodeBridge) {
+      LOG(FATAL) << "Attempt to call compiled code when -Xint: " << PrettyMethod(mh.Get());
     }
-    (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
+    (mh.Get()->GetEntryPointFromInterpreter())(self, &mh, code_item, new_shadow_frame, result);
   } else {
-    UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, first_dest_reg);
+    UnstartedRuntimeInvoke(self, &mh, code_item, new_shadow_frame, result, first_dest_reg);
   }
   return !self->IsExceptionPending();
 }
@@ -692,7 +712,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 +805,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;
     }
@@ -792,7 +813,7 @@
   result->SetL(found);
 }
 
-static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
+static void UnstartedRuntimeInvoke(Thread* self, MethodHelper* mh,
                                    const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
                                    JValue* result, size_t arg_offset) {
   // In a runtime that's not started we intercept certain methods to avoid complicated dependency
@@ -834,12 +855,12 @@
     // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
     // going the reflective Dex way.
     Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
-    String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
+    String* name2 = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
     ArtField* found = NULL;
     ObjectArray<ArtField>* fields = klass->GetIFields();
     for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
       ArtField* f = fields->Get(i);
-      if (name->Equals(f->GetName())) {
+      if (name2->Equals(f->GetName())) {
         found = f;
       }
     }
@@ -847,14 +868,14 @@
       fields = klass->GetSFields();
       for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
         ArtField* f = fields->Get(i);
-        if (name->Equals(f->GetName())) {
+        if (name2->Equals(f->GetName())) {
           found = f;
         }
       }
     }
     CHECK(found != NULL)
       << "Failed to find field in Class.getDeclaredField in un-started runtime. name="
-      << name->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass);
+      << name2->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass);
     // TODO: getDeclaredField calls GetType once the field is found to ensure a
     //       NoClassDefFoundError is thrown if the field's type cannot be resolved.
     Class* jlr_Field = self->DecodeJObject(WellKnownClasses::java_lang_reflect_Field)->AsClass();
@@ -870,9 +891,8 @@
     Object* obj = shadow_frame->GetVRegReference(arg_offset);
     result->SetI(obj->IdentityHashCode());
   } else if (name == "java.lang.String java.lang.reflect.ArtMethod.getMethodName(java.lang.reflect.ArtMethod)") {
-    StackHandleScope<1> hs(self);
-    MethodHelper mh(hs.NewHandle(shadow_frame->GetVRegReference(arg_offset)->AsArtMethod()));
-    result->SetL(mh.GetNameAsString(self));
+    mirror::ArtMethod* method = shadow_frame->GetVRegReference(arg_offset)->AsArtMethod();
+    result->SetL(method->GetNameAsString(self));
   } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)" ||
              name == "void java.lang.System.arraycopy(char[], int, char[], int, int)") {
     // Special case array copying without initializing System.
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 5a1d01e..7f6303a 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -21,6 +21,9 @@
 
 #include <math.h>
 
+#include <iostream>
+#include <sstream>
+
 #include "base/logging.h"
 #include "class_linker-inl.h"
 #include "common_throws.h"
@@ -184,7 +187,7 @@
 
 // Handles string resolution for const-string and const-string-jumbo instructions. Also ensures the
 // java.lang.String class is initialized.
-static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx)
+static inline String* ResolveString(Thread* self, ShadowFrame& shadow_frame, uint32_t string_idx)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   CHECK(!kMovingMethods);
   Class* java_lang_string_class = String::GetJavaLangString();
@@ -192,12 +195,20 @@
     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;
     }
   }
-  return mh.ResolveString(string_idx);
+  mirror::ArtMethod* method = shadow_frame.GetMethod();
+  mirror::String* s = method->GetDexCacheStrings()->Get(string_idx);
+  if (UNLIKELY(s == nullptr)) {
+    StackHandleScope<1> hs(self);
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
+    s = Runtime::Current()->GetClassLinker()->ResolveString(*method->GetDexFile(), string_idx,
+                                                            dex_cache);
+  }
+  return s;
 }
 
 // Handles div-int, div-int/2addr, div-int/li16 and div-int/lit8 instructions.
@@ -205,7 +216,7 @@
 static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg,
                                int32_t dividend, int32_t divisor)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  const int32_t kMinInt = std::numeric_limits<int32_t>::min();
+  constexpr int32_t kMinInt = std::numeric_limits<int32_t>::min();
   if (UNLIKELY(divisor == 0)) {
     ThrowArithmeticExceptionDivideByZero();
     return false;
@@ -223,7 +234,7 @@
 static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg,
                                   int32_t dividend, int32_t divisor)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  const int32_t kMinInt = std::numeric_limits<int32_t>::min();
+  constexpr int32_t kMinInt = std::numeric_limits<int32_t>::min();
   if (UNLIKELY(divisor == 0)) {
     ThrowArithmeticExceptionDivideByZero();
     return false;
@@ -388,11 +399,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..6350c56 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -15,6 +15,7 @@
  */
 
 #include "interpreter_common.h"
+#include "safe_math.h"
 
 namespace art {
 namespace interpreter {
@@ -249,9 +250,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 +267,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 +285,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 +302,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,15 +318,11 @@
 
   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) {
-      StackHandleScope<1> hs(self);
-      MethodHelper mh(hs.NewHandle(shadow_frame.GetMethod()));
-      Class* return_type = mh.GetReturnType();
+      Class* return_type = shadow_frame.GetMethod()->GetReturnType();
       obj_result = shadow_frame.GetVRegReference(vreg_index);
       if (return_type == NULL) {
         // Return the pending exception.
@@ -430,7 +419,7 @@
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(CONST_STRING) {
-    String* s = ResolveString(self, mh, inst->VRegB_21c());
+    String* s = ResolveString(self, shadow_frame, inst->VRegB_21c());
     if (UNLIKELY(s == NULL)) {
       HANDLE_PENDING_EXCEPTION();
     } else {
@@ -441,7 +430,7 @@
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(CONST_STRING_JUMBO) {
-    String* s = ResolveString(self, mh, inst->VRegB_31c());
+    String* s = ResolveString(self, shadow_frame, inst->VRegB_31c());
     if (UNLIKELY(s == NULL)) {
       HANDLE_PENDING_EXCEPTION();
     } else {
@@ -583,30 +572,14 @@
 
   HANDLE_INSTRUCTION_START(FILL_ARRAY_DATA) {
     Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
-    if (UNLIKELY(obj == NULL)) {
-      ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      Array* array = obj->AsArray();
-      DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
-      const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
-      const Instruction::ArrayDataPayload* payload =
-          reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
-      if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
-        self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
-                                 "Ljava/lang/ArrayIndexOutOfBoundsException;",
-                                 "failed FILL_ARRAY_DATA; length=%d, index=%d",
-                                 array->GetLength(), payload->element_count);
-        HANDLE_PENDING_EXCEPTION();
-      } else {
-        if (transaction_active) {
-          RecordArrayElementsInTransaction(array, payload->element_count);
-        }
-        uint32_t size_in_bytes = payload->element_count * payload->element_width;
-        memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes);
-        ADVANCE(3);
-      }
+    const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+    const Instruction::ArrayDataPayload* payload =
+        reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
+    bool success = FillArrayData(obj, payload);
+    if (transaction_active && success) {
+      RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count);
     }
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
   }
   HANDLE_INSTRUCTION_END();
 
@@ -632,7 +605,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 +617,7 @@
     int16_t offset = inst->VRegA_20t();
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
-        CheckSuspend(self);
+        self->CheckSuspend();
         UPDATE_HANDLER_TABLE();
       }
     }
@@ -656,7 +629,7 @@
     int32_t offset = inst->VRegA_30t();
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
-        CheckSuspend(self);
+        self->CheckSuspend();
         UPDATE_HANDLER_TABLE();
       }
     }
@@ -668,7 +641,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 +653,7 @@
     int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
-        CheckSuspend(self);
+        self->CheckSuspend();
         UPDATE_HANDLER_TABLE();
       }
     }
@@ -688,6 +661,11 @@
   }
   HANDLE_INSTRUCTION_END();
 
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wfloat-equal"
+#endif
+
   HANDLE_INSTRUCTION_START(CMPL_FLOAT) {
     float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
     float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
@@ -752,6 +730,10 @@
   }
   HANDLE_INSTRUCTION_END();
 
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
   HANDLE_INSTRUCTION_START(CMP_LONG) {
     int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
     int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
@@ -773,7 +755,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -789,7 +771,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -805,7 +787,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -821,7 +803,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -837,7 +819,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -853,7 +835,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -869,7 +851,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -885,7 +867,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -901,7 +883,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -917,7 +899,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -933,7 +915,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -949,7 +931,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -1369,6 +1351,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);
@@ -1629,22 +1635,22 @@
 
   HANDLE_INSTRUCTION_START(ADD_INT)
     shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_23x()) +
-                         shadow_frame.GetVReg(inst->VRegC_23x()));
+                         SafeAdd(shadow_frame.GetVReg(inst->VRegB_23x()),
+                                 shadow_frame.GetVReg(inst->VRegC_23x())));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(SUB_INT)
     shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_23x()) -
-                         shadow_frame.GetVReg(inst->VRegC_23x()));
+                         SafeSub(shadow_frame.GetVReg(inst->VRegB_23x()),
+                                 shadow_frame.GetVReg(inst->VRegC_23x())));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(MUL_INT)
     shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_23x()) *
-                         shadow_frame.GetVReg(inst->VRegC_23x()));
+                         SafeMul(shadow_frame.GetVReg(inst->VRegB_23x()),
+                                 shadow_frame.GetVReg(inst->VRegC_23x())));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
@@ -1708,22 +1714,22 @@
 
   HANDLE_INSTRUCTION_START(ADD_LONG)
     shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_23x()) +
-                             shadow_frame.GetVRegLong(inst->VRegC_23x()));
+                             SafeAdd(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                                     shadow_frame.GetVRegLong(inst->VRegC_23x())));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(SUB_LONG)
     shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_23x()) -
-                             shadow_frame.GetVRegLong(inst->VRegC_23x()));
+                             SafeSub(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                                     shadow_frame.GetVRegLong(inst->VRegC_23x())));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(MUL_LONG)
     shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_23x()) *
-                             shadow_frame.GetVRegLong(inst->VRegC_23x()));
+                             SafeMul(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                                     shadow_frame.GetVRegLong(inst->VRegC_23x())));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
@@ -1858,8 +1864,8 @@
   HANDLE_INSTRUCTION_START(ADD_INT_2ADDR) {
     uint32_t vregA = inst->VRegA_12x(inst_data);
     shadow_frame.SetVReg(vregA,
-                         shadow_frame.GetVReg(vregA) +
-                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+                         SafeAdd(shadow_frame.GetVReg(vregA),
+                                 shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
     ADVANCE(1);
   }
   HANDLE_INSTRUCTION_END();
@@ -1867,8 +1873,8 @@
   HANDLE_INSTRUCTION_START(SUB_INT_2ADDR) {
     uint32_t vregA = inst->VRegA_12x(inst_data);
     shadow_frame.SetVReg(vregA,
-                         shadow_frame.GetVReg(vregA) -
-                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+                         SafeSub(shadow_frame.GetVReg(vregA),
+                                 shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
     ADVANCE(1);
   }
   HANDLE_INSTRUCTION_END();
@@ -1876,8 +1882,8 @@
   HANDLE_INSTRUCTION_START(MUL_INT_2ADDR) {
     uint32_t vregA = inst->VRegA_12x(inst_data);
     shadow_frame.SetVReg(vregA,
-                         shadow_frame.GetVReg(vregA) *
-                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+                         SafeMul(shadow_frame.GetVReg(vregA),
+                                 shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
     ADVANCE(1);
   }
   HANDLE_INSTRUCTION_END();
@@ -1955,8 +1961,8 @@
   HANDLE_INSTRUCTION_START(ADD_LONG_2ADDR) {
     uint32_t vregA = inst->VRegA_12x(inst_data);
     shadow_frame.SetVRegLong(vregA,
-                             shadow_frame.GetVRegLong(vregA) +
-                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+                             SafeAdd(shadow_frame.GetVRegLong(vregA),
+                                     shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
     ADVANCE(1);
   }
   HANDLE_INSTRUCTION_END();
@@ -1964,8 +1970,8 @@
   HANDLE_INSTRUCTION_START(SUB_LONG_2ADDR) {
     uint32_t vregA = inst->VRegA_12x(inst_data);
     shadow_frame.SetVRegLong(vregA,
-                             shadow_frame.GetVRegLong(vregA) -
-                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+                             SafeSub(shadow_frame.GetVRegLong(vregA),
+                                     shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
     ADVANCE(1);
   }
   HANDLE_INSTRUCTION_END();
@@ -1973,8 +1979,8 @@
   HANDLE_INSTRUCTION_START(MUL_LONG_2ADDR) {
     uint32_t vregA = inst->VRegA_12x(inst_data);
     shadow_frame.SetVRegLong(vregA,
-                             shadow_frame.GetVRegLong(vregA) *
-                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+                             SafeMul(shadow_frame.GetVRegLong(vregA),
+                                     shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
     ADVANCE(1);
   }
   HANDLE_INSTRUCTION_END();
@@ -2141,22 +2147,22 @@
 
   HANDLE_INSTRUCTION_START(ADD_INT_LIT16)
     shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) +
-                         inst->VRegC_22s());
+                         SafeAdd(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+                                 inst->VRegC_22s()));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(RSUB_INT)
     shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                         inst->VRegC_22s() -
-                         shadow_frame.GetVReg(inst->VRegB_22s(inst_data)));
+                         SafeSub(inst->VRegC_22s(),
+                                 shadow_frame.GetVReg(inst->VRegB_22s(inst_data))));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(MUL_INT_LIT16)
     shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) *
-                         inst->VRegC_22s());
+                         SafeMul(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+                                 inst->VRegC_22s()));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
@@ -2197,22 +2203,22 @@
 
   HANDLE_INSTRUCTION_START(ADD_INT_LIT8)
     shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22b()) +
-                         inst->VRegC_22b());
+                         SafeAdd(shadow_frame.GetVReg(inst->VRegB_22b()),
+                                 inst->VRegC_22b()));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(RSUB_INT_LIT8)
     shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         inst->VRegC_22b() -
-                         shadow_frame.GetVReg(inst->VRegB_22b()));
+                         SafeSub(inst->VRegC_22b(),
+                                 shadow_frame.GetVReg(inst->VRegB_22b())));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(MUL_INT_LIT8)
     shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22b()) *
-                         inst->VRegC_22b());
+                         SafeMul(shadow_frame.GetVReg(inst->VRegB_22b()),
+                                 inst->VRegC_22b()));
     ADVANCE(2);
   HANDLE_INSTRUCTION_END();
 
@@ -2304,22 +2310,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 +2381,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..1b6f53e 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -15,6 +15,7 @@
  */
 
 #include "interpreter_common.h"
+#include "safe_math.h"
 
 namespace art {
 namespace interpreter {
@@ -22,9 +23,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 +174,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 +188,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 +203,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 +217,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,15 +230,11 @@
       }
       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) {
-          StackHandleScope<1> hs(self);
-          MethodHelper mhs(hs.NewHandle(shadow_frame.GetMethod()));
-          Class* return_type = mhs.GetReturnType();
+          Class* return_type = shadow_frame.GetMethod()->GetReturnType();
           // Re-load since it might have moved.
           obj_result = shadow_frame.GetVRegReference(ref_idx);
           if (return_type == NULL) {
@@ -343,7 +330,7 @@
         break;
       case Instruction::CONST_STRING: {
         PREAMBLE();
-        String* s = ResolveString(self, mh,  inst->VRegB_21c());
+        String* s = ResolveString(self, shadow_frame,  inst->VRegB_21c());
         if (UNLIKELY(s == NULL)) {
           HANDLE_PENDING_EXCEPTION();
         } else {
@@ -354,7 +341,7 @@
       }
       case Instruction::CONST_STRING_JUMBO: {
         PREAMBLE();
-        String* s = ResolveString(self, mh,  inst->VRegB_31c());
+        String* s = ResolveString(self, shadow_frame,  inst->VRegB_31c());
         if (UNLIKELY(s == NULL)) {
           HANDLE_PENDING_EXCEPTION();
         } else {
@@ -496,30 +483,18 @@
       }
       case Instruction::FILL_ARRAY_DATA: {
         PREAMBLE();
-        Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
-        if (UNLIKELY(obj == NULL)) {
-          ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        Array* array = obj->AsArray();
-        DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
         const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
         const Instruction::ArrayDataPayload* payload =
             reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
-        if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
-          self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
-                                   "Ljava/lang/ArrayIndexOutOfBoundsException;",
-                                   "failed FILL_ARRAY_DATA; length=%d, index=%d",
-                                   array->GetLength(), payload->element_count);
+        Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
+        bool success = FillArrayData(obj, payload);
+        if (!success) {
           HANDLE_PENDING_EXCEPTION();
           break;
         }
         if (transaction_active) {
-          RecordArrayElementsInTransaction(array, payload->element_count);
+          RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count);
         }
-        uint32_t size_in_bytes = payload->element_count * payload->element_width;
-        memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes);
         inst = inst->Next_3xx();
         break;
       }
@@ -545,9 +520,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 +529,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 +538,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 +547,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,13 +556,17 @@
         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;
       }
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wfloat-equal"
+#endif
+
       case Instruction::CMPL_FLOAT: {
         PREAMBLE();
         float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
@@ -661,6 +632,11 @@
         inst = inst->Next_2xx();
         break;
       }
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
       case Instruction::CMP_LONG: {
         PREAMBLE();
         int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
@@ -682,9 +658,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 +671,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 +684,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 +697,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 +710,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 +723,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 +736,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 +749,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 +762,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 +775,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 +788,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 +801,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 +1218,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);
@@ -1520,25 +1496,26 @@
                              static_cast<int16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
         inst = inst->Next_1xx();
         break;
-      case Instruction::ADD_INT:
+      case Instruction::ADD_INT: {
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_23x()) +
-                             shadow_frame.GetVReg(inst->VRegC_23x()));
+                             SafeAdd(shadow_frame.GetVReg(inst->VRegB_23x()),
+                                     shadow_frame.GetVReg(inst->VRegC_23x())));
         inst = inst->Next_2xx();
         break;
+      }
       case Instruction::SUB_INT:
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_23x()) -
-                             shadow_frame.GetVReg(inst->VRegC_23x()));
+                             SafeSub(shadow_frame.GetVReg(inst->VRegB_23x()),
+                                     shadow_frame.GetVReg(inst->VRegC_23x())));
         inst = inst->Next_2xx();
         break;
       case Instruction::MUL_INT:
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_23x()) *
-                             shadow_frame.GetVReg(inst->VRegC_23x()));
+                             SafeMul(shadow_frame.GetVReg(inst->VRegB_23x()),
+                                     shadow_frame.GetVReg(inst->VRegC_23x())));
         inst = inst->Next_2xx();
         break;
       case Instruction::DIV_INT: {
@@ -1602,29 +1579,29 @@
       case Instruction::ADD_LONG:
         PREAMBLE();
         shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_23x()) +
-                                 shadow_frame.GetVRegLong(inst->VRegC_23x()));
+                                 SafeAdd(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                                         shadow_frame.GetVRegLong(inst->VRegC_23x())));
         inst = inst->Next_2xx();
         break;
       case Instruction::SUB_LONG:
         PREAMBLE();
         shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_23x()) -
-                                 shadow_frame.GetVRegLong(inst->VRegC_23x()));
+                                 SafeSub(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                                         shadow_frame.GetVRegLong(inst->VRegC_23x())));
         inst = inst->Next_2xx();
         break;
       case Instruction::MUL_LONG:
         PREAMBLE();
         shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_23x()) *
-                                 shadow_frame.GetVRegLong(inst->VRegC_23x()));
+                                 SafeMul(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                                         shadow_frame.GetVRegLong(inst->VRegC_23x())));
         inst = inst->Next_2xx();
         break;
       case Instruction::DIV_LONG:
         PREAMBLE();
         DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
                      shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                    shadow_frame.GetVRegLong(inst->VRegC_23x()));
+                     shadow_frame.GetVRegLong(inst->VRegC_23x()));
         POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
         break;
       case Instruction::REM_LONG:
@@ -1749,9 +1726,8 @@
       case Instruction::ADD_INT_2ADDR: {
         PREAMBLE();
         uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA,
-                             shadow_frame.GetVReg(vregA) +
-                             shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+        shadow_frame.SetVReg(vregA, SafeAdd(shadow_frame.GetVReg(vregA),
+                                            shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
         inst = inst->Next_1xx();
         break;
       }
@@ -1759,8 +1735,8 @@
         PREAMBLE();
         uint4_t vregA = inst->VRegA_12x(inst_data);
         shadow_frame.SetVReg(vregA,
-                             shadow_frame.GetVReg(vregA) -
-                             shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+                             SafeSub(shadow_frame.GetVReg(vregA),
+                                     shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
         inst = inst->Next_1xx();
         break;
       }
@@ -1768,8 +1744,8 @@
         PREAMBLE();
         uint4_t vregA = inst->VRegA_12x(inst_data);
         shadow_frame.SetVReg(vregA,
-                             shadow_frame.GetVReg(vregA) *
-                             shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+                             SafeMul(shadow_frame.GetVReg(vregA),
+                                     shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
         inst = inst->Next_1xx();
         break;
       }
@@ -1847,8 +1823,8 @@
         PREAMBLE();
         uint4_t vregA = inst->VRegA_12x(inst_data);
         shadow_frame.SetVRegLong(vregA,
-                                 shadow_frame.GetVRegLong(vregA) +
-                                 shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+                                 SafeAdd(shadow_frame.GetVRegLong(vregA),
+                                         shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
         inst = inst->Next_1xx();
         break;
       }
@@ -1856,8 +1832,8 @@
         PREAMBLE();
         uint4_t vregA = inst->VRegA_12x(inst_data);
         shadow_frame.SetVRegLong(vregA,
-                                 shadow_frame.GetVRegLong(vregA) -
-                                 shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+                                 SafeSub(shadow_frame.GetVRegLong(vregA),
+                                         shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
         inst = inst->Next_1xx();
         break;
       }
@@ -1865,8 +1841,8 @@
         PREAMBLE();
         uint4_t vregA = inst->VRegA_12x(inst_data);
         shadow_frame.SetVRegLong(vregA,
-                                 shadow_frame.GetVRegLong(vregA) *
-                                 shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+                                 SafeMul(shadow_frame.GetVRegLong(vregA),
+                                         shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
         inst = inst->Next_1xx();
         break;
       }
@@ -2033,22 +2009,22 @@
       case Instruction::ADD_INT_LIT16:
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) +
-                             inst->VRegC_22s());
+                             SafeAdd(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+                                     inst->VRegC_22s()));
         inst = inst->Next_2xx();
         break;
-      case Instruction::RSUB_INT:
+      case Instruction::RSUB_INT_LIT16:
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                             inst->VRegC_22s() -
-                             shadow_frame.GetVReg(inst->VRegB_22s(inst_data)));
+                             SafeSub(inst->VRegC_22s(),
+                                     shadow_frame.GetVReg(inst->VRegB_22s(inst_data))));
         inst = inst->Next_2xx();
         break;
       case Instruction::MUL_INT_LIT16:
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) *
-                             inst->VRegC_22s());
+                             SafeMul(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+                                     inst->VRegC_22s()));
         inst = inst->Next_2xx();
         break;
       case Instruction::DIV_INT_LIT16: {
@@ -2089,22 +2065,19 @@
       case Instruction::ADD_INT_LIT8:
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22b()) +
-                             inst->VRegC_22b());
+                             SafeAdd(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
         inst = inst->Next_2xx();
         break;
       case Instruction::RSUB_INT_LIT8:
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             inst->VRegC_22b() -
-                             shadow_frame.GetVReg(inst->VRegB_22b()));
+                             SafeSub(inst->VRegC_22b(), shadow_frame.GetVReg(inst->VRegB_22b())));
         inst = inst->Next_2xx();
         break;
       case Instruction::MUL_INT_LIT8:
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22b()) *
-                             inst->VRegC_22b());
+                             SafeMul(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
         inst = inst->Next_2xx();
         break;
       case Instruction::DIV_INT_LIT8: {
@@ -2164,7 +2137,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/interpreter/safe_math.h b/runtime/interpreter/safe_math.h
new file mode 100644
index 0000000..78b3539
--- /dev/null
+++ b/runtime/interpreter/safe_math.h
@@ -0,0 +1,65 @@
+/*
+ * 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_RUNTIME_INTERPRETER_SAFE_MATH_H_
+#define ART_RUNTIME_INTERPRETER_SAFE_MATH_H_
+
+#include <functional>
+#include <type_traits>
+
+namespace art {
+namespace interpreter {
+
+// Declares a type which is the larger in bit size of the two template parameters.
+template <typename T1, typename T2>
+struct select_bigger {
+  typedef typename std::conditional<sizeof(T1) >= sizeof(T2), T1, T2>::type type;
+};
+
+// Perform signed arithmetic Op on 'a' and 'b' with defined wrapping behavior.
+template<template <typename OpT> class Op, typename T1, typename T2>
+static inline typename select_bigger<T1, T2>::type SafeMath(T1 a, T2 b) {
+  typedef typename select_bigger<T1, T2>::type biggest_T;
+  typedef typename std::make_unsigned<biggest_T>::type unsigned_biggest_T;
+  static_assert(std::is_signed<T1>::value, "Expected T1 to be signed");
+  static_assert(std::is_signed<T2>::value, "Expected T2 to be signed");
+  unsigned_biggest_T val1 = static_cast<unsigned_biggest_T>(static_cast<biggest_T>(a));
+  unsigned_biggest_T val2 = static_cast<unsigned_biggest_T>(b);
+  return static_cast<biggest_T>(Op<unsigned_biggest_T>()(val1, val2));
+}
+
+// Perform signed a signed add on 'a' and 'b' with defined wrapping behavior.
+template<typename T1, typename T2>
+static inline typename select_bigger<T1, T2>::type SafeAdd(T1 a, T2 b) {
+  return SafeMath<std::plus>(a, b);
+}
+
+// Perform signed a signed substract on 'a' and 'b' with defined wrapping behavior.
+template<typename T1, typename T2>
+static inline typename select_bigger<T1, T2>::type SafeSub(T1 a, T2 b) {
+  return SafeMath<std::minus>(a, b);
+}
+
+// Perform signed a signed multiply on 'a' and 'b' with defined wrapping behavior.
+template<typename T1, typename T2>
+static inline typename select_bigger<T1, T2>::type SafeMul(T1 a, T2 b) {
+  return SafeMath<std::multiplies>(a, b);
+}
+
+}  // namespace interpreter
+}  // namespace art
+
+#endif  // ART_RUNTIME_INTERPRETER_SAFE_MATH_H_
diff --git a/runtime/interpreter/safe_math_test.cc b/runtime/interpreter/safe_math_test.cc
new file mode 100644
index 0000000..28087a3
--- /dev/null
+++ b/runtime/interpreter/safe_math_test.cc
@@ -0,0 +1,132 @@
+/*
+ * 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 "safe_math.h"
+
+#include <limits>
+
+#include "gtest/gtest.h"
+
+namespace art {
+namespace interpreter {
+
+TEST(SafeMath, Add) {
+  // Adding 1 overflows 0x7ff... to 0x800... aka max and min.
+  EXPECT_EQ(SafeAdd(std::numeric_limits<int32_t>::max(), 1),
+            std::numeric_limits<int32_t>::min());
+  EXPECT_EQ(SafeAdd(std::numeric_limits<int64_t>::max(), 1),
+            std::numeric_limits<int64_t>::min());
+
+  // Vanilla arithmetic should work too.
+  EXPECT_EQ(SafeAdd(std::numeric_limits<int32_t>::max() - 1, 1),
+            std::numeric_limits<int32_t>::max());
+  EXPECT_EQ(SafeAdd(std::numeric_limits<int64_t>::max() - 1, 1),
+            std::numeric_limits<int64_t>::max());
+
+  EXPECT_EQ(SafeAdd(std::numeric_limits<int32_t>::min() + 1, -1),
+            std::numeric_limits<int32_t>::min());
+  EXPECT_EQ(SafeAdd(std::numeric_limits<int64_t>::min() + 1, -1),
+            std::numeric_limits<int64_t>::min());
+
+  EXPECT_EQ(SafeAdd(int32_t(-1), -1), -2);
+  EXPECT_EQ(SafeAdd(int64_t(-1), -1), -2);
+
+  EXPECT_EQ(SafeAdd(int32_t(1), 1), 2);
+  EXPECT_EQ(SafeAdd(int64_t(1), 1), 2);
+
+  EXPECT_EQ(SafeAdd(int32_t(-1), 1), 0);
+  EXPECT_EQ(SafeAdd(int64_t(-1), 1), 0);
+
+  EXPECT_EQ(SafeAdd(int32_t(1), -1), 0);
+  EXPECT_EQ(SafeAdd(int64_t(1), -1), 0);
+
+  // Test sign extension of smaller operand sizes.
+  EXPECT_EQ(SafeAdd(int32_t(1), int8_t(-1)), 0);
+  EXPECT_EQ(SafeAdd(int64_t(1), int8_t(-1)), 0);
+}
+
+TEST(SafeMath, Sub) {
+  // Subtracting 1 underflows 0x800... to 0x7ff... aka min and max.
+  EXPECT_EQ(SafeSub(std::numeric_limits<int32_t>::min(), 1),
+            std::numeric_limits<int32_t>::max());
+  EXPECT_EQ(SafeSub(std::numeric_limits<int64_t>::min(), 1),
+            std::numeric_limits<int64_t>::max());
+
+  // Vanilla arithmetic should work too.
+  EXPECT_EQ(SafeSub(std::numeric_limits<int32_t>::max() - 1, -1),
+            std::numeric_limits<int32_t>::max());
+  EXPECT_EQ(SafeSub(std::numeric_limits<int64_t>::max() - 1, -1),
+            std::numeric_limits<int64_t>::max());
+
+  EXPECT_EQ(SafeSub(std::numeric_limits<int32_t>::min() + 1, 1),
+            std::numeric_limits<int32_t>::min());
+  EXPECT_EQ(SafeSub(std::numeric_limits<int64_t>::min() + 1, 1),
+            std::numeric_limits<int64_t>::min());
+
+  EXPECT_EQ(SafeSub(int32_t(-1), -1), 0);
+  EXPECT_EQ(SafeSub(int64_t(-1), -1), 0);
+
+  EXPECT_EQ(SafeSub(int32_t(1), 1), 0);
+  EXPECT_EQ(SafeSub(int64_t(1), 1), 0);
+
+  EXPECT_EQ(SafeSub(int32_t(-1), 1), -2);
+  EXPECT_EQ(SafeSub(int64_t(-1), 1), -2);
+
+  EXPECT_EQ(SafeSub(int32_t(1), -1), 2);
+  EXPECT_EQ(SafeSub(int64_t(1), -1), 2);
+
+  // Test sign extension of smaller operand sizes.
+  EXPECT_EQ(SafeAdd(int32_t(1), int8_t(-1)), 0);
+  EXPECT_EQ(SafeAdd(int64_t(1), int8_t(-1)), 0);
+}
+
+TEST(SafeMath, Mul) {
+  // Multiplying by 2 overflows 0x7ff...f to 0xfff...e aka max and -2.
+  EXPECT_EQ(SafeMul(std::numeric_limits<int32_t>::max(), 2),
+            -2);
+  EXPECT_EQ(SafeMul(std::numeric_limits<int64_t>::max(), 2),
+            -2);
+
+  // Vanilla arithmetic should work too.
+  EXPECT_EQ(SafeMul(std::numeric_limits<int32_t>::max() / 2, 2),
+            std::numeric_limits<int32_t>::max() - 1);  // -1 as LSB is lost by division.
+  EXPECT_EQ(SafeMul(std::numeric_limits<int64_t>::max() / 2, 2),
+            std::numeric_limits<int64_t>::max() - 1);  // -1 as LSB is lost by division.
+
+  EXPECT_EQ(SafeMul(std::numeric_limits<int32_t>::min() / 2, 2),
+            std::numeric_limits<int32_t>::min());
+  EXPECT_EQ(SafeMul(std::numeric_limits<int64_t>::min() / 2, 2),
+            std::numeric_limits<int64_t>::min());
+
+  EXPECT_EQ(SafeMul(int32_t(-1), -1), 1);
+  EXPECT_EQ(SafeMul(int64_t(-1), -1), 1);
+
+  EXPECT_EQ(SafeMul(int32_t(1), 1), 1);
+  EXPECT_EQ(SafeMul(int64_t(1), 1), 1);
+
+  EXPECT_EQ(SafeMul(int32_t(-1), 1), -1);
+  EXPECT_EQ(SafeMul(int64_t(-1), 1), -1);
+
+  EXPECT_EQ(SafeMul(int32_t(1), -1), -1);
+  EXPECT_EQ(SafeMul(int64_t(1), -1), -1);
+
+  // Test sign extension of smaller operand sizes.
+  EXPECT_EQ(SafeMul(int32_t(1), int8_t(-1)), -1);
+  EXPECT_EQ(SafeMul(int64_t(1), int8_t(-1)), -1);
+}
+
+}  // namespace interpreter
+}  // namespace art
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
new file mode 100644
index 0000000..a5abce6
--- /dev/null
+++ b/runtime/java_vm_ext.cc
@@ -0,0 +1,814 @@
+/*
+ * 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/dumpable.h"
+#include "base/mutex.h"
+#include "base/stl_util.h"
+#include "check_jni.h"
+#include "fault_handler.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);
+
+    if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
+      fault_manager.EnsureArtActionInFrontOfSignalChain();
+    }
+
+    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_buf, jsize buf_len, jsize* vm_count) {
+  Runtime* runtime = Runtime::Current();
+  if (runtime == nullptr || buf_len == 0) {
+    *vm_count = 0;
+  } else {
+    *vm_count = 1;
+    vms_buf[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/java_vm_ext_test.cc b/runtime/java_vm_ext_test.cc
new file mode 100644
index 0000000..60c6a5c
--- /dev/null
+++ b/runtime/java_vm_ext_test.cc
@@ -0,0 +1,132 @@
+/*
+ * 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 <pthread.h>
+
+#include "common_runtime_test.h"
+#include "java_vm_ext.h"
+#include "runtime.h"
+
+namespace art {
+
+class JavaVmExtTest : public CommonRuntimeTest {
+ protected:
+  virtual void SetUp() {
+    CommonRuntimeTest::SetUp();
+
+    vm_ = Runtime::Current()->GetJavaVM();
+  }
+
+
+  virtual void TearDown() OVERRIDE {
+    CommonRuntimeTest::TearDown();
+  }
+
+  JavaVMExt* vm_;
+};
+
+TEST_F(JavaVmExtTest, JNI_GetDefaultJavaVMInitArgs) {
+  jint err = JNI_GetDefaultJavaVMInitArgs(nullptr);
+  EXPECT_EQ(JNI_ERR, err);
+}
+
+TEST_F(JavaVmExtTest, JNI_GetCreatedJavaVMs) {
+  JavaVM* vms_buf[1];
+  jsize num_vms;
+  jint ok = JNI_GetCreatedJavaVMs(vms_buf, arraysize(vms_buf), &num_vms);
+  EXPECT_EQ(JNI_OK, ok);
+  EXPECT_EQ(1, num_vms);
+  EXPECT_EQ(vms_buf[0], vm_);
+}
+
+static bool gSmallStack = false;
+static bool gAsDaemon = false;
+
+static void* attach_current_thread_callback(void* arg ATTRIBUTE_UNUSED) {
+  JavaVM* vms_buf[1];
+  jsize num_vms;
+  JNIEnv* env;
+  jint ok = JNI_GetCreatedJavaVMs(vms_buf, arraysize(vms_buf), &num_vms);
+  EXPECT_EQ(JNI_OK, ok);
+  if (ok == JNI_OK) {
+    if (!gAsDaemon) {
+      ok = vms_buf[0]->AttachCurrentThread(&env, nullptr);
+    } else {
+      ok = vms_buf[0]->AttachCurrentThreadAsDaemon(&env, nullptr);
+    }
+    EXPECT_EQ(gSmallStack ? JNI_ERR : JNI_OK, ok);
+    if (ok == JNI_OK) {
+      ok = vms_buf[0]->DetachCurrentThread();
+      EXPECT_EQ(JNI_OK, ok);
+    }
+  }
+  return nullptr;
+}
+
+TEST_F(JavaVmExtTest, AttachCurrentThread) {
+  pthread_t pthread;
+  const char* reason = __PRETTY_FUNCTION__;
+  gSmallStack = false;
+  gAsDaemon = false;
+  CHECK_PTHREAD_CALL(pthread_create, (&pthread, nullptr, attach_current_thread_callback,
+      nullptr), reason);
+  void* ret_val;
+  CHECK_PTHREAD_CALL(pthread_join, (pthread, &ret_val), reason);
+  EXPECT_EQ(ret_val, nullptr);
+}
+
+TEST_F(JavaVmExtTest, AttachCurrentThreadAsDaemon) {
+  pthread_t pthread;
+  const char* reason = __PRETTY_FUNCTION__;
+  gSmallStack = false;
+  gAsDaemon = true;
+  CHECK_PTHREAD_CALL(pthread_create, (&pthread, nullptr, attach_current_thread_callback,
+      nullptr), reason);
+  void* ret_val;
+  CHECK_PTHREAD_CALL(pthread_join, (pthread, &ret_val), reason);
+  EXPECT_EQ(ret_val, nullptr);
+}
+
+TEST_F(JavaVmExtTest, AttachCurrentThread_SmallStack) {
+  pthread_t pthread;
+  pthread_attr_t attr;
+  const char* reason = __PRETTY_FUNCTION__;
+  gSmallStack = true;
+  gAsDaemon = false;
+  CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason);
+  CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, PTHREAD_STACK_MIN), reason);
+  CHECK_PTHREAD_CALL(pthread_create, (&pthread, &attr, attach_current_thread_callback,
+      nullptr), reason);
+  CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), reason);
+  void* ret_val;
+  CHECK_PTHREAD_CALL(pthread_join, (pthread, &ret_val), reason);
+  EXPECT_EQ(ret_val, nullptr);
+}
+
+TEST_F(JavaVmExtTest, DetachCurrentThread) {
+  JNIEnv* env;
+  jint ok = vm_->AttachCurrentThread(&env, nullptr);
+  ASSERT_EQ(JNI_OK, ok);
+  ok = vm_->DetachCurrentThread();
+  EXPECT_EQ(JNI_OK, ok);
+
+  jint err = vm_->DetachCurrentThread();
+  EXPECT_EQ(JNI_ERR, err);
+}
+
+}  // namespace art
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_adb.cc b/runtime/jdwp/jdwp_adb.cc
index fe91bb6..df7d068 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -84,13 +84,13 @@
     shutting_down_ = true;
 
     int control_sock = this->control_sock_;
-    int clientSock = this->clientSock;
+    int local_clientSock = this->clientSock;
 
     /* clear these out so it doesn't wake up and try to reuse them */
     this->control_sock_ = this->clientSock = -1;
 
-    if (clientSock != -1) {
-      shutdown(clientSock, SHUT_RDWR);
+    if (local_clientSock != -1) {
+      shutdown(local_clientSock, SHUT_RDWR);
     }
 
     if (control_sock != -1) {
diff --git a/runtime/jdwp/jdwp_bits.h b/runtime/jdwp/jdwp_bits.h
index 9f80cbe..f9cf9ca 100644
--- a/runtime/jdwp/jdwp_bits.h
+++ b/runtime/jdwp/jdwp_bits.h
@@ -68,7 +68,7 @@
 
 // @deprecated
 static inline void Set1(uint8_t* buf, uint8_t val) {
-  *buf = (uint8_t)(val);
+  *buf = val;
 }
 
 // @deprecated
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 10a017c..44f713c 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -138,7 +138,7 @@
     }
 }
 
-uint32_t GetInstrumentationEventFor(JdwpEventKind eventKind) {
+static uint32_t GetInstrumentationEventFor(JdwpEventKind eventKind) {
   switch (eventKind) {
     case EK_BREAKPOINT:
     case EK_SINGLE_STEP:
@@ -791,7 +791,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 e6d7e7b..be34bd3 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,7 +222,7 @@
  * 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(nullptr /* all thread groups */, &thread_ids);
@@ -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,19 +324,19 @@
   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, "/");
 
   std::vector<std::string> class_path;
-  Split(Runtime::Current()->GetClassPathString(), ':', class_path);
+  Split(Runtime::Current()->GetClassPathString(), ':', &class_path);
   expandBufAdd4BE(pReply, class_path.size());
   for (size_t i = 0; i < class_path.size(); ++i) {
     expandBufAddUtf8String(pReply, class_path[i]);
   }
 
   std::vector<std::string> boot_class_path;
-  Split(Runtime::Current()->GetBootClassPathString(), ':', boot_class_path);
+  Split(Runtime::Current()->GetBootClassPathString(), ':', &boot_class_path);
   expandBufAdd4BE(pReply, boot_class_path.size());
   for (size_t i = 0; i < boot_class_path.size(); ++i) {
     expandBufAddUtf8String(pReply, boot_class_path[i]);
@@ -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,32 @@
   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();
+  UNUSED(reply);
+  ObjectId thread_id = request->ReadThreadId();
   return Dbg::Interrupt(thread_id);
 }
 
@@ -1136,9 +1137,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,9 +1148,9 @@
  *
  * 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();
+  ObjectId thread_group_id = request->ReadThreadGroupId();
   return Dbg::GetThreadGroupName(thread_group_id, pReply);
 }
 
@@ -1157,9 +1158,9 @@
  * 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 thread_group_id = request->ReadThreadGroupId();
   return Dbg::GetThreadGroupParent(thread_group_id, pReply);
 }
 
@@ -1167,21 +1168,21 @@
  * 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();
+  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;
   }
@@ -1195,28 +1196,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.
@@ -1228,11 +1229,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 */
 
@@ -1247,12 +1248,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;
         }
@@ -1262,21 +1263,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;
@@ -1284,7 +1285,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());
       }
@@ -1293,7 +1294,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());
       }
@@ -1301,23 +1302,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;
       }
@@ -1325,9 +1326,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);
 
@@ -1339,7 +1340,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;
@@ -1369,10 +1370,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.
@@ -1383,23 +1384,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_) {
-  return Dbg::GetLocalValues(&request, pReply);
+  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_) {
-  return Dbg::SetLocalValues(&request);
+  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);
@@ -1417,16 +1418,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;
@@ -1447,7 +1448,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;
@@ -1590,20 +1591,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;
 }
 
@@ -1612,10 +1613,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
@@ -1635,7 +1636,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
@@ -1644,7 +1645,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();
@@ -1660,18 +1661,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;
   }
 
@@ -1683,11 +1684,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 << ")";
@@ -1701,7 +1702,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/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc
index 4a80957..e8c0856 100644
--- a/runtime/jdwp/jdwp_socket.cc
+++ b/runtime/jdwp/jdwp_socket.cc
@@ -170,20 +170,20 @@
  * for an open port.)
  */
 void JdwpSocketState::Shutdown() {
-  int listenSock = this->listenSock;
-  int clientSock = this->clientSock;
+  int local_listenSock = this->listenSock;
+  int local_clientSock = this->clientSock;
 
   /* clear these out so it doesn't wake up and try to reuse them */
   this->listenSock = this->clientSock = -1;
 
   /* "shutdown" dislodges blocking read() and accept() calls */
-  if (listenSock != -1) {
-    shutdown(listenSock, SHUT_RDWR);
-    close(listenSock);
+  if (local_listenSock != -1) {
+    shutdown(local_listenSock, SHUT_RDWR);
+    close(local_listenSock);
   }
-  if (clientSock != -1) {
-    shutdown(clientSock, SHUT_RDWR);
-    close(clientSock);
+  if (local_clientSock != -1) {
+    shutdown(local_clientSock, SHUT_RDWR);
+    close(local_clientSock);
   }
 
   WakePipe();
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index b5be3e3..9123994 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -17,13 +17,12 @@
 #include "object_registry.h"
 
 #include "handle_scope-inl.h"
+#include "jni_internal.h"
 #include "mirror/class.h"
 #include "scoped_thread_state_change.h"
 
 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
@@ -124,14 +123,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);
 }
 
@@ -213,10 +214,10 @@
     // Erase the object from the maps. Note object may be null if it's
     // a weak ref and the GC has cleared it.
     int32_t hash_code = entry->identity_hash_code;
-    for (auto it = object_to_entry_.lower_bound(hash_code), end = object_to_entry_.end();
-         it != end && it->first == hash_code; ++it) {
-      if (entry == it->second) {
-        object_to_entry_.erase(it);
+    for (auto inner_it = object_to_entry_.lower_bound(hash_code), end = object_to_entry_.end();
+         inner_it != end && inner_it->first == hash_code; ++inner_it) {
+      if (entry == inner_it->second) {
+        object_to_entry_.erase(inner_it);
         break;
       }
     }
diff --git a/runtime/jdwp/object_registry.h b/runtime/jdwp/object_registry.h
index 4770a7d..0693f33 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));
   }
 
   void Clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -86,9 +89,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_);
@@ -98,7 +98,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..b2d3835
--- /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_in, JavaVMExt* vm_in)
+    : self(self_in),
+      vm(vm_in),
+      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 bdda5c8..67e52cb 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -35,7 +35,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"
@@ -44,7 +45,6 @@
 #include "mirror/object_array-inl.h"
 #include "mirror/string-inl.h"
 #include "mirror/throwable.h"
-#include "nativebridge/native_bridge.h"
 #include "parsed_options.h"
 #include "reflection.h"
 #include "runtime.h"
@@ -57,27 +57,9 @@
 
 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;
-}
+// Consider turning this on when there is errors which could be related to JNI array copies such as
+// things not rendering correctly. E.g. b/16858794
+static constexpr bool kWarnJniAbort = false;
 
 // 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
@@ -131,7 +113,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();
@@ -169,7 +151,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) {
@@ -182,7 +164,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());
@@ -214,8 +196,8 @@
     // Failed to find type from the signature of the field.
     DCHECK(soa.Self()->IsExceptionPending());
     ThrowLocation throw_location;
-    StackHandleScope<1> hs(soa.Self());
-    Handle<mirror::Throwable> cause(hs.NewHandle(soa.Self()->GetException(&throw_location)));
+    StackHandleScope<1> hs2(soa.Self());
+    Handle<mirror::Throwable> cause(hs2.NewHandle(soa.Self()->GetException(&throw_location)));
     soa.Self()->ClearException();
     std::string temp;
     soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
@@ -304,255 +286,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)
 
@@ -567,13 +304,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; \
   }
 
@@ -774,10 +511,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;
   }
 
@@ -791,48 +528,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) {
@@ -849,7 +569,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;
@@ -1915,11 +1634,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);
@@ -2030,6 +1749,7 @@
   }
 
   static void ReleaseStringCritical(JNIEnv* env, jstring java_string, const jchar* chars) {
+    UNUSED(chars);
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string);
     ScopedObjectAccess soa(env);
     gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -2058,7 +1778,7 @@
     return bytes;
   }
 
-  static void ReleaseStringUTFChars(JNIEnv* env, jstring, const char* chars) {
+  static void ReleaseStringUTFChars(JNIEnv*, jstring, const char* chars) {
     delete[] chars;
   }
 
@@ -2067,7 +1787,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();
@@ -2122,7 +1843,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);
@@ -2133,8 +1854,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();
@@ -2152,10 +1873,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);
@@ -2175,8 +1897,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();
@@ -2197,8 +1919,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();
@@ -2370,8 +2092,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);
@@ -2423,7 +2146,7 @@
 
       VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]";
 
-      m->RegisterNative(soa.Self(), fnPtr, is_fast);
+      m->RegisterNative(fnPtr, is_fast);
     }
     return JNI_OK;
   }
@@ -2439,14 +2162,14 @@
     for (size_t i = 0; i < c->NumDirectMethods(); ++i) {
       mirror::ArtMethod* m = c->GetDirectMethod(i);
       if (m->IsNative()) {
-        m->UnregisterNative(soa.Self());
+        m->UnregisterNative();
         unregistered_count++;
       }
     }
     for (size_t i = 0; i < c->NumVirtualMethods(); ++i) {
       mirror::ArtMethod* m = c->GetVirtualMethod(i);
       if (m->IsNative()) {
-        m->UnregisterNative(soa.Self());
+        m->UnregisterNative();
         unregistered_count++;
       }
     }
@@ -2495,17 +2218,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);
@@ -2534,33 +2261,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;
@@ -2577,11 +2295,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);
   }
@@ -2592,9 +2310,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());
@@ -2656,14 +2376,18 @@
       // 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;
       }
-    }
-    // Don't need to copy if we had a direct pointer.
-    if (mode != JNI_ABORT && is_copy) {
-      memcpy(array_data, elements, bytes);
+      if (mode != JNI_ABORT) {
+        memcpy(array_data, elements, bytes);
+      } else if (kWarnJniAbort && memcmp(array_data, elements, bytes) != 0) {
+        // Warn if we have JNI_ABORT and the arrays don't match since this is usually an error.
+        LOG(WARNING) << "Possible incorrect JNI_ABORT in Release*ArrayElements";
+        soa.Self()->DumpJavaStack(LOG(WARNING));
+      }
     }
     if (mode != JNI_COMMIT) {
       if (is_copy) {
@@ -2952,476 +2676,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);
-    }
-
-    if (runtime->GetTargetSdkVersion() != 0 && runtime->GetTargetSdkVersion() <= 21) {
-      fault_manager.EnsureArtActionInFrontOfSignalChain();
-    }
-    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,
@@ -3450,7 +2706,7 @@
     os << "JNIWeakGlobalRefType";
     return os;
   default:
-    LOG(FATAL) << "jobjectRefType[" << static_cast<int>(rhs) << "]";
-    return os;
+    LOG(::art::FATAL) << "jobjectRefType[" << static_cast<int>(rhs) << "]";
+    UNREACHABLE();
   }
 }
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..ccad137 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) {
@@ -921,7 +1170,15 @@
 }
 
 TEST_F(JniInternalTest, GetArrayLength) {
-  // Already tested in NewObjectArray/NewPrimitiveArray.
+  // Already tested in NewObjectArray/NewPrimitiveArray except for NULL.
+  CheckJniAbortCatcher jni_abort_catcher;
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
+  EXPECT_EQ(0, env_->GetArrayLength(nullptr));
+  jni_abort_catcher.Check("java_array == null");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  EXPECT_EQ(JNI_ERR, env_->GetArrayLength(nullptr));
+  jni_abort_catcher.Check("jarray was NULL");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, GetObjectClass) {
@@ -956,8 +1213,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 +1253,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 +1277,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 +1349,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 +1417,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,41 +1522,62 @@
 
   // 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) \
+#define EXPECT_STATIC_PRIMITIVE_FIELD(expect_eq, type, field_name, sig, value1, value2) \
   do { \
     jfieldID fid = env_->GetStaticFieldID(c, field_name, sig); \
     EXPECT_NE(fid, nullptr); \
     env_->SetStatic ## type ## Field(c, fid, value1); \
-    EXPECT_EQ(value1, env_->GetStatic ## type ## Field(c, fid)); \
+    expect_eq(value1, env_->GetStatic ## type ## Field(c, fid)); \
     env_->SetStatic ## type ## Field(c, fid, value2); \
-    EXPECT_EQ(value2, env_->GetStatic ## type ## Field(c, fid)); \
+    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) \
+#define EXPECT_PRIMITIVE_FIELD(expect_eq, instance, type, field_name, sig, value1, value2) \
   do { \
     jfieldID fid = env_->GetFieldID(c, field_name, sig); \
     EXPECT_NE(fid, nullptr); \
     env_->Set ## type ## Field(instance, fid, value1); \
-    EXPECT_EQ(value1, env_->Get ## type ## Field(instance, fid)); \
+    expect_eq(value1, env_->Get ## type ## Field(instance, fid)); \
     env_->Set ## type ## Field(instance, fid, value2); \
-    EXPECT_EQ(value2, env_->Get ## type ## Field(instance, fid)); \
+    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 +1587,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)
 
 
@@ -1297,23 +1612,23 @@
   jobject o = env_->AllocObject(c);
   ASSERT_NE(o, nullptr);
 
-  EXPECT_STATIC_PRIMITIVE_FIELD(Boolean, "sZ", "Z", JNI_TRUE, JNI_FALSE);
-  EXPECT_STATIC_PRIMITIVE_FIELD(Byte, "sB", "B", 1, 2);
-  EXPECT_STATIC_PRIMITIVE_FIELD(Char, "sC", "C", 'a', 'b');
-  EXPECT_STATIC_PRIMITIVE_FIELD(Double, "sD", "D", 1.0, 2.0);
-  EXPECT_STATIC_PRIMITIVE_FIELD(Float, "sF", "F", 1.0, 2.0);
-  EXPECT_STATIC_PRIMITIVE_FIELD(Int, "sI", "I", 1, 2);
-  EXPECT_STATIC_PRIMITIVE_FIELD(Long, "sJ", "J", 1, 2);
-  EXPECT_STATIC_PRIMITIVE_FIELD(Short, "sS", "S", 1, 2);
+  EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Boolean, "sZ", "Z", JNI_TRUE, JNI_FALSE);
+  EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Byte, "sB", "B", 1, 2);
+  EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Char, "sC", "C", 'a', 'b');
+  EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, Double, "sD", "D", 1.0, 2.0);
+  EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, Float, "sF", "F", 1.0, 2.0);
+  EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Int, "sI", "I", 1, 2);
+  EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Long, "sJ", "J", 1, 2);
+  EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Short, "sS", "S", 1, 2);
 
-  EXPECT_PRIMITIVE_FIELD(o, Boolean, "iZ", "Z", JNI_TRUE, JNI_FALSE);
-  EXPECT_PRIMITIVE_FIELD(o, Byte, "iB", "B", 1, 2);
-  EXPECT_PRIMITIVE_FIELD(o, Char, "iC", "C", 'a', 'b');
-  EXPECT_PRIMITIVE_FIELD(o, Double, "iD", "D", 1.0, 2.0);
-  EXPECT_PRIMITIVE_FIELD(o, Float, "iF", "F", 1.0, 2.0);
-  EXPECT_PRIMITIVE_FIELD(o, Int, "iI", "I", 1, 2);
-  EXPECT_PRIMITIVE_FIELD(o, Long, "iJ", "J", 1, 2);
-  EXPECT_PRIMITIVE_FIELD(o, Short, "iS", "S", 1, 2);
+  EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Boolean, "iZ", "Z", JNI_TRUE, JNI_FALSE);
+  EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Byte, "iB", "B", 1, 2);
+  EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Char, "iC", "C", 'a', 'b');
+  EXPECT_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, o, Double, "iD", "D", 1.0, 2.0);
+  EXPECT_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, o, Float, "iF", "F", 1.0, 2.0);
+  EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Int, "iI", "I", 1, 2);
+  EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Long, "iJ", "J", 1, 2);
+  EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Short, "iS", "S", 1, 2);
 }
 
 TEST_F(JniInternalTest, GetObjectField_SetObjectField) {
@@ -1373,12 +1688,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 +1732,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 +1740,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 +1799,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 +1849,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 +1878,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 +1888,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 +1916,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) {
@@ -1664,14 +2019,4 @@
   }
 }
 
-TEST_F(JniInternalTest, DetachCurrentThread) {
-  CleanUpJniEnv();  // cleanup now so TearDown won't have junk from wrong JNIEnv
-  jint ok = vm_->DetachCurrentThread();
-  EXPECT_EQ(JNI_OK, ok);
-
-  jint err = vm_->DetachCurrentThread();
-  EXPECT_EQ(JNI_ERR, err);
-  vm_->AttachCurrentThread(&env_, nullptr);  // need attached thread for CommonRuntimeTest::TearDown
-}
-
 }  // namespace art
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..c52578f 100644
--- a/runtime/lock_word-inl.h
+++ b/runtime/lock_word-inl.h
@@ -34,13 +34,13 @@
 
 inline Monitor* LockWord::FatLockMonitor() const {
   DCHECK_EQ(GetState(), kFatLocked);
-  MonitorId mon_id = static_cast<MonitorId>(value_ & ~(kStateMask << kStateShift));
+  MonitorId mon_id = value_ & ~(kStateMask << kStateShift);
   return MonitorPool::MonitorFromMonitorId(mon_id);
 }
 
 inline size_t LockWord::ForwardingAddress() const {
   DCHECK_EQ(GetState(), kForwardingAddress);
-  return static_cast<size_t>(value_ << kStateSize);
+  return value_ << kStateSize;
 }
 
 inline LockWord::LockWord() : value_(0) {
@@ -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..2d5c71b 100644
--- a/runtime/lock_word.h
+++ b/runtime/lock_word.h
@@ -52,7 +52,7 @@
  */
 class LockWord {
  public:
-  enum {
+  enum SizeShiftsAndMasks {  // private marker to avoid generate-operator-out.py from processing.
     // Number of bits to encode the state, currently just fat or thin/unlocked or hash code.
     kStateSize = 2,
     // Number of bits to encode the thin lock owner.
@@ -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 bad86db..8303f84 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -15,11 +15,12 @@
  */
 
 #include "mem_map.h"
-#include "thread-inl.h"
 
-#include <inttypes.h>
 #include <backtrace/BacktraceMap.h>
+#include <inttypes.h>
+
 #include <memory>
+#include <sstream>
 
 // See CreateStartPos below.
 #ifdef __BIONIC__
@@ -27,7 +28,13 @@
 #endif
 
 #include "base/stringprintf.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
 #include "ScopedFd.h"
+#pragma GCC diagnostic pop
+
+#include "thread-inl.h"
 #include "utils.h"
 
 #define USE_ASHMEM 1
@@ -130,7 +137,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 +159,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,
@@ -191,7 +196,7 @@
 // non-null, we check that pointer is the actual_ptr == expected_ptr,
 // and if not, report in error_msg what the conflict mapping was if
 // found, or a generic error in other cases.
-static bool CheckMapRequest(byte* expected_ptr, void* actual_ptr, size_t byte_count,
+static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte_count,
                             std::string* error_msg) {
   // Handled first by caller for more specific error messages.
   CHECK(actual_ptr != MAP_FAILED);
@@ -236,8 +241,11 @@
   return false;
 }
 
-MemMap* MemMap::MapAnonymous(const char* name, byte* expected_ptr, size_t byte_count, int prot,
+MemMap* MemMap::MapAnonymous(const char* name, uint8_t* expected_ptr, size_t byte_count, int prot,
                              bool low_4gb, std::string* error_msg) {
+#ifndef __LP64__
+  UNUSED(low_4gb);
+#endif
   if (byte_count == 0) {
     return new MemMap(name, nullptr, 0, nullptr, 0, prot, false);
   }
@@ -379,15 +387,17 @@
   if (!CheckMapRequest(expected_ptr, actual, page_aligned_byte_count, error_msg)) {
     return nullptr;
   }
-  return new MemMap(name, reinterpret_cast<byte*>(actual), byte_count, actual,
+  return new MemMap(name, reinterpret_cast<uint8_t*>(actual), byte_count, actual,
                     page_aligned_byte_count, prot, false);
 }
 
-MemMap* MemMap::MapFileAtAddress(byte* expected_ptr, size_t byte_count, int prot, int flags, int fd,
+MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, size_t byte_count, int prot, int flags, int fd,
                                  off_t start, bool reuse, const char* filename,
                                  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 +406,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);
@@ -418,9 +424,9 @@
   size_t page_aligned_byte_count = RoundUp(byte_count + page_offset, kPageSize);
   // The 'expected_ptr' is modified (if specified, ie non-null) to be page aligned to the file but
   // not necessarily to virtual memory. mmap will page align 'expected' for us.
-  byte* page_aligned_expected = (expected_ptr == nullptr) ? nullptr : (expected_ptr - page_offset);
+  uint8_t* page_aligned_expected = (expected_ptr == nullptr) ? nullptr : (expected_ptr - page_offset);
 
-  byte* actual = reinterpret_cast<byte*>(mmap(page_aligned_expected,
+  uint8_t* actual = reinterpret_cast<uint8_t*>(mmap(page_aligned_expected,
                                               page_aligned_byte_count,
                                               prot,
                                               flags,
@@ -473,7 +479,7 @@
   CHECK(found) << "MemMap not found";
 }
 
-MemMap::MemMap(const std::string& name, byte* begin, size_t size, void* base_begin,
+MemMap::MemMap(const std::string& name, uint8_t* begin, size_t size, void* base_begin,
                size_t base_size, int prot, bool reuse)
     : name_(name), begin_(begin), size_(size), base_begin_(base_begin), base_size_(base_size),
       prot_(prot), reuse_(reuse) {
@@ -491,29 +497,29 @@
     DCHECK(maps_ != nullptr);
     maps_->insert(std::make_pair(base_begin_, this));
   }
-};
+}
 
-MemMap* MemMap::RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot,
+MemMap* MemMap::RemapAtEnd(uint8_t* new_end, const char* tail_name, int tail_prot,
                            std::string* error_msg) {
   DCHECK_GE(new_end, Begin());
   DCHECK_LE(new_end, End());
-  DCHECK_LE(begin_ + size_, reinterpret_cast<byte*>(base_begin_) + base_size_);
+  DCHECK_LE(begin_ + size_, reinterpret_cast<uint8_t*>(base_begin_) + base_size_);
   DCHECK(IsAligned<kPageSize>(begin_));
   DCHECK(IsAligned<kPageSize>(base_begin_));
-  DCHECK(IsAligned<kPageSize>(reinterpret_cast<byte*>(base_begin_) + base_size_));
+  DCHECK(IsAligned<kPageSize>(reinterpret_cast<uint8_t*>(base_begin_) + base_size_));
   DCHECK(IsAligned<kPageSize>(new_end));
-  byte* old_end = begin_ + size_;
-  byte* old_base_end = reinterpret_cast<byte*>(base_begin_) + base_size_;
-  byte* new_base_end = new_end;
+  uint8_t* old_end = begin_ + size_;
+  uint8_t* old_base_end = reinterpret_cast<uint8_t*>(base_begin_) + base_size_;
+  uint8_t* new_base_end = new_end;
   DCHECK_LE(new_base_end, old_base_end);
   if (new_base_end == old_base_end) {
     return new MemMap(tail_name, nullptr, 0, nullptr, 0, tail_prot, false);
   }
-  size_ = new_end - reinterpret_cast<byte*>(begin_);
-  base_size_ = new_base_end - reinterpret_cast<byte*>(base_begin_);
-  DCHECK_LE(begin_ + size_, reinterpret_cast<byte*>(base_begin_) + base_size_);
+  size_ = new_end - reinterpret_cast<uint8_t*>(begin_);
+  base_size_ = new_base_end - reinterpret_cast<uint8_t*>(base_begin_);
+  DCHECK_LE(begin_ + size_, reinterpret_cast<uint8_t*>(base_begin_) + base_size_);
   size_t tail_size = old_end - new_end;
-  byte* tail_base_begin = new_base_end;
+  uint8_t* tail_base_begin = new_base_end;
   size_t tail_base_size = old_base_end - new_base_end;
   DCHECK_EQ(tail_base_begin + tail_base_size, old_base_end);
   DCHECK(IsAligned<kPageSize>(tail_base_size));
@@ -549,7 +555,7 @@
   // calls. Otherwise, libc (or something else) might take this memory
   // region. Note this isn't perfect as there's no way to prevent
   // other threads to try to take this memory region here.
-  byte* actual = reinterpret_cast<byte*>(mmap(tail_base_begin, tail_base_size, tail_prot,
+  uint8_t* actual = reinterpret_cast<uint8_t*>(mmap(tail_base_begin, tail_base_size, tail_prot,
                                               flags, fd.get(), 0));
   if (actual == MAP_FAILED) {
     std::string maps;
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index ad62f83..9b003aa 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -60,7 +60,7 @@
   // a name.
   //
   // On success, returns returns a MemMap instance.  On failure, returns a NULL;
-  static MemMap* MapAnonymous(const char* ashmem_name, byte* addr, size_t byte_count, int prot,
+  static MemMap* MapAnonymous(const char* ashmem_name, uint8_t* addr, size_t byte_count, int prot,
                               bool low_4gb, std::string* error_msg);
 
   // Map part of a file, taking care of non-page aligned offsets.  The
@@ -80,7 +80,7 @@
   //
   // On success, returns returns a MemMap instance.  On failure, returns a
   // nullptr;
-  static MemMap* MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd,
+  static MemMap* MapFileAtAddress(uint8_t* addr, size_t byte_count, int prot, int flags, int fd,
                                   off_t start, bool reuse, const char* filename,
                                   std::string* error_msg);
 
@@ -99,7 +99,7 @@
     return prot_;
   }
 
-  byte* Begin() const {
+  uint8_t* Begin() const {
     return begin_;
   }
 
@@ -107,7 +107,7 @@
     return size_;
   }
 
-  byte* End() const {
+  uint8_t* End() const {
     return Begin() + Size();
   }
 
@@ -120,7 +120,7 @@
   }
 
   void* BaseEnd() const {
-    return reinterpret_cast<byte*>(BaseBegin()) + BaseSize();
+    return reinterpret_cast<uint8_t*>(BaseBegin()) + BaseSize();
   }
 
   bool HasAddress(const void* addr) const {
@@ -128,7 +128,7 @@
   }
 
   // Unmap the pages at end and remap them to create another memory map.
-  MemMap* RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot,
+  MemMap* RemapAtEnd(uint8_t* new_end, const char* tail_name, int tail_prot,
                      std::string* error_msg);
 
   static bool CheckNoGaps(MemMap* begin_map, MemMap* end_map)
@@ -142,7 +142,7 @@
   static void Shutdown() LOCKS_EXCLUDED(Locks::mem_maps_lock_);
 
  private:
-  MemMap(const std::string& name, byte* begin, size_t size, void* base_begin, size_t base_size,
+  MemMap(const std::string& name, uint8_t* begin, size_t size, void* base_begin, size_t base_size,
          int prot, bool reuse) LOCKS_EXCLUDED(Locks::mem_maps_lock_);
 
   static void DumpMapsLocked(std::ostream& os)
@@ -153,7 +153,7 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mem_maps_lock_);
 
   const std::string name_;
-  byte* const begin_;  // Start of data.
+  uint8_t* const begin_;  // Start of data.
   size_t size_;  // Length of data.
 
   void* const base_begin_;  // Page-aligned base address.
@@ -175,6 +175,7 @@
   friend class MemMapTest;  // To allow access to base_begin_ and base_size_.
 };
 std::ostream& operator<<(std::ostream& os, const MemMap& mem_map);
+std::ostream& operator<<(std::ostream& os, const MemMap::Maps& mem_maps);
 
 }  // namespace art
 
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index 5ec3335..14a72b9 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -18,14 +18,16 @@
 
 #include <memory>
 
+#include <valgrind.h>
+
 #include "gtest/gtest.h"
 
 namespace art {
 
 class MemMapTest : public testing::Test {
  public:
-  static byte* BaseBegin(MemMap* mem_map) {
-    return reinterpret_cast<byte*>(mem_map->base_begin_);
+  static uint8_t* BaseBegin(MemMap* mem_map) {
+    return reinterpret_cast<uint8_t*>(mem_map->base_begin_);
   }
   static size_t BaseSize(MemMap* mem_map) {
     return mem_map->base_size_;
@@ -43,7 +45,7 @@
                                       low_4gb,
                                       &error_msg);
     // Check its state and write to it.
-    byte* base0 = m0->Begin();
+    uint8_t* base0 = m0->Begin();
     ASSERT_TRUE(base0 != nullptr) << error_msg;
     size_t size0 = m0->Size();
     EXPECT_EQ(m0->Size(), 2 * page_size);
@@ -60,7 +62,7 @@
     EXPECT_EQ(m0->Size(), page_size);
     EXPECT_EQ(BaseBegin(m0), base0);
     EXPECT_EQ(BaseSize(m0), page_size);
-    byte* base1 = m1->Begin();
+    uint8_t* base1 = m1->Begin();
     size_t size1 = m1->Size();
     EXPECT_EQ(base1, base0 + page_size);
     EXPECT_EQ(size1, page_size);
@@ -165,7 +167,7 @@
   std::string error_msg;
   // Map at an address that should work, which should succeed.
   std::unique_ptr<MemMap> map0(MemMap::MapAnonymous("MapAnonymous0",
-                                              reinterpret_cast<byte*>(ART_BASE_ADDRESS),
+                                              reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS),
                                               kPageSize,
                                               PROT_READ | PROT_WRITE,
                                               false,
@@ -185,7 +187,7 @@
   ASSERT_TRUE(map1->BaseBegin() != nullptr);
   // Attempt to map at the same address, which should fail.
   std::unique_ptr<MemMap> map2(MemMap::MapAnonymous("MapAnonymous2",
-                                              reinterpret_cast<byte*>(map1->BaseBegin()),
+                                              reinterpret_cast<uint8_t*>(map1->BaseBegin()),
                                               kPageSize,
                                               PROT_READ | PROT_WRITE,
                                               false,
@@ -205,18 +207,21 @@
 #endif
 
 TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) {
-  uintptr_t start_addr = ART_BASE_ADDRESS + 0x1000000;
-  std::string error_msg;
   CommonInit();
-  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<uint8_t*>(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) {
@@ -225,7 +230,7 @@
   uintptr_t ptr = 0;
   ptr -= kPageSize;  // Now it's close to the top.
   std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousOverflow",
-                                             reinterpret_cast<byte*>(ptr),
+                                             reinterpret_cast<uint8_t*>(ptr),
                                              2 * kPageSize,  // brings it over the top.
                                              PROT_READ | PROT_WRITE,
                                              false,
@@ -239,7 +244,7 @@
   CommonInit();
   std::string error_msg;
   std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousLow4GBExpectedTooHigh",
-                                             reinterpret_cast<byte*>(UINT64_C(0x100000000)),
+                                             reinterpret_cast<uint8_t*>(UINT64_C(0x100000000)),
                                              kPageSize,
                                              PROT_READ | PROT_WRITE,
                                              true,
@@ -252,7 +257,7 @@
   CommonInit();
   std::string error_msg;
   std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousLow4GBRangeTooHigh",
-                                             reinterpret_cast<byte*>(0xF0000000),
+                                             reinterpret_cast<uint8_t*>(0xF0000000),
                                              0x20000000,
                                              PROT_READ | PROT_WRITE,
                                              true,
@@ -276,7 +281,7 @@
   ASSERT_TRUE(map.get() != nullptr) << error_msg;
   ASSERT_TRUE(error_msg.empty());
   // Record the base address.
-  byte* map_base = reinterpret_cast<byte*>(map->BaseBegin());
+  uint8_t* map_base = reinterpret_cast<uint8_t*>(map->BaseBegin());
   // Unmap it.
   map.reset();
 
diff --git a/runtime/memory_region.h b/runtime/memory_region.h
index bab2e86..b3820be 100644
--- a/runtime/memory_region.h
+++ b/runtime/memory_region.h
@@ -21,6 +21,7 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/value_object.h"
 #include "globals.h"
 
 namespace art {
@@ -28,10 +29,10 @@
 // Memory regions are useful for accessing memory with bounds check in
 // debug mode. They can be safely passed by value and do not assume ownership
 // of the region.
-class MemoryRegion {
+class MemoryRegion FINAL : public ValueObject {
  public:
-  MemoryRegion() : pointer_(NULL), size_(0) {}
-  MemoryRegion(void* pointer, uword size) : pointer_(pointer), size_(size) {}
+  MemoryRegion() : pointer_(nullptr), size_(0) {}
+  MemoryRegion(void* pointer_in, uintptr_t size_in) : pointer_(pointer_in), size_(size_in) {}
 
   void* pointer() const { return pointer_; }
   size_t size() const { return size_; }
@@ -41,8 +42,8 @@
     return OFFSETOF_MEMBER(MemoryRegion, pointer_);
   }
 
-  byte* start() const { return reinterpret_cast<byte*>(pointer_); }
-  byte* end() const { return start() + size_; }
+  uint8_t* start() const { return reinterpret_cast<uint8_t*>(pointer_); }
+  uint8_t* end() const { return start() + size_; }
 
   template<typename T> T Load(uintptr_t offset) const {
     return *ComputeInternalPointer<T>(offset);
@@ -77,10 +78,10 @@
   void CopyFrom(size_t offset, const MemoryRegion& from) const;
 
   // Compute a sub memory region based on an existing one.
-  MemoryRegion Subregion(uintptr_t offset, uintptr_t size) const {
-    CHECK_GE(this->size(), size);
-    CHECK_LE(offset,  this->size() - size);
-    return MemoryRegion(reinterpret_cast<void*>(start() + offset), size);
+  MemoryRegion Subregion(uintptr_t offset, uintptr_t size_in) const {
+    CHECK_GE(this->size(), size_in);
+    CHECK_LE(offset,  this->size() - size_in);
+    return MemoryRegion(reinterpret_cast<void*>(start() + offset), size_in);
   }
 
   // Compute an extended memory region based on an existing one.
@@ -98,11 +99,11 @@
 
   // Locate the bit with the given offset. Returns a pointer to the byte
   // containing the bit, and sets bit_mask to the bit within that byte.
-  byte* ComputeBitPointer(uintptr_t bit_offset, byte* bit_mask) const {
+  uint8_t* ComputeBitPointer(uintptr_t bit_offset, uint8_t* bit_mask) const {
     uintptr_t bit_remainder = (bit_offset & (kBitsPerByte - 1));
     *bit_mask = (1U << bit_remainder);
     uintptr_t byte_offset = (bit_offset >> kBitsPerByteLog2);
-    return ComputeInternalPointer<byte>(byte_offset);
+    return ComputeInternalPointer<uint8_t>(byte_offset);
   }
 
   void* pointer_;
diff --git a/runtime/method_helper-inl.h b/runtime/method_helper-inl.h
index 9af835f..7a7949e 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,27 +57,6 @@
   return type;
 }
 
-inline mirror::Class* MethodHelper::GetReturnType(bool resolve) {
-  mirror::ArtMethod* method = GetMethod();
-  const DexFile* dex_file = method->GetDexFile();
-  const DexFile::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex());
-  const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
-  uint16_t return_type_idx = proto_id.return_type_idx_;
-  return GetClassFromTypeIdx(return_type_idx, resolve);
-}
-
-inline mirror::String* MethodHelper::ResolveString(uint32_t string_idx) {
-  mirror::ArtMethod* method = GetMethod();
-  mirror::String* s = method->GetDexCacheStrings()->Get(string_idx);
-  if (UNLIKELY(s == nullptr)) {
-    StackHandleScope<1> hs(Thread::Current());
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
-    s = Runtime::Current()->GetClassLinker()->ResolveString(*method->GetDexFile(), string_idx,
-                                                            dex_cache);
-  }
-  return s;
-}
-
 }  // namespace art
 
 #endif  // ART_RUNTIME_METHOD_HELPER_INL_H_
diff --git a/runtime/method_helper.cc b/runtime/method_helper.cc
index d6f83a8..81e1794 100644
--- a/runtime/method_helper.cc
+++ b/runtime/method_helper.cc
@@ -25,20 +25,16 @@
 
 namespace art {
 
-mirror::String* MethodHelper::GetNameAsString(Thread* self) {
-  const DexFile* dex_file = method_->GetDexFile();
-  mirror::ArtMethod* method = method_->GetInterfaceMethodIfProxy();
-  uint32_t dex_method_idx = method->GetDexMethodIndex();
-  const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx);
-  StackHandleScope<1> hs(self);
-  Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
-  return Runtime::Current()->GetClassLinker()->ResolveString(*dex_file, method_id.name_idx_,
-                                                             dex_cache);
-}
-
-bool MethodHelper::HasSameSignatureWithDifferentClassLoaders(MethodHelper* other) {
-  if (UNLIKELY(GetReturnType() != other->GetReturnType())) {
-    return false;
+template <template <class T> class HandleKind>
+template <template <class T2> class HandleKind2>
+bool MethodHelperT<HandleKind>::HasSameSignatureWithDifferentClassLoaders(Thread* self,
+    MethodHelperT<HandleKind2>* other) {
+  {
+    StackHandleScope<1> hs(self);
+    Handle<mirror::Class> return_type(hs.NewHandle(GetMethod()->GetReturnType()));
+    if (UNLIKELY(other->GetMethod()->GetReturnType() != return_type.Get())) {
+      return false;
+    }
   }
   const DexFile::TypeList* types = method_->GetParameterTypeList();
   const DexFile::TypeList* other_types = other->method_->GetParameterTypeList();
@@ -62,7 +58,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 +99,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 +131,34 @@
   return DexFile::kDexNoIndex;
 }
 
+// Instantiate methods.
+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>(Thread* self,
+    MethodHelperT<Handle>* other);
+
+template
+bool MethodHelperT<Handle>::HasSameSignatureWithDifferentClassLoaders<MutableHandle>(Thread* self,
+    MethodHelperT<MutableHandle>* other);
+
+template
+bool MethodHelperT<MutableHandle>::HasSameSignatureWithDifferentClassLoaders<Handle>(Thread* self,
+    MethodHelperT<Handle>* other);
+
+template
+bool MethodHelperT<MutableHandle>::HasSameSignatureWithDifferentClassLoaders<MutableHandle>(
+    Thread* self, MethodHelperT<MutableHandle>* other);
+
 }  // namespace art
diff --git a/runtime/method_helper.h b/runtime/method_helper.h
index f71d273..dc305d5 100644
--- a/runtime/method_helper.h
+++ b/runtime/method_helper.h
@@ -24,24 +24,21 @@
 
 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();
   }
 
-  mirror::String* GetNameAsString(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // GetMethod() != Get() for proxy methods.
+  mirror::ArtMethod* Get() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return method_.Get();
+  }
 
   const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     const char* result = shorty_;
@@ -73,10 +70,6 @@
     return refs;
   }
 
-  // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
-  // number of bugs at call sites.
-  mirror::Class* GetReturnType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   size_t NumArgs() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // "1 +" because the first in Args is the receiver.
     // "- 1" because we don't count the return type.
@@ -105,17 +98,17 @@
     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(Thread* self, MethodHelperT<HandleKind2>* other)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   mirror::Class* GetClassFromTypeIdx(uint16_t type_idx, bool resolve = true)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  mirror::String* ResolveString(uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   uint32_t FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -125,6 +118,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 +152,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..13f881d 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -29,22 +29,30 @@
 
 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;
 }
 
+inline MemberOffset Array::DataOffset(size_t component_size) {
+  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);
+}
+
 template<VerifyObjectFlags kVerifyFlags>
 inline bool Array::CheckIsValidIndex(int32_t index) {
   if (UNLIKELY(static_cast<uint32_t>(index) >=
@@ -56,24 +64,37 @@
 }
 
 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);
+  UNUSED(self);
+#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 +124,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 +135,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));
+    uint8_t* old_end = reinterpret_cast<uint8_t*>(array->GetRawData(1U << component_size_shift_,
+                                                                    minimum_length_));
+    uint8_t* new_end = reinterpret_cast<uint8_t*>(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 +149,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 +206,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..b92f017 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());
@@ -57,8 +58,8 @@
   if (current_dimension + 1 < dimensions->GetLength()) {
     // Create a new sub-array in every element of the array.
     for (int32_t i = 0; i < array_length; i++) {
-      StackHandleScope<1> hs(self);
-      Handle<mirror::Class> h_component_type(hs.NewHandle(array_class->GetComponentType()));
+      StackHandleScope<1> hs2(self);
+      Handle<mirror::Class> h_component_type(hs2.NewHandle(array_class->GetComponentType()));
       Array* sub_array = RecursiveCreateMultiArray(self, h_component_type,
                                                    current_dimension + 1, dimensions);
       if (UNLIKELY(sub_array == nullptr)) {
@@ -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 75d8d91..83e3688 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,
@@ -65,14 +64,7 @@
     return OFFSET_OF_OBJECT_MEMBER(Array, length_);
   }
 
-  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);
-    }
-  }
+  static MemberOffset DataOffset(size_t component_size);
 
   void* GetRawData(size_t component_size, int32_t index)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
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 b02f456..494fa2f 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -24,7 +24,6 @@
 #include "class_linker.h"
 #include "dex_cache.h"
 #include "dex_file.h"
-#include "entrypoints/entrypoint_utils.h"
 #include "method_helper.h"
 #include "object-inl.h"
 #include "object_array.h"
@@ -38,7 +37,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>
@@ -176,81 +175,29 @@
     }
     default:
       LOG(FATAL) << "Unreachable - invocation type: " << type;
-      return true;
+      UNREACHABLE();
   }
 }
 
-inline void ArtMethod::AssertPcIsWithinQuickCode(uintptr_t pc) {
-  if (!kIsDebugBuild) {
-    return;
-  }
-  if (IsNative() || IsRuntimeMethod() || IsProxyMethod()) {
-    return;
-  }
-  if (pc == GetQuickInstrumentationExitPc()) {
-    return;
-  }
-  const void* code = GetEntryPointFromQuickCompiledCode();
-  if (code == GetQuickToInterpreterBridge() || code == GetQuickInstrumentationEntryPoint()) {
-    return;
-  }
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  if (code == class_linker->GetQuickResolutionTrampoline() ||
-      code == class_linker->GetQuickToInterpreterBridgeTrampoline()) {
-    return;
-  }
-  DCHECK(IsWithinQuickCode(pc))
-      << PrettyMethod(this)
-      << " pc=" << std::hex << pc
-      << " code=" << code
-      << " size=" << GetCodeSize();
-}
-
 inline uint32_t ArtMethod::GetQuickOatCodeOffset() {
   DCHECK(!Runtime::Current()->IsStarted());
   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()) {
-    return nullptr;
-  }
-  Runtime* runtime = Runtime::Current();
-  const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this);
-  // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
-  // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
-  // for non-native methods.
-  DCHECK(entry_point != runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline());
-  if (UNLIKELY(entry_point == GetQuickToInterpreterBridge()) ||
-      UNLIKELY(entry_point == runtime->GetClassLinker()->GetQuickGenericJniTrampoline())) {
-    return nullptr;
-  }
-  return entry_point;
-}
-
-inline const void* ArtMethod::GetQuickOatCodePointer() {
-  return EntryPointToCodePointer(GetQuickOatEntryPoint());
-}
 
 inline const uint8_t* ArtMethod::GetMappingTable() {
   const void* code_pointer = GetQuickOatCodePointer();
@@ -280,6 +227,7 @@
 }
 
 inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer) {
+  CHECK(!IsOptimized()) << "Unimplemented vmap table for optimized compiler";
   DCHECK(code_pointer != nullptr);
   DCHECK(code_pointer == GetQuickOatCodePointer());
   uint32_t offset =
@@ -290,6 +238,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));
@@ -333,7 +295,6 @@
   return result;
 }
 
-
 inline bool ArtMethod::IsImtUnimplementedMethod() {
   bool result = this == Runtime::Current()->GetImtUnimplementedMethod();
   // Check that if we do think it is phony it looks like the imt unimplemented method.
@@ -341,69 +302,17 @@
   return result;
 }
 
-inline uintptr_t ArtMethod::NativePcOffset(const uintptr_t pc) {
+inline uintptr_t ArtMethod::NativeQuickPcOffset(const uintptr_t pc) {
   const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this);
   return pc - reinterpret_cast<uintptr_t>(code);
 }
 
-inline uintptr_t ArtMethod::NativePcOffset(const uintptr_t pc, const void* quick_entry_point) {
-  DCHECK(quick_entry_point != GetQuickToInterpreterBridge());
-  DCHECK(quick_entry_point == Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this));
-  return pc - reinterpret_cast<uintptr_t>(quick_entry_point);
-}
-
 template<VerifyObjectFlags kVerifyFlags>
 inline void ArtMethod::SetNativeMethod(const void* native_method) {
   SetFieldPtr<false, true, kVerifyFlags>(
       OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_), native_method);
 }
 
-inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() {
-  if (UNLIKELY(IsPortableCompiled())) {
-    // Portable compiled dex bytecode or jni stub.
-    return QuickMethodFrameInfo(kStackAlignment, 0u, 0u);
-  }
-  Runtime* runtime = Runtime::Current();
-  // For Proxy method we exclude direct method (there is only one direct method - constructor).
-  // Direct method is cloned from original java.lang.reflect.Proxy class together with code
-  // and as a result it is executed as usual quick compiled method without any stubs.
-  // So the frame info should be returned as it is a quick method not a stub.
-  if (UNLIKELY(IsAbstract()) || UNLIKELY(IsProxyMethod() && !IsDirect())) {
-    return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
-  }
-  if (UNLIKELY(IsRuntimeMethod())) {
-    return runtime->GetRuntimeMethodFrameInfo(this);
-  }
-
-  const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this);
-  // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
-  // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
-  // for non-native methods. And we really shouldn't see a failure for non-native methods here.
-  DCHECK(entry_point != runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline());
-  CHECK(entry_point != GetQuickToInterpreterBridge());
-
-  if (UNLIKELY(entry_point == runtime->GetClassLinker()->GetQuickGenericJniTrampoline())) {
-    // Generic JNI frame.
-    DCHECK(IsNative());
-    StackHandleScope<1> hs(Thread::Current());
-    uint32_t handle_refs =
-        MethodHelper(hs.NewHandle(this)).GetNumberOfReferenceArgsWithoutReceiver() + 1;
-    size_t scope_size = HandleScope::SizeOf(handle_refs);
-    QuickMethodFrameInfo callee_info = runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
-
-    // Callee saves + handle scope + method ref + alignment
-    size_t frame_size = RoundUp(callee_info.FrameSizeInBytes() + scope_size
-                                - kPointerSize  // callee-save frame stores a whole method pointer
-                                + sizeof(StackReference<mirror::ArtMethod>),
-                                kStackAlignment);
-
-    return QuickMethodFrameInfo(frame_size, callee_info.CoreSpillMask(), callee_info.FpSpillMask());
-  }
-
-  const void* code_pointer = EntryPointToCodePointer(entry_point);
-  return GetQuickFrameInfo(code_pointer);
-}
-
 inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo(const void* code_pointer) {
   DCHECK(code_pointer != nullptr);
   DCHECK_EQ(code_pointer, GetQuickOatCodePointer());
@@ -562,6 +471,20 @@
                         new_dex_cache_classes);
 }
 
+inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
+  DCHECK(!IsProxyMethod());
+  const DexFile* dex_file = GetDexFile();
+  const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
+  const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
+  uint16_t return_type_idx = proto_id.return_type_idx_;
+  mirror::Class* type = GetDexCacheResolvedType(return_type_idx);
+  if (type == nullptr && resolve) {
+    type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this);
+    CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
+  }
+  return type;
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 5b833b9..a742aaa 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -23,6 +23,8 @@
 #include "class-inl.h"
 #include "dex_file-inl.h"
 #include "dex_instruction.h"
+#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc/accounting/card_table-inl.h"
 #include "interpreter/interpreter.h"
 #include "jni_internal.h"
@@ -41,7 +43,7 @@
 extern "C" void art_portable_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, char);
 extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
                                       const char*);
-#ifdef __LP64__
+#if defined(__LP64__) || defined(__arm__)
 extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
                                              const char*);
 #endif
@@ -65,6 +67,17 @@
   }
 }
 
+mirror::String* ArtMethod::GetNameAsString(Thread* self) {
+  mirror::ArtMethod* method = GetInterfaceMethodIfProxy();
+  const DexFile* dex_file = method->GetDexFile();
+  uint32_t dex_method_idx = method->GetDexMethodIndex();
+  const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx);
+  StackHandleScope<1> hs(self);
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
+  return Runtime::Current()->GetClassLinker()->ResolveString(*dex_file, method_id.name_idx_,
+                                                             dex_cache);
+}
+
 InvokeType ArtMethod::GetInvokeType() {
   // TODO: kSuper?
   if (GetDeclaringClass()->IsInterface()) {
@@ -90,9 +103,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;
@@ -124,7 +137,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);
@@ -184,7 +197,7 @@
   return DexFile::kDexNoIndex;
 }
 
-uintptr_t ArtMethod::ToNativePc(const uint32_t dex_pc) {
+uintptr_t ArtMethod::ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure) {
   const void* entry_point = GetQuickOatEntryPoint();
   MappingTable table(
       entry_point != nullptr ? GetMappingTable(EntryPointToCodePointer(entry_point)) : nullptr);
@@ -206,9 +219,11 @@
       return reinterpret_cast<uintptr_t>(entry_point) + cur.NativePcOffset();
     }
   }
-  LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc
-             << " in " << PrettyMethod(this);
-  return 0;
+  if (abort_on_failure) {
+    LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc
+               << " in " << PrettyMethod(this);
+  }
+  return UINTPTR_MAX;
 }
 
 uint32_t ArtMethod::FindCatchBlock(Handle<ArtMethod> h_this, Handle<Class> exception_type,
@@ -262,6 +277,74 @@
   return found_dex_pc;
 }
 
+void ArtMethod::AssertPcIsWithinQuickCode(uintptr_t pc) {
+  if (IsNative() || IsRuntimeMethod() || IsProxyMethod()) {
+    return;
+  }
+  if (pc == reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc())) {
+    return;
+  }
+  const void* code = GetEntryPointFromQuickCompiledCode();
+  if (code == GetQuickInstrumentationEntryPoint()) {
+    return;
+  }
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  if (class_linker->IsQuickToInterpreterBridge(code) ||
+      class_linker->IsQuickResolutionStub(code)) {
+    return;
+  }
+  /*
+   * During a stack walk, a return PC may point past-the-end of the code
+   * in the case that the last instruction is a call that isn't expected to
+   * return.  Thus, we check <= code + GetCodeSize().
+   *
+   * NOTE: For Thumb both pc and code are offset by 1 indicating the Thumb state.
+   */
+  CHECK(PcIsWithinQuickCode(pc))
+      << PrettyMethod(this)
+      << " pc=" << std::hex << pc
+      << " code=" << code
+      << " size=" << GetCodeSize();
+}
+
+bool ArtMethod::IsEntrypointInterpreter() {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  if (!IsPortableCompiled()) {  // Quick.
+    const void* oat_quick_code = class_linker->GetOatMethodQuickCodeFor(this);
+    return oat_quick_code == nullptr ||
+        oat_quick_code != GetEntryPointFromQuickCompiledCode();
+  } else {  // Portable.
+    const void* oat_portable_code = class_linker->GetOatMethodPortableCodeFor(this);
+    return oat_portable_code == nullptr ||
+        oat_portable_code != GetEntryPointFromPortableCompiledCode();
+  }
+}
+
+const void* ArtMethod::GetQuickOatEntryPoint() {
+  if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
+    return nullptr;
+  }
+  Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  const void* code = runtime->GetInstrumentation()->GetQuickCodeFor(this);
+  // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
+  // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
+  // for non-native methods.
+  if (class_linker->IsQuickToInterpreterBridge(code) ||
+      class_linker->IsQuickGenericJniStub(code)) {
+    return nullptr;
+  }
+  return code;
+}
+
+#ifndef NDEBUG
+uintptr_t ArtMethod::NativeQuickPcOffset(const uintptr_t pc, const void* quick_entry_point) {
+  CHECK_NE(quick_entry_point, GetQuickToInterpreterBridge());
+  CHECK_EQ(quick_entry_point, Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this));
+  return pc - reinterpret_cast<uintptr_t>(quick_entry_point);
+}
+#endif
+
 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())) {
@@ -291,24 +374,23 @@
   } 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 defined(__LP64__) || defined(__arm__)
         if (!IsStatic()) {
           (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
         } else {
@@ -326,7 +408,7 @@
         // stack. Continue execution in the interpreter.
         self->ClearException();
         ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result);
-        self->SetTopOfStack(nullptr, 0);
+        self->SetTopOfStack(nullptr);
         self->SetTopOfShadowStack(shadow_frame);
         interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);
       }
@@ -334,11 +416,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";
@@ -352,8 +430,53 @@
   self->PopManagedStackFragment(fragment);
 }
 
-void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) {
-  DCHECK(Thread::Current() == self);
+QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() {
+  if (UNLIKELY(IsPortableCompiled())) {
+    // Portable compiled dex bytecode or jni stub.
+    return QuickMethodFrameInfo(kStackAlignment, 0u, 0u);
+  }
+  Runtime* runtime = Runtime::Current();
+  // For Proxy method we exclude direct method (there is only one direct method - constructor).
+  // Direct method is cloned from original java.lang.reflect.Proxy class together with code
+  // and as a result it is executed as usual quick compiled method without any stubs.
+  // So the frame info should be returned as it is a quick method not a stub.
+  if (UNLIKELY(IsAbstract()) || UNLIKELY(IsProxyMethod() && !IsDirect())) {
+    return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
+  }
+  if (UNLIKELY(IsRuntimeMethod())) {
+    return runtime->GetRuntimeMethodFrameInfo(this);
+  }
+
+  const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this);
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
+  // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
+  // for non-native methods. And we really shouldn't see a failure for non-native methods here.
+  DCHECK(!class_linker->IsQuickToInterpreterBridge(entry_point));
+
+  if (class_linker->IsQuickGenericJniStub(entry_point)) {
+    // Generic JNI frame.
+    DCHECK(IsNative());
+    StackHandleScope<1> hs(Thread::Current());
+    uint32_t handle_refs =
+        MethodHelper(hs.NewHandle(this)).GetNumberOfReferenceArgsWithoutReceiver() + 1;
+    size_t scope_size = HandleScope::SizeOf(handle_refs);
+    QuickMethodFrameInfo callee_info = runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
+
+    // Callee saves + handle scope + method ref + alignment
+    size_t frame_size = RoundUp(callee_info.FrameSizeInBytes() + scope_size
+                                - sizeof(void*)  // callee-save frame stores a whole method pointer
+                                + sizeof(StackReference<mirror::ArtMethod>),
+                                kStackAlignment);
+
+    return QuickMethodFrameInfo(frame_size, callee_info.CoreSpillMask(), callee_info.FpSpillMask());
+  }
+
+  const void* code_pointer = EntryPointToCodePointer(entry_point);
+  return GetQuickFrameInfo(code_pointer);
+}
+
+void ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
   CHECK(IsNative()) << PrettyMethod(this);
   CHECK(!IsFastNative()) << PrettyMethod(this);
   CHECK(native_method != NULL) << PrettyMethod(this);
@@ -363,10 +486,10 @@
   SetNativeMethod(native_method);
 }
 
-void ArtMethod::UnregisterNative(Thread* self) {
+void ArtMethod::UnregisterNative() {
   CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this);
   // restore stub to lookup native pointer via dlsym
-  RegisterNative(self, GetJniDlsymLookupStub(), false);
+  RegisterNative(GetJniDlsymLookupStub(), false);
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 36bb9e0..d92d00a 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 {
 
@@ -38,7 +39,7 @@
 
 namespace mirror {
 
-typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper& mh,
+typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper* mh,
     const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result);
 
 // C++ mirror of java.lang.reflect.ArtMethod.
@@ -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_) {
@@ -260,7 +270,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_));
   }
@@ -277,7 +286,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_));
@@ -297,7 +305,10 @@
 
   uint32_t GetCodeSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Check whether the given PC is within the quick compiled code associated with this method's
+  // quick entrypoint. This code isn't robust for instrumentation, etc. and is only used for
+  // debug purposes.
+  bool PcIsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     uintptr_t code = reinterpret_cast<uintptr_t>(GetEntryPointFromQuickCompiledCode());
     if (code == 0) {
       return pc == 0;
@@ -314,23 +325,31 @@
 
   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
-  uint32_t GetQuickOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SetQuickOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // 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_);
 
-  static const void* EntryPointToCodePointer(const void* entry_point) ALWAYS_INLINE {
+  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_);
+
+  ALWAYS_INLINE static const void* EntryPointToCodePointer(const void* entry_point) {
     uintptr_t code = reinterpret_cast<uintptr_t>(entry_point);
-    code &= ~0x1;  // TODO: Make this Thumb2 specific.
+    // TODO: Make this Thumb2 specific. It is benign on other architectures as code is always at
+    //       least 2 byte aligned.
+    code &= ~0x1;
     return reinterpret_cast<const void*>(code);
   }
 
-  // Actual entry point pointer to compiled oat code or nullptr.
+  // Actual entry point pointer to compiled oat code or nullptr if method has no compiled code.
   const void* GetQuickOatEntryPoint() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Actual pointer to compiled oat code or nullptr.
-  const void* GetQuickOatCodePointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const void* GetQuickOatCodePointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return EntryPointToCodePointer(GetQuickOatEntryPoint());
+  }
 
   // Callers should wrap the uint8_t* in a MappingTable instance for convenient access.
   const uint8_t* GetMappingTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -342,6 +361,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_));
   }
@@ -367,24 +389,25 @@
   QuickMethodFrameInfo GetQuickFrameInfo(const void* code_pointer)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  size_t GetReturnPcOffsetInBytes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetReturnPcOffsetInBytes(GetFrameSizeInBytes());
+  FrameOffset GetReturnPcOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetReturnPcOffset(GetFrameSizeInBytes());
   }
 
-  size_t GetReturnPcOffsetInBytes(uint32_t frame_size_in_bytes)
+  FrameOffset GetReturnPcOffset(uint32_t frame_size_in_bytes)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_EQ(frame_size_in_bytes, GetFrameSizeInBytes());
-    return frame_size_in_bytes - kPointerSize;
+    return FrameOffset(frame_size_in_bytes - sizeof(void*));
   }
 
-  size_t GetHandleScopeOffsetInBytes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return kPointerSize;
+  FrameOffset GetHandleScopeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK_LT(sizeof(void*), GetFrameSizeInBytes());
+    return FrameOffset(sizeof(void*));
   }
 
-  void RegisterNative(Thread* self, const void* native_method, bool is_fast)
+  void RegisterNative(const void* native_method, bool is_fast)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void UnregisterNative(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void UnregisterNative() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static MemberOffset NativeMethodOffset() {
     return OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_);
@@ -414,16 +437,24 @@
 
   bool IsImtUnimplementedMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  uintptr_t NativePcOffset(const uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  uintptr_t NativePcOffset(const uintptr_t pc, const void* quick_entry_point)
+  uintptr_t NativeQuickPcOffset(const uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+#ifdef NDEBUG
+  uintptr_t NativeQuickPcOffset(const uintptr_t pc, const void* quick_entry_point)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return pc - reinterpret_cast<uintptr_t>(quick_entry_point);
+  }
+#else
+  uintptr_t NativeQuickPcOffset(const uintptr_t pc, const void* quick_entry_point)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+#endif
 
   // Converts a native PC to a dex PC.
   uint32_t ToDexPc(const uintptr_t pc, bool abort_on_failure = true)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Converts a dex PC to a native PC.
-  uintptr_t ToNativePc(const uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  uintptr_t ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure = true)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Find the catch block for the given exception type and dex_pc. When a catch block is found,
   // indicates whether the found catch block is responsible for clearing the exception or whether
@@ -457,6 +488,8 @@
 
   ALWAYS_INLINE const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  mirror::String* GetNameAsString(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   const DexFile::CodeItem* GetCodeItem() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool IsResolvedTypeIdx(uint16_t type_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -478,13 +511,17 @@
   const char* GetTypeDescriptorFromTypeIdx(uint16_t type_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
+  // number of bugs at call sites.
+  mirror::Class* GetReturnType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   mirror::ClassLoader* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
- protected:
+ private:
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
   // The class we are a part of.
   HeapReference<Class> declaring_class_;
@@ -507,9 +544,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 0d109e5..5f72dbe 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -112,7 +112,8 @@
 
 template<VerifyObjectFlags kVerifyFlags>
 inline ArtMethod* Class::GetVirtualMethod(uint32_t i) {
-  DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>());
+  DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>())
+      << PrettyClass(this) << " status=" << GetStatus();
   return GetVirtualMethods()->GetWithoutChecks(i);
 }
 
@@ -278,7 +279,7 @@
 template <bool throw_on_failure, bool use_referrers_cache, InvokeType throw_invoke_type>
 inline bool Class::ResolvedMethodAccessTest(Class* access_to, ArtMethod* method,
                                             uint32_t method_idx, DexCache* dex_cache) {
-  COMPILE_ASSERT(throw_on_failure || throw_invoke_type == kStatic, non_default_throw_invoke_type);
+  static_assert(throw_on_failure || throw_invoke_type == kStatic, "Non-default throw invoke type");
   DCHECK_EQ(use_referrers_cache, dex_cache == nullptr);
   if (UNLIKELY(!this->CanAccess(access_to))) {
     // The referrer class can't access the method's declaring class but may still be able
@@ -501,8 +502,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() {
@@ -547,6 +559,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) {
@@ -560,30 +574,44 @@
             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;
 }
 
 template <bool kVisitClass, typename Visitor>
 inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) {
-  // Visit the static fields first so that we don't overwrite the SFields / IFields instance
-  // fields.
   VisitInstanceFieldsReferences<kVisitClass>(klass, visitor);
-  if (!IsTemp()) {
+  if (!IsTemp() && IsResolved()) {
     // Temp classes don't ever populate imt/vtable or static fields and they are not even
-    // allocated with the right size for those.
+    // allocated with the right size for those. Also, unresolved classes don't have fields
+    // linked yet.
     VisitStaticFieldsReferences<kVisitClass>(this, visitor);
     if (ShouldHaveEmbeddedImtAndVTable()) {
       VisitEmbeddedImtAndVTable(visitor);
@@ -696,11 +724,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 4ef6ea0..5665059 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -117,7 +117,7 @@
     self->SetException(gc_safe_throw_location, old_exception.Get());
     self->SetExceptionReportedToInstrumentation(is_exception_reported);
   }
-  COMPILE_ASSERT(sizeof(Status) == sizeof(uint32_t), size_of_status_not_uint32);
+  static_assert(sizeof(Status) == sizeof(uint32_t), "Size of status not equal to uint32");
   if (Runtime::Current()->IsActiveTransaction()) {
     SetField32Volatile<true>(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status);
   } else {
@@ -278,35 +278,25 @@
 }
 
 void Class::SetReferenceInstanceOffsets(uint32_t new_reference_offsets) {
-  if (new_reference_offsets != CLASS_WALK_SUPER) {
+  if (kIsDebugBuild && (new_reference_offsets != kClassWalkSuper)) {
     // Sanity check that the number of bits set in the reference offset bitmap
     // agrees with the number of references
-    size_t count = 0;
+    uint32_t count = 0;
     for (Class* c = this; c != nullptr; c = c->GetSuperClass()) {
       count += c->NumReferenceInstanceFieldsDuringLinking();
     }
-    CHECK_EQ((size_t)POPCOUNT(new_reference_offsets), count);
+    // +1 for the Class in Object.
+    CHECK_EQ(static_cast<uint32_t>(POPCOUNT(new_reference_offsets)) + 1, count);
   }
   // Not called within a transaction.
   SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_),
                     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 ||
@@ -638,8 +628,8 @@
     HandleWrapper<mirror::Class> h_k(hs.NewHandleWrapper(&k));
     // Is this field in any of this class' interfaces?
     for (uint32_t i = 0; i < h_k->NumDirectInterfaces(); ++i) {
-      StackHandleScope<1> hs(self);
-      Handle<mirror::Class> interface(hs.NewHandle(GetDirectInterface(self, h_k, i)));
+      StackHandleScope<1> hs2(self);
+      Handle<mirror::Class> interface(hs2.NewHandle(GetDirectInterface(self, h_k, i)));
       f = FindStaticField(self, interface, name, type);
       if (f != nullptr) {
         return f;
@@ -662,8 +652,8 @@
     HandleWrapper<mirror::Class> h_k(hs.NewHandleWrapper(&k));
     // Is this field in any of this class' interfaces?
     for (uint32_t i = 0; i < h_k->NumDirectInterfaces(); ++i) {
-      StackHandleScope<1> hs(self);
-      Handle<mirror::Class> interface(hs.NewHandle(GetDirectInterface(self, h_k, i)));
+      StackHandleScope<1> hs2(self);
+      Handle<mirror::Class> interface(hs2.NewHandle(GetDirectInterface(self, h_k, i)));
       f = FindStaticField(self, interface, dex_cache, dex_field_idx);
       if (f != nullptr) {
         return f;
@@ -690,8 +680,8 @@
     StackHandleScope<1> hs(self);
     HandleWrapper<mirror::Class> h_k(hs.NewHandleWrapper(&k));
     for (uint32_t i = 0; i < h_k->NumDirectInterfaces(); ++i) {
-      StackHandleScope<1> hs(self);
-      Handle<mirror::Class> interface(hs.NewHandle(GetDirectInterface(self, h_k, i)));
+      StackHandleScope<1> hs2(self);
+      Handle<mirror::Class> interface(hs2.NewHandle(GetDirectInterface(self, h_k, i)));
       f = interface->FindStaticField(self, interface, name, type);
       if (f != nullptr) {
         return f;
@@ -757,7 +747,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 6bb05ba..4f1af44 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -27,44 +27,13 @@
 #include "object_callbacks.h"
 #include "primitive.h"
 #include "read_barrier_option.h"
-
-/*
- * A magic value for refOffsets. Ignore the bits and walk the super
- * chain when this is the value.
- * [This is an unlikely "natural" value, since it would be 30 non-ref instance
- * fields followed by 2 ref instance fields.]
- */
-#define CLASS_WALK_SUPER 3U
-#define CLASS_BITS_PER_WORD (sizeof(uint32_t) * 8)
-#define CLASS_OFFSET_ALIGNMENT 4
-#define CLASS_HIGH_BIT (1U << (CLASS_BITS_PER_WORD - 1))
-/*
- * Given an offset, return the bit number which would encode that offset.
- * Local use only.
- */
-#define _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) \
-    ((unsigned int)(byteOffset) / \
-     CLASS_OFFSET_ALIGNMENT)
-/*
- * Is the given offset too large to be encoded?
- */
-#define CLASS_CAN_ENCODE_OFFSET(byteOffset) \
-    (_CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) < CLASS_BITS_PER_WORD)
-/*
- * Return a single bit, encoding the offset.
- * Undefined if the offset is too large, as defined above.
- */
-#define CLASS_BIT_FROM_OFFSET(byteOffset) \
-    (CLASS_HIGH_BIT >> _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset))
-/*
- * Return an offset, given a bit number as returned from CLZ.
- */
-#define CLASS_OFFSET_FROM_CLZ(rshift) \
-    MemberOffset((static_cast<int>(rshift) * CLASS_OFFSET_ALIGNMENT))
+#include "utils.h"
 
 namespace art {
 
 struct ClassOffsets;
+template<class T> class Handle;
+template<class T> class Handle;
 class Signature;
 class StringPiece;
 template<size_t kNumReferences> class PACKED(4) StackHandleScope;
@@ -80,6 +49,12 @@
 // C++ mirror of java.lang.Class
 class MANAGED Class FINAL : public Object {
  public:
+  // A magic value for reference_instance_offsets_. Ignore the bits and walk the super chain when
+  // this is the value.
+  // [This is an unlikely "natural" value, since it would be 30 non-ref instance fields followed by
+  // 2 ref instance fields.]
+  static constexpr uint32_t kClassWalkSuper = 0xC0000000;
+
   // Interface method table size. Increasing this value reduces the chance of two interface methods
   // colliding in the interface method table but increases the size of classes that implement
   // (non-marker) interfaces.
@@ -97,6 +72,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
@@ -132,7 +113,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_.
@@ -150,7 +131,7 @@
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Status GetStatus() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    COMPILE_ASSERT(sizeof(Status) == sizeof(uint32_t), size_of_status_not_uint32);
+    static_assert(sizeof(Status) == sizeof(uint32_t), "Size of status not equal to uint32");
     return static_cast<Status>(
         GetField32Volatile<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, status_)));
   }
@@ -259,6 +240,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;
@@ -328,9 +319,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_) {
@@ -440,15 +438,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>
@@ -492,6 +500,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);
@@ -500,12 +510,12 @@
   static uint32_t ClassClassSize() {
     // The number of vtable entries in java.lang.Class.
     uint32_t vtable_entries = Object::kVTableLength + 66;
-    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,
@@ -856,14 +866,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)
@@ -970,7 +972,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_);
@@ -1123,15 +1126,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/class_loader.h b/runtime/mirror/class_loader.h
index ff2ad89..b10a296 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -17,7 +17,7 @@
 #ifndef ART_RUNTIME_MIRROR_CLASS_LOADER_H_
 #define ART_RUNTIME_MIRROR_CLASS_LOADER_H_
 
-#include "mirror/object.h"
+#include "object.h"
 
 namespace art {
 
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..c451764 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>
@@ -121,7 +121,7 @@
       OFFSET_OF_OBJECT_MEMBER(Object, x_rb_ptr_));
 #else
   LOG(FATAL) << "Unreachable";
-  return nullptr;
+  UNREACHABLE();
 #endif
 }
 
@@ -134,6 +134,8 @@
       OFFSET_OF_OBJECT_MEMBER(Object, x_rb_ptr_), rb_ptr);
 #else
   LOG(FATAL) << "Unreachable";
+  UNREACHABLE();
+  UNUSED(rb_ptr);
 #endif
 }
 
@@ -141,7 +143,7 @@
 #ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
   DCHECK(kUseBakerOrBrooksReadBarrier);
   MemberOffset offset = OFFSET_OF_OBJECT_MEMBER(Object, x_rb_ptr_);
-  byte* raw_addr = reinterpret_cast<byte*>(this) + offset.SizeValue();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + offset.SizeValue();
   Atomic<uint32_t>* atomic_rb_ptr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
   HeapReference<Object> expected_ref(HeapReference<Object>::FromMirrorPtr(expected_rb_ptr));
   HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(rb_ptr));
@@ -155,8 +157,9 @@
   DCHECK_EQ(new_ref.reference_, atomic_rb_ptr->LoadRelaxed());
   return true;
 #else
+  UNUSED(expected_rb_ptr, rb_ptr);
   LOG(FATAL) << "Unreachable";
-  return false;
+  UNREACHABLE();
 #endif
 }
 
@@ -166,13 +169,12 @@
     DCHECK(obj->GetReadBarrierPointer() == nullptr)
         << "Bad Baker pointer: obj=" << reinterpret_cast<void*>(obj)
         << " ptr=" << reinterpret_cast<void*>(obj->GetReadBarrierPointer());
-  } else if (kUseBrooksReadBarrier) {
+  } else {
+    CHECK(kUseBrooksReadBarrier);
     Object* obj = const_cast<Object*>(this);
     DCHECK_EQ(obj, obj->GetReadBarrierPointer())
         << "Bad Brooks pointer: obj=" << reinterpret_cast<void*>(obj)
         << " ptr=" << reinterpret_cast<void*>(obj->GetReadBarrierPointer());
-  } else {
-    LOG(FATAL) << "Unreachable";
   }
 }
 
@@ -408,17 +410,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 +582,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>
@@ -468,7 +604,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
   AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr);
 
   return atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_value, new_value);
@@ -486,7 +622,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
   AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr);
 
   return atomic_addr->CompareExchangeWeakRelaxed(old_value, new_value);
@@ -504,7 +640,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
   AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr);
 
   return atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_value, new_value);
@@ -515,13 +651,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 +673,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 +682,28 @@
                                                                                new_value);
 }
 
+template<typename kSize, bool kIsVolatile>
+inline void Object::SetField(MemberOffset field_offset, kSize new_value) {
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(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 uint8_t* raw_addr = reinterpret_cast<const uint8_t*>(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) {
@@ -570,7 +716,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
   Atomic<int64_t>* atomic_addr = reinterpret_cast<Atomic<int64_t>*>(raw_addr);
   return atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_value, new_value);
 }
@@ -587,7 +733,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
   Atomic<int64_t>* atomic_addr = reinterpret_cast<Atomic<int64_t>*>(raw_addr);
   return atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_value, new_value);
 }
@@ -598,7 +744,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
   HeapReference<T>* objref_addr = reinterpret_cast<HeapReference<T>*>(raw_addr);
   T* result = ReadBarrier::Barrier<T, kReadBarrierOption>(this, field_offset, objref_addr);
   if (kIsVolatile) {
@@ -638,7 +784,7 @@
   if (kVerifyFlags & kVerifyWrites) {
     VerifyObject(new_value);
   }
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
   HeapReference<Object>* objref_addr = reinterpret_cast<HeapReference<Object>*>(raw_addr);
   if (kIsVolatile) {
     // TODO: Refactor to use a SequentiallyConsistent store instead.
@@ -674,7 +820,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  return reinterpret_cast<HeapReference<Object>*>(reinterpret_cast<byte*>(this) +
+  return reinterpret_cast<HeapReference<Object>*>(reinterpret_cast<uint8_t*>(this) +
       field_offset.Int32Value());
 }
 
@@ -698,7 +844,7 @@
   }
   HeapReference<Object> old_ref(HeapReference<Object>::FromMirrorPtr(old_value));
   HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_value));
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
   Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
 
   bool success = atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_ref.reference_,
@@ -730,7 +876,7 @@
   }
   HeapReference<Object> old_ref(HeapReference<Object>::FromMirrorPtr(old_value));
   HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_value));
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
   Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
 
   bool success = atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_ref.reference_,
@@ -744,21 +890,21 @@
 
 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 (!kVisitClass) {
-     // Mask out the class from the reference offsets.
-      ref_offsets ^= kWordHighBitMask;
+  if (!kIsStatic && (ref_offsets != mirror::Class::kClassWalkSuper)) {
+    // Instance fields and not the slow-path.
+    if (kVisitClass) {
+      visitor(this, ClassOffset(), kIsStatic);
     }
-    DCHECK_EQ(ClassOffset().Uint32Value(), 0U);
-    // Found a reference offset bitmap. Visit the specified offsets.
+    uint32_t field_offset = mirror::kObjectHeaderSize;
     while (ref_offsets != 0) {
-      size_t right_shift = CLZ(ref_offsets);
-      MemberOffset field_offset = CLASS_OFFSET_FROM_CLZ(right_shift);
-      visitor(this, field_offset, kIsStatic);
-      ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
+      if ((ref_offsets & 1) != 0) {
+        visitor(this, MemberOffset(field_offset), kIsStatic);
+      }
+      ref_offsets >>= 1;
+      field_offset += sizeof(mirror::HeapReference<mirror::Object>);
     }
   } 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 +932,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..4227723 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -69,8 +69,8 @@
                            size_t num_bytes) {
   // Copy instance data.  We assume memcpy copies by words.
   // TODO: expose and use move32.
-  byte* src_bytes = reinterpret_cast<byte*>(src);
-  byte* dst_bytes = reinterpret_cast<byte*>(dest);
+  uint8_t* src_bytes = reinterpret_cast<uint8_t*>(src);
+  uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(dest);
   size_t offset = sizeof(Object);
   memcpy(dst_bytes + offset, src_bytes + offset, num_bytes - offset);
   if (kUseBakerOrBrooksReadBarrier) {
@@ -135,11 +135,11 @@
   return copy;
 }
 
-int32_t Object::GenerateIdentityHashCode() {
-  static AtomicInteger seed(987654321 + std::time(nullptr));
-  int32_t expected_value, new_value;
+uint32_t Object::GenerateIdentityHashCode() {
+  static Atomic<uint32_t> seed(987654321U + std::time(nullptr));
+  uint32_t expected_value, new_value;
   do {
-    expected_value = static_cast<uint32_t>(seed.LoadRelaxed());
+    expected_value = seed.LoadRelaxed();
     new_value = expected_value * 1103515245 + 12345;
   } while ((expected_value & LockWord::kHashMask) == 0 ||
       !seed.CompareExchangeWeakRelaxed(expected_value, new_value));
@@ -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..0ce5231 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_MIRROR_OBJECT_H_
 #define ART_RUNTIME_MIRROR_OBJECT_H_
 
+#include "globals.h"
 #include "object_reference.h"
 #include "offsets.h"
 #include "verify_object.h"
@@ -60,6 +61,9 @@
 // Checks that we don't do field assignments which violate the typing system.
 static constexpr bool kCheckFieldAssignments = false;
 
+// Size of Object.
+static constexpr uint32_t kObjectHeaderSize = kUseBakerOrBrooksReadBarrier ? 16 : 8;
+
 // C++ mirror of java.lang.Object
 class MANAGED LOCKABLE Object {
  public:
@@ -247,6 +251,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 +432,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)
@@ -368,7 +451,7 @@
   }
 
   // Generate an identity hash code.
-  static int32_t GenerateIdentityHashCode();
+  static uint32_t GenerateIdentityHashCode();
 
   // A utility function that copies an object in a read barrier and
   // write barrier-aware way. This is internally used by Clone() and
@@ -395,6 +478,7 @@
   friend struct art::ObjectOffsets;  // for verifying offset information
   friend class CopyObjectVisitor;  // for CopyObject().
   friend class CopyClassVisitor;   // for CopyObject().
+  DISALLOW_ALLOCATION();
   DISALLOW_IMPLICIT_CONSTRUCTORS(Object);
 };
 
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index c7540dc..fbc4f4a 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -19,6 +19,7 @@
 
 #include "object_array.h"
 
+#include "array-inl.h"
 #include "base/stringprintf.h"
 #include "gc/heap.h"
 #include "mirror/art_field.h"
@@ -35,10 +36,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..a0aaa9e 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -74,26 +74,10 @@
   }
 };
 
-// Keep the assembly code in sync
-TEST_F(ObjectTest, AsmConstants) {
-  EXPECT_EQ(CLASS_OFFSET, Object::ClassOffset().Int32Value());
-  EXPECT_EQ(LOCK_WORD_OFFSET, Object::MonitorOffset().Int32Value());
-
-  EXPECT_EQ(CLASS_COMPONENT_TYPE_OFFSET, Class::ComponentTypeOffset().Int32Value());
-
-  EXPECT_EQ(ARRAY_LENGTH_OFFSET, Array::LengthOffset().Int32Value());
-  EXPECT_EQ(OBJECT_ARRAY_DATA_OFFSET, Array::DataOffset(sizeof(HeapReference<Object>)).Int32Value());
-
-  EXPECT_EQ(STRING_VALUE_OFFSET, String::ValueOffset().Int32Value());
-  EXPECT_EQ(STRING_COUNT_OFFSET, String::CountOffset().Int32Value());
-  EXPECT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value());
-  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());
+// Keep constants in sync.
+TEST_F(ObjectTest, Constants) {
+  EXPECT_EQ(kObjectReferenceSize, sizeof(HeapReference<Object>));
+  EXPECT_EQ(kObjectHeaderSize, sizeof(Object));
 }
 
 TEST_F(ObjectTest, IsInSamePackage) {
@@ -158,20 +142,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 +165,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());
 }
@@ -244,12 +228,6 @@
 TEST_F(ObjectTest, PrimitiveArray_Char_Alloc) {
   TestPrimitiveArray<CharArray>(class_linker_);
 }
-TEST_F(ObjectTest, PrimitiveArray_Double_Alloc) {
-  TestPrimitiveArray<DoubleArray>(class_linker_);
-}
-TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) {
-  TestPrimitiveArray<FloatArray>(class_linker_);
-}
 TEST_F(ObjectTest, PrimitiveArray_Int_Alloc) {
   TestPrimitiveArray<IntArray>(class_linker_);
 }
@@ -260,6 +238,67 @@
   TestPrimitiveArray<ShortArray>(class_linker_);
 }
 
+TEST_F(ObjectTest, PrimitiveArray_Double_Alloc) {
+  typedef DoubleArray ArrayT;
+  ScopedObjectAccess soa(Thread::Current());
+  typedef typename ArrayT::ElementType T;
+
+  ArrayT* a = ArrayT::Alloc(soa.Self(), 2);
+  EXPECT_EQ(2, a->GetLength());
+  EXPECT_DOUBLE_EQ(0, a->Get(0));
+  EXPECT_DOUBLE_EQ(0, a->Get(1));
+  a->Set(0, T(123));
+  EXPECT_DOUBLE_EQ(T(123), a->Get(0));
+  EXPECT_DOUBLE_EQ(0, a->Get(1));
+  a->Set(1, T(321));
+  EXPECT_DOUBLE_EQ(T(123), a->Get(0));
+  EXPECT_DOUBLE_EQ(T(321), a->Get(1));
+
+  Class* aioobe = class_linker_->FindSystemClass(soa.Self(),
+                                                 "Ljava/lang/ArrayIndexOutOfBoundsException;");
+
+  EXPECT_DOUBLE_EQ(0, a->Get(-1));
+  EXPECT_TRUE(soa.Self()->IsExceptionPending());
+  EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass());
+  soa.Self()->ClearException();
+
+  EXPECT_DOUBLE_EQ(0, a->Get(2));
+  EXPECT_TRUE(soa.Self()->IsExceptionPending());
+  EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass());
+  soa.Self()->ClearException();
+}
+
+TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) {
+  typedef FloatArray ArrayT;
+  ScopedObjectAccess soa(Thread::Current());
+  typedef typename ArrayT::ElementType T;
+
+  ArrayT* a = ArrayT::Alloc(soa.Self(), 2);
+  EXPECT_FLOAT_EQ(2, a->GetLength());
+  EXPECT_FLOAT_EQ(0, a->Get(0));
+  EXPECT_FLOAT_EQ(0, a->Get(1));
+  a->Set(0, T(123));
+  EXPECT_FLOAT_EQ(T(123), a->Get(0));
+  EXPECT_FLOAT_EQ(0, a->Get(1));
+  a->Set(1, T(321));
+  EXPECT_FLOAT_EQ(T(123), a->Get(0));
+  EXPECT_FLOAT_EQ(T(321), a->Get(1));
+
+  Class* aioobe = class_linker_->FindSystemClass(soa.Self(),
+                                                 "Ljava/lang/ArrayIndexOutOfBoundsException;");
+
+  EXPECT_FLOAT_EQ(0, a->Get(-1));
+  EXPECT_TRUE(soa.Self()->IsExceptionPending());
+  EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass());
+  soa.Self()->ClearException();
+
+  EXPECT_FLOAT_EQ(0, a->Get(2));
+  EXPECT_TRUE(soa.Self()->IsExceptionPending());
+  EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass());
+  soa.Self()->ClearException();
+}
+
+
 TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
   // pretend we are trying to call 'new char[3]' from String.toCharArray
   ScopedObjectAccess soa(Thread::Current());
@@ -284,7 +323,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 +521,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/mirror/string.h b/runtime/mirror/string.h
index 1320ab7..64408a6 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -17,8 +17,6 @@
 #ifndef ART_RUNTIME_MIRROR_STRING_H_
 #define ART_RUNTIME_MIRROR_STRING_H_
 
-#include <gtest/gtest.h>
-
 #include "gc_root.h"
 #include "object.h"
 #include "object_callbacks.h"
@@ -163,7 +161,8 @@
   static GcRoot<Class> java_lang_String_;
 
   friend struct art::StringOffsets;  // for verifying offset information
-  FRIEND_TEST(ObjectTest, StringLength);  // for SetOffset and SetCount
+  ART_FRIEND_TEST(ObjectTest, StringLength);  // for SetOffset and SetCount
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(String);
 };
 
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 0cf077a..6445b88 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -178,10 +178,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);
@@ -199,10 +195,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);
@@ -404,29 +396,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);
@@ -583,7 +552,7 @@
     thread->SetWaitNext(nullptr);
 
     // Check to see if the thread is still waiting.
-    MutexLock mu(self, *thread->GetWaitMutex());
+    MutexLock wait_mu(self, *thread->GetWaitMutex());
     if (thread->GetWaitMonitor() != nullptr) {
       thread->GetWaitConditionVariable()->Signal(self);
       return;
@@ -827,9 +796,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);
@@ -920,7 +886,7 @@
     }
     default: {
       LOG(FATAL) << "Unreachable";
-      return ThreadList::kInvalidThreadId;
+      UNREACHABLE();
     }
   }
 }
@@ -1035,12 +1001,12 @@
   for (size_t i = 0; i < monitor_enter_dex_pcs.size(); ++i) {
     // The verifier works in terms of the dex pcs of the monitor-enter instructions.
     // We want the registers used by those instructions (so we can read the values out of them).
-    uint32_t dex_pc = monitor_enter_dex_pcs[i];
-    uint16_t monitor_enter_instruction = code_item->insns_[dex_pc];
+    uint32_t monitor_dex_pc = monitor_enter_dex_pcs[i];
+    uint16_t monitor_enter_instruction = code_item->insns_[monitor_dex_pc];
 
     // Quick sanity check.
     if ((monitor_enter_instruction & 0xff) != Instruction::MONITOR_ENTER) {
-      LOG(FATAL) << "expected monitor-enter @" << dex_pc << "; was "
+      LOG(FATAL) << "expected monitor-enter @" << monitor_dex_pc << "; was "
                  << reinterpret_cast<void*>(monitor_enter_instruction);
     }
 
@@ -1075,7 +1041,7 @@
       return true;
     default:
       LOG(FATAL) << "Unreachable";
-      return false;
+      UNREACHABLE();
   }
 }
 
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_pool.h b/runtime/monitor_pool.h
index cb45162..27678dc 100644
--- a/runtime/monitor_pool.h
+++ b/runtime/monitor_pool.h
@@ -53,6 +53,7 @@
 
   static void ReleaseMonitor(Thread* self, Monitor* monitor) {
 #ifndef __LP64__
+    UNUSED(self);
     delete monitor;
 #else
     GetMonitorPool()->ReleaseMonitorToPool(self, monitor);
@@ -61,6 +62,7 @@
 
   static void ReleaseMonitors(Thread* self, MonitorList::Monitors* monitors) {
 #ifndef __LP64__
+    UNUSED(self);
     STLDeleteElements(monitors);
 #else
     GetMonitorPool()->ReleaseMonitorsToPool(self, monitors);
@@ -85,6 +87,7 @@
 
   static MonitorId ComputeMonitorId(Monitor* mon, Thread* self) {
 #ifndef __LP64__
+    UNUSED(self);
     return MonitorIdFromMonitor(mon);
 #else
     return GetMonitorPool()->ComputeMonitorIdInPool(mon, self);
@@ -172,7 +175,7 @@
   // To avoid race issues when resizing, we keep all the previous arrays.
   std::vector<uintptr_t*> old_chunk_arrays_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
 
-  typedef TrackingAllocator<byte, kAllocatorTagMonitorPool> Allocator;
+  typedef TrackingAllocator<uint8_t, kAllocatorTagMonitorPool> Allocator;
   Allocator allocator_;
 
   // Start of free list of monitors.
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index af24368..adc7848 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);
@@ -341,8 +341,7 @@
 
   // Wake the watchdog.
   {
-    Thread* self = Thread::Current();
-    ScopedObjectAccess soa(self);
+    ScopedObjectAccess soa(Thread::Current());
 
     test->watchdog_object_.Get()->MonitorEnter(self);     // Lock the object.
     test->watchdog_object_.Get()->NotifyAll(self);        // Wake up waiting parties.
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index a8b297d..f37312e 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "dalvik_system_DexFile.h"
+
 #include <algorithm>
 #include <set>
 #include <fcntl.h>
@@ -43,13 +45,17 @@
 #include "profiler.h"
 #include "runtime.h"
 #include "scoped_thread_state_change.h"
-#include "ScopedFd.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
 #include "utils.h"
 #include "well_known_classes.h"
 #include "zip_archive.h"
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#include "ScopedFd.h"
+#pragma GCC diagnostic pop
+
 namespace art {
 
 // A smart pointer that provides read-only access to a Java string's UTF chars.
@@ -192,12 +198,13 @@
       mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash,
                                                         class_loader, *dex_file, *dex_class_def);
       if (result != nullptr) {
-        VLOG(class_linker) << "DexFile_defineClassNative returning " << result;
+        VLOG(class_linker) << "DexFile_defineClassNative returning " << result
+                           << " for " << class_name.c_str();
         return soa.AddLocalReference<jclass>(result);
       }
     }
   }
-  VLOG(class_linker) << "Failed to find dex_class_def";
+  VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str();
   return nullptr;
 }
 
diff --git a/runtime/log_severity.h b/runtime/native/dalvik_system_DexFile.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/dalvik_system_DexFile.h
index 31682df..487df05 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/dalvik_system_DexFile.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_DEXFILE_H_
+#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_DEXFILE_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_dalvik_system_DexFile(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_DEXFILE_H_
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index ceff206..6c82eb2 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "dalvik_system_VMDebug.h"
+
 #include <string.h>
 #include <unistd.h>
 
diff --git a/runtime/log_severity.h b/runtime/native/dalvik_system_VMDebug.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/dalvik_system_VMDebug.h
index 31682df..b7eb8a8 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/dalvik_system_VMDebug.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_VMDEBUG_H_
+#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_VMDEBUG_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_dalvik_system_VMDebug(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_VMDEBUG_H_
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 4b1236a..f6e2b21 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "dalvik_system_VMRuntime.h"
+
 #include <limits.h>
 
 #include "ScopedUtfChars.h"
@@ -38,7 +40,11 @@
 #include "scoped_thread_state_change.h"
 #include "thread.h"
 #include "thread_list.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
 #include "toStringArray.h"
+#pragma GCC diagnostic pop
 
 namespace art {
 
@@ -76,7 +82,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 +106,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);
 }
 
@@ -165,13 +172,13 @@
   return env->NewStringUTF(isa_string);
 }
 
-static jboolean VMRuntime_is64Bit(JNIEnv* env, jobject) {
+static jboolean VMRuntime_is64Bit(JNIEnv*, jobject) {
   bool is64BitMode = (sizeof(void*) == sizeof(uint64_t));
   return is64BitMode ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean VMRuntime_isCheckJniEnabled(JNIEnv* env, jobject) {
-  return Runtime::Current()->GetJavaVM()->check_jni ? JNI_TRUE : JNI_FALSE;
+  return down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE;
 }
 
 static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) {
@@ -200,9 +207,10 @@
   Runtime::Current()->GetHeap()->RegisterNativeFree(env, static_cast<size_t>(bytes));
 }
 
-static void VMRuntime_updateProcessState(JNIEnv* env, jobject, jint process_state) {
-  Runtime::Current()->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state));
-  Runtime::Current()->UpdateProfilerState(process_state);
+static void VMRuntime_updateProcessState(JNIEnv*, jobject, jint process_state) {
+  Runtime* runtime = Runtime::Current();
+  runtime->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state));
+  runtime->UpdateProfilerState(process_state);
 }
 
 static void VMRuntime_trimHeap(JNIEnv*, jobject) {
@@ -242,7 +250,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 +262,7 @@
   if (class_name[1] == '\0') {
     klass = linker->FindPrimitiveClass(class_name[0]);
   } else {
-    klass = linker->LookupClass(class_name, ComputeModifiedUtf8Hash(class_name), NULL);
+    klass = linker->LookupClass(self, class_name, ComputeModifiedUtf8Hash(class_name), NULL);
   }
   if (klass == NULL) {
     return;
@@ -325,6 +333,7 @@
       break;
     default:
       LOG(FATAL) << "Unreachable - invocation type: " << invoke_type;
+      UNREACHABLE();
   }
   if (method == NULL) {
     return;
@@ -383,26 +392,26 @@
     const DexFile* dex_file = boot_class_path[i];
     CHECK(dex_file != NULL);
     mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file);
-    for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
-      mirror::String* string = dex_cache->GetResolvedString(i);
+    for (size_t j = 0; j < dex_cache->NumStrings(); j++) {
+      mirror::String* string = dex_cache->GetResolvedString(j);
       if (string != NULL) {
         filled->num_strings++;
       }
     }
-    for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
-      mirror::Class* klass = dex_cache->GetResolvedType(i);
+    for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) {
+      mirror::Class* klass = dex_cache->GetResolvedType(j);
       if (klass != NULL) {
         filled->num_types++;
       }
     }
-    for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
-      mirror::ArtField* field = dex_cache->GetResolvedField(i);
+    for (size_t j = 0; j < dex_cache->NumResolvedFields(); j++) {
+      mirror::ArtField* field = dex_cache->GetResolvedField(j);
       if (field != NULL) {
         filled->num_fields++;
       }
     }
-    for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
-      mirror::ArtMethod* method = dex_cache->GetResolvedMethod(i);
+    for (size_t j = 0; j < dex_cache->NumResolvedMethods(); j++) {
+      mirror::ArtMethod* method = dex_cache->GetResolvedMethod(j);
       if (method != NULL) {
         filled->num_methods++;
       }
@@ -431,7 +440,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,18 +452,18 @@
   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) {
-      for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
-        PreloadDexCachesResolveString(dex_cache, i, strings);
+      for (size_t j = 0; j < dex_cache->NumStrings(); j++) {
+        PreloadDexCachesResolveString(dex_cache, j, strings);
       }
     }
 
     if (kPreloadDexCachesTypes) {
-      for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
-        PreloadDexCachesResolveType(dex_cache.Get(), i);
+      for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) {
+        PreloadDexCachesResolveType(soa.Self(), dex_cache.Get(), j);
       }
     }
 
@@ -464,7 +472,7 @@
            class_def_index < dex_file->NumClassDefs();
            class_def_index++) {
         const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
-        const byte* class_data = dex_file->GetClassData(class_def);
+        const uint8_t* class_data = dex_file->GetClassData(class_def);
         if (class_data == NULL) {
           continue;
         }
@@ -513,8 +521,9 @@
  * for ART.
  */
 static void VMRuntime_registerAppInfo(JNIEnv* env, jclass, jstring pkgName,
-                                      jstring appDir, jstring procName) {
-  const char *pkgNameChars = env->GetStringUTFChars(pkgName, NULL);
+                                      jstring appDir ATTRIBUTE_UNUSED,
+                                      jstring procName ATTRIBUTE_UNUSED) {
+  const char *pkgNameChars = env->GetStringUTFChars(pkgName, nullptr);
   std::string profileFile = StringPrintf("/data/dalvik-cache/profiles/%s", pkgNameChars);
 
   Runtime::Current()->StartProfiler(profileFile.c_str());
diff --git a/runtime/log_severity.h b/runtime/native/dalvik_system_VMRuntime.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/dalvik_system_VMRuntime.h
index 31682df..795caa5 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/dalvik_system_VMRuntime.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_VMRUNTIME_H_
+#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_VMRUNTIME_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_dalvik_system_VMRuntime(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_VMRUNTIME_H_
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index eef1c46..e396dad 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "dalvik_system_VMStack.h"
+
 #include "jni_internal.h"
 #include "nth_caller_visitor.h"
 #include "mirror/art_method-inl.h"
@@ -87,8 +89,10 @@
 static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject javaBootstrap,
                                                  jobject javaSystem) {
   struct ClosestUserClassLoaderVisitor : public StackVisitor {
-    ClosestUserClassLoaderVisitor(Thread* thread, mirror::Object* bootstrap, mirror::Object* system)
-      : StackVisitor(thread, NULL), bootstrap(bootstrap), system(system), class_loader(NULL) {}
+    ClosestUserClassLoaderVisitor(Thread* thread, mirror::Object* bootstrap_in,
+                                  mirror::Object* system_in)
+      : StackVisitor(thread, NULL), bootstrap(bootstrap_in), system(system_in),
+        class_loader(NULL) {}
 
     bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
       DCHECK(class_loader == NULL);
diff --git a/runtime/log_severity.h b/runtime/native/dalvik_system_VMStack.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/dalvik_system_VMStack.h
index 31682df..5638f99 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/dalvik_system_VMStack.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_VMSTACK_H_
+#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_VMSTACK_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_dalvik_system_VMStack(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_VMSTACK_H_
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index a4ef839..0966954 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
+#include "dalvik_system_ZygoteHooks.h"
+
 #include <stdlib.h>
 
 #include "debugger.h"
 #include "instruction_set.h"
+#include "java_vm_ext.h"
 #include "jni_internal.h"
 #include "JNIHelp.h"
 #include "ScopedUtfChars.h"
@@ -49,7 +52,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 +64,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.
@@ -99,8 +102,7 @@
   runtime->PreZygoteFork();
 
   // Grab thread before fork potentially makes Thread::pthread_key_self_ unusable.
-  Thread* self = Thread::Current();
-  return reinterpret_cast<jlong>(self);
+  return reinterpret_cast<jlong>(ThreadForEnv(env));
 }
 
 static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags,
diff --git a/runtime/log_severity.h b/runtime/native/dalvik_system_ZygoteHooks.h
similarity index 62%
copy from runtime/log_severity.h
copy to runtime/native/dalvik_system_ZygoteHooks.h
index 31682df..ca0658d 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/dalvik_system_ZygoteHooks.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_ZYGOTEHOOKS_H_
+#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_ZYGOTEHOOKS_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_dalvik_system_ZygoteHooks(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_ZYGOTEHOOKS_H_
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 124bdf5..1ea75f3 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_Class.h"
+
 #include "class_linker.h"
 #include "dex_file-inl.h"
 #include "jni_internal.h"
@@ -78,7 +80,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/log_severity.h b/runtime/native/java_lang_Class.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_Class.h
index 31682df..8f769c3 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_Class.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_CLASS_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_CLASS_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_Class(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_CLASS_H_
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index 51cd5b8..27eae46 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+#include "java_lang_DexCache.h"
+
 #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/log_severity.h b/runtime/native/java_lang_DexCache.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_DexCache.h
index 31682df..b1c1f5e 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_DexCache.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_DEXCACHE_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_DEXCACHE_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_DexCache(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_DEXCACHE_H_
diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc
index 4768f48..49cacdf 100644
--- a/runtime/native/java_lang_Object.cc
+++ b/runtime/native/java_lang_Object.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_Object.h"
+
 #include "jni_internal.h"
 #include "mirror/object-inl.h"
 #include "scoped_fast_native_object_access.h"
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_Object.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_Object.h
index 31682df..c860571 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_Object.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_OBJECT_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_OBJECT_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_Object(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_OBJECT_H_
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index fb708a2..dc0cb7b 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_Runtime.h"
+
 #include <dlfcn.h>
 #include <limits.h>
 #include <unistd.h>
@@ -37,13 +39,16 @@
   Runtime::Current()->GetHeap()->CollectGarbage(false);
 }
 
-static void Runtime_nativeExit(JNIEnv*, jclass, jint status) {
+[[noreturn]] static void Runtime_nativeExit(JNIEnv*, jclass, jint status) {
   LOG(INFO) << "System.exit called, status: " << status;
   Runtime::Current()->CallExitHook(status);
   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 +69,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 +80,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/log_severity.h b/runtime/native/java_lang_Runtime.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_Runtime.h
index 31682df..ceda06b 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_Runtime.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_RUNTIME_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_RUNTIME_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_Runtime(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_RUNTIME_H_
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index d6b47eb..4ea2546 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_String.h"
+
 #include "common_throws.h"
 #include "jni_internal.h"
 #include "mirror/string-inl.h"
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_String.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_String.h
index 31682df..357eb3d 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_String.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_STRING_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_STRING_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_String(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_STRING_H_
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index ee99e78..f79be56 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_System.h"
+
 #include "common_throws.h"
 #include "gc/accounting/card_table-inl.h"
 #include "jni_internal.h"
@@ -93,7 +95,7 @@
     switch (dstComponentPrimitiveType) {
       case Primitive::kPrimVoid:
         LOG(FATAL) << "Unreachable, cannot have arrays of type void";
-        return;
+        UNREACHABLE();
       case Primitive::kPrimBoolean:
       case Primitive::kPrimByte:
         DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 1U);
@@ -122,7 +124,7 @@
       }
       default:
         LOG(FATAL) << "Unknown array type: " << PrettyTypeOf(srcArray);
-        return;
+        UNREACHABLE();
     }
   }
   // If one of the arrays holds a primitive type the other array must hold the exact same type.
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_System.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_System.h
index 31682df..e371fa5 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_System.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_SYSTEM_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_SYSTEM_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_System(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_SYSTEM_H_
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index c0c7265..0722a24 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_Thread.h"
+
 #include "common_throws.h"
 #include "debugger.h"
 #include "jni_internal.h"
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_Thread.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_Thread.h
index 31682df..7700ce2 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_Thread.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_THREAD_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_THREAD_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_Thread(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_THREAD_H_
diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc
index 3ed4cfe..cb8a869 100644
--- a/runtime/native/java_lang_Throwable.cc
+++ b/runtime/native/java_lang_Throwable.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_Throwable.h"
+
 #include "jni_internal.h"
 #include "scoped_fast_native_object_access.h"
 #include "thread.h"
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_Throwable.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_Throwable.h
index 31682df..f9aea84 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_Throwable.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_THROWABLE_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_THROWABLE_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_Throwable(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_THROWABLE_H_
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 36b02dc..35932e0 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_VMClassLoader.h"
+
 #include "class_linker.h"
 #include "jni_internal.h"
 #include "mirror/class_loader.h"
@@ -24,7 +26,8 @@
 
 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);
@@ -34,7 +37,7 @@
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
   std::string descriptor(DotToDescriptor(name.c_str()));
   const size_t descriptor_hash = ComputeModifiedUtf8Hash(descriptor.c_str());
-  mirror::Class* c = cl->LookupClass(descriptor.c_str(), descriptor_hash, loader);
+  mirror::Class* c = cl->LookupClass(soa.Self(), descriptor.c_str(), descriptor_hash, loader);
   if (c != nullptr && c->IsResolved()) {
     return soa.AddLocalReference<jclass>(c);
   }
@@ -69,13 +72,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/log_severity.h b/runtime/native/java_lang_VMClassLoader.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_VMClassLoader.h
index 31682df..bf8d94f 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_VMClassLoader.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_VMCLASSLOADER_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_VMCLASSLOADER_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_VMClassLoader(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_VMCLASSLOADER_H_
diff --git a/runtime/native/java_lang_ref_FinalizerReference.cc b/runtime/native/java_lang_ref_FinalizerReference.cc
index ad48ec0..0532c35 100644
--- a/runtime/native/java_lang_ref_FinalizerReference.cc
+++ b/runtime/native/java_lang_ref_FinalizerReference.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_ref_FinalizerReference.h"
+
 #include "gc/heap.h"
 #include "gc/reference_processor.h"
 #include "jni_internal.h"
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_ref_FinalizerReference.h
similarity index 60%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_ref_FinalizerReference.h
index 31682df..848a7ad 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_ref_FinalizerReference.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REF_FINALIZERREFERENCE_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_REF_FINALIZERREFERENCE_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_ref_FinalizerReference(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REF_FINALIZERREFERENCE_H_
diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc
index 4f04d60..d232059 100644
--- a/runtime/native/java_lang_ref_Reference.cc
+++ b/runtime/native/java_lang_ref_Reference.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_ref_Reference.h"
+
 #include "gc/heap.h"
 #include "gc/reference_processor.h"
 #include "jni_internal.h"
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_ref_Reference.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_ref_Reference.h
index 31682df..0cbf116 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_ref_Reference.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REF_REFERENCE_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_REF_REFERENCE_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_ref_Reference(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REF_REFERENCE_H_
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 058458f..1ffcbdf 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_reflect_Array.h"
+
 #include "class_linker-inl.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
@@ -59,9 +61,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/log_severity.h b/runtime/native/java_lang_reflect_Array.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_reflect_Array.h
index 31682df..805bf79 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_reflect_Array.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ARRAY_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ARRAY_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_reflect_Array(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ARRAY_H_
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 34cb93a..3121a90 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_reflect_Constructor.h"
+
 #include "class_linker.h"
 #include "jni_internal.h"
 #include "mirror/art_method.h"
@@ -48,7 +50,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/log_severity.h b/runtime/native/java_lang_reflect_Constructor.h
similarity index 61%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_reflect_Constructor.h
index 31682df..7baae97 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_reflect_Constructor.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_CONSTRUCTOR_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_CONSTRUCTOR_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_reflect_Constructor(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_CONSTRUCTOR_H_
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 8850df2..a042620 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_reflect_Field.h"
+
 #include "class_linker.h"
 #include "class_linker-inl.h"
 #include "common_throws.h"
@@ -33,22 +35,32 @@
                                                    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;
 }
 
 template<bool kAllowReferences>
-ALWAYS_INLINE inline static bool GetFieldValue(
-    const ScopedFastNativeObjectAccess& soa, mirror::Object* o, mirror::ArtField* f,
-    Primitive::Type field_type, JValue* value)
+ALWAYS_INLINE inline static bool GetFieldValue(mirror::Object* o, mirror::ArtField* f,
+                                               Primitive::Type field_type, JValue* value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   DCHECK_EQ(value->GetJ(), INT64_C(0));
   switch (field_type) {
@@ -104,7 +116,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;
       }
@@ -137,7 +149,7 @@
   // Get the field's value, boxing if necessary.
   Primitive::Type field_type = f->GetTypeAsPrimitiveType();
   JValue value;
-  if (!GetFieldValue<true>(soa, o, f, field_type, &value)) {
+  if (!GetFieldValue<true>(o, f, field_type, &value)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return nullptr;
   }
@@ -167,13 +179,13 @@
   JValue field_value;
   if (field_type == kPrimitiveType) {
     // This if statement should get optimized out since we only pass in valid primitive types.
-    if (UNLIKELY(!GetFieldValue<false>(soa, o, f, kPrimitiveType, &field_value))) {
+    if (UNLIKELY(!GetFieldValue<false>(o, f, kPrimitiveType, &field_value))) {
       DCHECK(soa.Self()->IsExceptionPending());
       return JValue();
     }
     return field_value;
   }
-  if (!GetFieldValue<false>(soa, o, f, field_type, &field_value)) {
+  if (!GetFieldValue<false>(o, f, field_type, &field_value)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return JValue();
   }
@@ -221,9 +233,8 @@
   return GetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj, accessible).GetS();
 }
 
-static void SetFieldValue(ScopedFastNativeObjectAccess& soa, mirror::Object* o,
-                          mirror::ArtField* f, Primitive::Type field_type, bool allow_references,
-                          const JValue& new_value)
+static void SetFieldValue(mirror::Object* o, mirror::ArtField* f, Primitive::Type field_type,
+                          bool allow_references, const JValue& new_value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   DCHECK(f->GetDeclaringClass()->IsInitialized());
   switch (field_type) {
@@ -257,6 +268,7 @@
       break;
     }
     // Else fall through to report an error.
+    FALLTHROUGH_INTENDED;
   case Primitive::kPrimVoid:
     // Never okay.
     ThrowIllegalArgumentException(nullptr, StringPrintf("Not a primitive field: %s",
@@ -305,7 +317,7 @@
     DCHECK(soa.Self()->IsExceptionPending());
     return;
   }
-  SetFieldValue(soa, o, f, field_prim_type, true, unboxed_value);
+  SetFieldValue(o, f, field_prim_type, true, unboxed_value);
 }
 
 template<Primitive::Type kPrimitiveType>
@@ -338,7 +350,7 @@
   }
 
   // Write the value.
-  SetFieldValue(soa, o, f, field_type, false, wide_value);
+  SetFieldValue(o, f, field_type, false, wide_value);
 }
 
 static void Field_setBoolean(JNIEnv* env, jobject javaField, jobject javaObj, jboolean z,
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_reflect_Field.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_reflect_Field.h
index 31682df..1739711 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_reflect_Field.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_FIELD_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_FIELD_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_reflect_Field(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_FIELD_H_
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index f029b16..9859746 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_reflect_Method.h"
+
 #include "class_linker.h"
 #include "jni_internal.h"
 #include "mirror/art_method.h"
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_reflect_Method.h
similarity index 62%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_reflect_Method.h
index 31682df..3a93cd0 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_reflect_Method.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_METHOD_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_METHOD_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_reflect_Method(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_METHOD_H_
diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc
index 07d670d..baf8b24 100644
--- a/runtime/native/java_lang_reflect_Proxy.cc
+++ b/runtime/native/java_lang_reflect_Proxy.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_lang_reflect_Proxy.h"
+
 #include "class_linker.h"
 #include "jni_internal.h"
 #include "mirror/class_loader.h"
diff --git a/runtime/log_severity.h b/runtime/native/java_lang_reflect_Proxy.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/java_lang_reflect_Proxy.h
index 31682df..e25f0f7 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/java_lang_reflect_Proxy.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_PROXY_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_PROXY_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_java_lang_reflect_Proxy(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_PROXY_H_
diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc
index bf92e12..04f0ba0 100644
--- a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc
+++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "java_util_concurrent_atomic_AtomicLong.h"
+
 #include "atomic.h"
 #include "jni_internal.h"
 
diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.h b/runtime/native/java_util_concurrent_atomic_AtomicLong.h
new file mode 100644
index 0000000..990dc86
--- /dev/null
+++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.h
@@ -0,0 +1,28 @@
+/*
+ * 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_RUNTIME_NATIVE_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMICLONG_H_
+#define ART_RUNTIME_NATIVE_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMICLONG_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_java_util_concurrent_atomic_AtomicLong(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMICLONG_H_
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index 163ae20..0ab2979 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+#include "org_apache_harmony_dalvik_ddmc_DdmServer.h"
+
 #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/org_apache_harmony_dalvik_ddmc_DdmServer.h b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.h
new file mode 100644
index 0000000..9a4645c
--- /dev/null
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.h
@@ -0,0 +1,28 @@
+/*
+ * 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_RUNTIME_NATIVE_ORG_APACHE_HARMONY_DALVIK_DDMC_DDMSERVER_H_
+#define ART_RUNTIME_NATIVE_ORG_APACHE_HARMONY_DALVIK_DDMC_DDMSERVER_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_org_apache_harmony_dalvik_ddmc_DdmServer(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_ORG_APACHE_HARMONY_DALVIK_DDMC_DDMSERVER_H_
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 45ef9ae..b74430f 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
+
 #include "base/logging.h"
 #include "base/mutex.h"
 #include "debugger.h"
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h
new file mode 100644
index 0000000..736e4c8
--- /dev/null
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h
@@ -0,0 +1,28 @@
+/*
+ * 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_RUNTIME_NATIVE_ORG_APACHE_HARMONY_DALVIK_DDMC_DDMVMINTERNAL_H_
+#define ART_RUNTIME_NATIVE_ORG_APACHE_HARMONY_DALVIK_DDMC_DDMVMINTERNAL_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_ORG_APACHE_HARMONY_DALVIK_DDMC_DDMVMINTERNAL_H_
diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h
index 606d62d..dfabff5 100644
--- a/runtime/native/scoped_fast_native_object_access.h
+++ b/runtime/native/scoped_fast_native_object_access.h
@@ -17,7 +17,7 @@
 #ifndef ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
 #define ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
 
-#include "mirror/art_method.h"
+#include "mirror/art_method-inl.h"
 #include "scoped_thread_state_change.h"
 
 namespace art {
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 65dece0..17ebdff 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "sun_misc_Unsafe.h"
+
 #include "gc/accounting/card_table-inl.h"
 #include "jni_internal.h"
 #include "mirror/array.h"
diff --git a/runtime/log_severity.h b/runtime/native/sun_misc_Unsafe.h
similarity index 63%
copy from runtime/log_severity.h
copy to runtime/native/sun_misc_Unsafe.h
index 31682df..93194f4 100644
--- a/runtime/log_severity.h
+++ b/runtime/native/sun_misc_Unsafe.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+#ifndef ART_RUNTIME_NATIVE_SUN_MISC_UNSAFE_H_
+#define ART_RUNTIME_NATIVE_SUN_MISC_UNSAFE_H_
 
-typedef int LogSeverity;
+#include <jni.h>
 
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
+namespace art {
 
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+void register_sun_misc_Unsafe(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_SUN_MISC_UNSAFE_H_
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index 9f77e55..c2c6b12 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -19,13 +19,14 @@
 #include "nativebridge/native_bridge.h"
 
 #include "base/logging.h"
+#include "base/macros.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
 #include "scoped_thread_state_change.h"
 
 namespace art {
 
-const char* GetMethodShorty(JNIEnv* env, jmethodID mid) {
+static const char* GetMethodShorty(JNIEnv* env, jmethodID mid) {
   ScopedObjectAccess soa(env);
   StackHandleScope<1> scope(soa.Self());
   mirror::ArtMethod* m = soa.DecodeMethod(mid);
@@ -33,7 +34,7 @@
   return mh.GetShorty();
 }
 
-uint32_t GetNativeMethodCount(JNIEnv* env, jclass clazz) {
+static uint32_t GetNativeMethodCount(JNIEnv* env, jclass clazz) {
   if (clazz == nullptr)
     return 0;
 
@@ -56,8 +57,8 @@
   return native_method_count;
 }
 
-uint32_t GetNativeMethods(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
-                          uint32_t method_count) {
+static uint32_t GetNativeMethods(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
+                                 uint32_t method_count) {
   if ((clazz == nullptr) || (methods == nullptr)) {
     return 0;
   }
@@ -121,6 +122,8 @@
     LOG(WARNING) << "Could not create mount namespace.";
   }
   android::PreInitializeNativeBridge(dir.c_str(), GetInstructionSetString(kRuntimeISA));
+#else
+  UNUSED(dir);
 #endif
 }
 
@@ -132,4 +135,4 @@
   android::UnloadNativeBridge();
 }
 
-};  // namespace art
+}  // namespace art
diff --git a/runtime/native_bridge_art_interface.h b/runtime/native_bridge_art_interface.h
index c287f3b..090cddb 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/noop_compiler_callbacks.h b/runtime/noop_compiler_callbacks.h
index e9ad353..300abc9 100644
--- a/runtime/noop_compiler_callbacks.h
+++ b/runtime/noop_compiler_callbacks.h
@@ -26,11 +26,11 @@
   NoopCompilerCallbacks() {}
   ~NoopCompilerCallbacks() {}
 
-  bool MethodVerified(verifier::MethodVerifier* verifier) OVERRIDE {
+  bool MethodVerified(verifier::MethodVerifier* verifier ATTRIBUTE_UNUSED) OVERRIDE {
     return true;
   }
 
-  void ClassRejected(ClassReference ref) OVERRIDE {}
+  void ClassRejected(ClassReference ref ATTRIBUTE_UNUSED) OVERRIDE {}
 
   // This is only used by compilers which need to be able to run without relocation even when it
   // would normally be enabled. For example the patchoat executable, and dex2oat --image, both need
diff --git a/runtime/nth_caller_visitor.h b/runtime/nth_caller_visitor.h
index 374a80e..a851f21 100644
--- a/runtime/nth_caller_visitor.h
+++ b/runtime/nth_caller_visitor.h
@@ -26,9 +26,9 @@
 
 // Walks up the stack 'n' callers, when used with Thread::WalkStack.
 struct NthCallerVisitor : public StackVisitor {
-  NthCallerVisitor(Thread* thread, size_t n, bool include_runtime_and_upcalls = false)
-      : StackVisitor(thread, NULL), n(n), include_runtime_and_upcalls_(include_runtime_and_upcalls),
-        count(0), caller(NULL) {}
+  NthCallerVisitor(Thread* thread, size_t n_in, bool include_runtime_and_upcalls = false)
+      : StackVisitor(thread, NULL), n(n_in),
+        include_runtime_and_upcalls_(include_runtime_and_upcalls), count(0), caller(NULL) {}
 
   bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     mirror::ArtMethod* m = GetMethod();
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 9e6b767..0749c06 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', '4', '0', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '4', '6', '\0' };
 
 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
   size_t estimate = 0U;
@@ -39,7 +39,7 @@
 }
 
 OatHeader* OatHeader::Create(InstructionSet instruction_set,
-                             const InstructionSetFeatures& instruction_set_features,
+                             const InstructionSetFeatures* instruction_set_features,
                              const std::vector<const DexFile*>* dex_files,
                              uint32_t image_file_location_oat_checksum,
                              uint32_t image_file_location_oat_data_begin,
@@ -60,7 +60,7 @@
 }
 
 OatHeader::OatHeader(InstructionSet instruction_set,
-                     const InstructionSetFeatures& instruction_set_features,
+                     const InstructionSetFeatures* instruction_set_features,
                      const std::vector<const DexFile*>* dex_files,
                      uint32_t image_file_location_oat_checksum,
                      uint32_t image_file_location_oat_data_begin,
@@ -76,8 +76,8 @@
   instruction_set_ = instruction_set;
   UpdateChecksum(&instruction_set_, sizeof(instruction_set_));
 
-  instruction_set_features_ = instruction_set_features;
-  UpdateChecksum(&instruction_set_features_, sizeof(instruction_set_features_));
+  instruction_set_features_bitmap_ = instruction_set_features->AsBitmap();
+  UpdateChecksum(&instruction_set_features_bitmap_, sizeof(instruction_set_features_bitmap_));
 
   dex_file_count_ = dex_files->size();
   UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
@@ -149,9 +149,9 @@
   return instruction_set_;
 }
 
-const InstructionSetFeatures& OatHeader::GetInstructionSetFeatures() const {
+uint32_t OatHeader::GetInstructionSetFeaturesBitmap() const {
   CHECK(IsValid());
-  return instruction_set_features_;
+  return instruction_set_features_bitmap_;
 }
 
 uint32_t OatHeader::GetExecutableOffset() const {
diff --git a/runtime/oat.h b/runtime/oat.h
index a5cb4bc..f577b07 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -38,7 +38,7 @@
   static constexpr const char* kPicKey = "pic";
 
   static OatHeader* Create(InstructionSet instruction_set,
-                           const InstructionSetFeatures& instruction_set_features,
+                           const InstructionSetFeatures* instruction_set_features,
                            const std::vector<const DexFile*>* dex_files,
                            uint32_t image_file_location_oat_checksum,
                            uint32_t image_file_location_oat_data_begin,
@@ -94,7 +94,7 @@
   void SetImagePatchDelta(int32_t off);
 
   InstructionSet GetInstructionSet() const;
-  const InstructionSetFeatures& GetInstructionSetFeatures() const;
+  uint32_t GetInstructionSetFeaturesBitmap() const;
   uint32_t GetImageFileLocationOatChecksum() const;
   uint32_t GetImageFileLocationOatDataBegin() const;
 
@@ -108,7 +108,7 @@
 
  private:
   OatHeader(InstructionSet instruction_set,
-            const InstructionSetFeatures& instruction_set_features,
+            const InstructionSetFeatures* instruction_set_features,
             const std::vector<const DexFile*>* dex_files,
             uint32_t image_file_location_oat_checksum,
             uint32_t image_file_location_oat_data_begin,
@@ -121,7 +121,7 @@
   uint32_t adler32_checksum_;
 
   InstructionSet instruction_set_;
-  InstructionSetFeatures instruction_set_features_;
+  uint32_t instruction_set_features_bitmap_;
   uint32_t dex_file_count_;
   uint32_t executable_offset_;
   uint32_t interpreter_to_interpreter_bridge_offset_;
@@ -154,7 +154,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-inl.h b/runtime/oat_file-inl.h
index 9570bb5..6237767 100644
--- a/runtime/oat_file-inl.h
+++ b/runtime/oat_file-inl.h
@@ -35,7 +35,7 @@
   if (method_header == nullptr) {
     return 0u;
   }
-  return reinterpret_cast<const byte*>(method_header) - begin_;
+  return reinterpret_cast<const uint8_t*>(method_header) - begin_;
 }
 
 inline uint32_t OatFile::OatMethod::GetQuickCodeSize() const {
@@ -51,7 +51,7 @@
   if (method_header == nullptr) {
     return 0u;
   }
-  return reinterpret_cast<const byte*>(&method_header->code_size_) - begin_;
+  return reinterpret_cast<const uint8_t*>(&method_header->code_size_) - begin_;
 }
 
 inline size_t OatFile::OatMethod::GetFrameSizeInBytes() const {
@@ -88,7 +88,7 @@
   if (method_header == nullptr) {
     return 0u;
   }
-  return reinterpret_cast<const byte*>(&method_header->mapping_table_offset_) - begin_;
+  return reinterpret_cast<const uint8_t*>(&method_header->mapping_table_offset_) - begin_;
 }
 
 inline uint32_t OatFile::OatMethod::GetVmapTableOffset() const {
@@ -101,7 +101,7 @@
   if (method_header == nullptr) {
     return 0u;
   }
-  return reinterpret_cast<const byte*>(&method_header->vmap_table_offset_) - begin_;
+  return reinterpret_cast<const uint8_t*>(&method_header->vmap_table_offset_) - begin_;
 }
 
 inline const uint8_t* OatFile::OatMethod::GetMappingTable() const {
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 9ba860c..54f5eab 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -47,9 +47,11 @@
                                   std::string* error_msg) {
   std::unique_ptr<OatFile> oat_file(new OatFile(location, false));
   oat_file->elf_file_.reset(elf_file);
-  Elf32_Shdr* hdr = elf_file->FindSectionByName(".rodata");
-  oat_file->begin_ = elf_file->Begin() + hdr->sh_offset;
-  oat_file->end_ = elf_file->Begin() + hdr->sh_size + hdr->sh_offset;
+  uint64_t offset, size;
+  bool has_section = elf_file->GetSectionOffsetAndSize(".rodata", &offset, &size);
+  CHECK(has_section);
+  oat_file->begin_ = elf_file->Begin() + offset;
+  oat_file->end_ = elf_file->Begin() + size + offset;
   return oat_file->Setup(error_msg) ? oat_file.release() : nullptr;
 }
 
@@ -66,7 +68,7 @@
 
 OatFile* OatFile::Open(const std::string& filename,
                        const std::string& location,
-                       byte* requested_base,
+                       uint8_t* requested_base,
                        uint8_t* oat_file_begin,
                        bool executable,
                        std::string* error_msg) {
@@ -114,7 +116,7 @@
 
 OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
                              const std::string& location,
-                             byte* requested_base,
+                             uint8_t* requested_base,
                              std::string* error_msg) {
   std::unique_ptr<OatFile> oat_file(new OatFile(location, true));
   bool success = oat_file->Dlopen(elf_filename, requested_base, error_msg);
@@ -126,7 +128,7 @@
 
 OatFile* OatFile::OpenElfFile(File* file,
                               const std::string& location,
-                              byte* requested_base,
+                              uint8_t* requested_base,
                               uint8_t* oat_file_begin,
                               bool writable,
                               bool executable,
@@ -155,7 +157,7 @@
   }
 }
 
-bool OatFile::Dlopen(const std::string& elf_filename, byte* requested_base,
+bool OatFile::Dlopen(const std::string& elf_filename, uint8_t* requested_base,
                      std::string* error_msg) {
   char* absolute_path = realpath(elf_filename.c_str(), NULL);
   if (absolute_path == NULL) {
@@ -168,7 +170,7 @@
     *error_msg = StringPrintf("Failed to dlopen '%s': %s", elf_filename.c_str(), dlerror());
     return false;
   }
-  begin_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_, "oatdata"));
+  begin_ = reinterpret_cast<uint8_t*>(dlsym(dlopen_handle_, "oatdata"));
   if (begin_ == NULL) {
     *error_msg = StringPrintf("Failed to find oatdata symbol in '%s': %s", elf_filename.c_str(),
                               dlerror());
@@ -181,7 +183,7 @@
     ReadFileToString("/proc/self/maps", error_msg);
     return false;
   }
-  end_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_, "oatlastword"));
+  end_ = reinterpret_cast<uint8_t*>(dlsym(dlopen_handle_, "oatlastword"));
   if (end_ == NULL) {
     *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s': %s", elf_filename.c_str(),
                               dlerror());
@@ -192,7 +194,7 @@
   return Setup(error_msg);
 }
 
-bool OatFile::ElfFileOpen(File* file, byte* requested_base, uint8_t* oat_file_begin,
+bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file_begin,
                           bool writable, bool executable,
                           std::string* error_msg) {
   // TODO: rename requested_base to oat_data_begin
@@ -234,7 +236,7 @@
     *error_msg = StringPrintf("Invalid oat magic for '%s'", GetLocation().c_str());
     return false;
   }
-  const byte* oat = Begin();
+  const uint8_t* oat = Begin();
   oat += sizeof(OatHeader);
   if (oat > End()) {
     *error_msg = StringPrintf("In oat file '%s' found truncated OatHeader", GetLocation().c_str());
@@ -300,7 +302,7 @@
     oat += sizeof(dex_file_offset);
     if (UNLIKELY(oat > End())) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated "
-                                " after dex file offsets", GetLocation().c_str(), i,
+                                "after dex file offsets", GetLocation().c_str(), i,
                                 dex_file_location.c_str());
       return false;
     }
@@ -308,13 +310,13 @@
     const uint8_t* dex_file_pointer = Begin() + dex_file_offset;
     if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "
-                                " dex file magic '%s'", GetLocation().c_str(), i,
+                                "dex file magic '%s'", GetLocation().c_str(), i,
                                 dex_file_location.c_str(), dex_file_pointer);
       return false;
     }
     if (UNLIKELY(!DexFile::IsVersionValid(dex_file_pointer))) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "
-                                " dex file version '%s'", GetLocation().c_str(), i,
+                                "dex file version '%s'", GetLocation().c_str(), i,
                                 dex_file_location.c_str(), dex_file_pointer);
       return false;
     }
@@ -324,7 +326,7 @@
     oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_);
     if (UNLIKELY(oat > End())) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated "
-                                " method offsets", GetLocation().c_str(), i,
+                                "method offsets", GetLocation().c_str(), i,
                                 dex_file_location.c_str());
       return false;
     }
@@ -355,12 +357,12 @@
   return *reinterpret_cast<const OatHeader*>(Begin());
 }
 
-const byte* OatFile::Begin() const {
+const uint8_t* OatFile::Begin() const {
   CHECK(begin_ != NULL);
   return begin_;
 }
 
-const byte* OatFile::End() const {
+const uint8_t* OatFile::End() const {
   CHECK(end_ != NULL);
   return end_;
 }
@@ -441,7 +443,7 @@
                                 const std::string& dex_file_location,
                                 const std::string& canonical_dex_file_location,
                                 uint32_t dex_file_location_checksum,
-                                const byte* dex_file_pointer,
+                                const uint8_t* dex_file_pointer,
                                 const uint32_t* oat_class_offsets_pointer)
     : oat_file_(oat_file),
       dex_file_location_(dex_file_location),
@@ -468,35 +470,37 @@
 OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const {
   uint32_t oat_class_offset = GetOatClassOffset(class_def_index);
 
-  const byte* oat_class_pointer = oat_file_->Begin() + oat_class_offset;
+  const uint8_t* oat_class_pointer = oat_file_->Begin() + oat_class_offset;
   CHECK_LT(oat_class_pointer, oat_file_->End()) << oat_file_->GetLocation();
 
-  const byte* status_pointer = oat_class_pointer;
+  const uint8_t* status_pointer = oat_class_pointer;
   CHECK_LT(status_pointer, oat_file_->End()) << oat_file_->GetLocation();
   mirror::Class::Status status =
       static_cast<mirror::Class::Status>(*reinterpret_cast<const int16_t*>(status_pointer));
   CHECK_LT(status, mirror::Class::kStatusMax);
 
-  const byte* type_pointer = status_pointer + sizeof(uint16_t);
+  const uint8_t* type_pointer = status_pointer + sizeof(uint16_t);
   CHECK_LT(type_pointer, oat_file_->End()) << oat_file_->GetLocation();
   OatClassType type = static_cast<OatClassType>(*reinterpret_cast<const uint16_t*>(type_pointer));
   CHECK_LT(type, kOatClassMax);
 
-  const byte* after_type_pointer = type_pointer + sizeof(int16_t);
+  const uint8_t* after_type_pointer = type_pointer + sizeof(int16_t);
   CHECK_LE(after_type_pointer, oat_file_->End()) << oat_file_->GetLocation();
 
   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;
+  const uint8_t* bitmap_pointer = nullptr;
+  const uint8_t* methods_pointer = nullptr;
+  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,
@@ -514,22 +518,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: {
@@ -587,23 +592,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.
 }
 
 bool OatFile::IsPic() const {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 488988e..2b94249 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -48,7 +48,7 @@
   // optionally be used to request where the file should be loaded.
   static OatFile* Open(const std::string& filename,
                        const std::string& location,
-                       byte* requested_base,
+                       uint8_t* requested_base,
                        uint8_t* oat_file_begin,
                        bool executable,
                        std::string* error_msg);
@@ -150,14 +150,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 uint8_t* 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>
@@ -168,10 +173,10 @@
       return reinterpret_cast<T>(begin_ + offset);
     }
 
-    const byte* begin_;
+    const uint8_t* 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;
   };
@@ -202,7 +207,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,
@@ -212,15 +222,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;
   };
@@ -265,14 +275,14 @@
                const std::string& dex_file_location,
                const std::string& canonical_dex_file_location,
                uint32_t dex_file_checksum,
-               const byte* dex_file_pointer,
+               const uint8_t* dex_file_pointer,
                const uint32_t* oat_class_offsets_pointer);
 
     const OatFile* const oat_file_;
     const std::string dex_file_location_;
     const std::string canonical_dex_file_location_;
     const uint32_t dex_file_location_checksum_;
-    const byte* const dex_file_pointer_;
+    const uint8_t* const dex_file_pointer_;
     const uint32_t* const oat_class_offsets_pointer_;
 
     friend class OatFile;
@@ -292,28 +302,28 @@
     return End() - Begin();
   }
 
-  const byte* Begin() const;
-  const byte* End() const;
+  const uint8_t* Begin() const;
+  const uint8_t* End() const;
 
  private:
   static void CheckLocation(const std::string& location);
 
   static OatFile* OpenDlopen(const std::string& elf_filename,
                              const std::string& location,
-                             byte* requested_base,
+                             uint8_t* requested_base,
                              std::string* error_msg);
 
   static OatFile* OpenElfFile(File* file,
                               const std::string& location,
-                              byte* requested_base,
+                              uint8_t* requested_base,
                               uint8_t* oat_file_begin,  // Override base if not null
                               bool writable,
                               bool executable,
                               std::string* error_msg);
 
   explicit OatFile(const std::string& filename, bool executable);
-  bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg);
-  bool ElfFileOpen(File* file, byte* requested_base,
+  bool Dlopen(const std::string& elf_filename, uint8_t* requested_base, std::string* error_msg);
+  bool ElfFileOpen(File* file, uint8_t* requested_base,
                    uint8_t* oat_file_begin,  // Override where the file is loaded to if not null
                    bool writable, bool executable,
                    std::string* error_msg);
@@ -325,10 +335,10 @@
   const std::string location_;
 
   // Pointer to OatHeader.
-  const byte* begin_;
+  const uint8_t* begin_;
 
   // Pointer to end of oat region for bounds checking.
-  const byte* end_;
+  const uint8_t* end_;
 
   // Was this oat_file loaded executable?
   const bool is_executable_;
diff --git a/runtime/offsets.cc b/runtime/offsets.cc
index 3691401..f59ed88 100644
--- a/runtime/offsets.cc
+++ b/runtime/offsets.cc
@@ -16,7 +16,7 @@
 
 #include "offsets.h"
 
-#include <iostream>  // NOLINT
+#include <ostream>
 
 namespace art {
 
diff --git a/runtime/offsets.h b/runtime/offsets.h
index 72a6b0f..9d5063f 100644
--- a/runtime/offsets.h
+++ b/runtime/offsets.h
@@ -17,7 +17,8 @@
 #ifndef ART_RUNTIME_OFFSETS_H_
 #define ART_RUNTIME_OFFSETS_H_
 
-#include <iostream>  // NOLINT
+#include <ostream>
+
 #include "globals.h"
 
 namespace art {
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index bb2ad44..1a97c35 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -16,6 +16,8 @@
 
 #include "parsed_options.h"
 
+#include <sstream>
+
 #ifdef HAVE_ANDROID_OS
 #include "cutils/properties.h"
 #endif
@@ -30,6 +32,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)) {
@@ -124,7 +207,7 @@
 
 bool ParsedOptions::ParseXGcOption(const std::string& option) {
   std::vector<std::string> gc_options;
-  Split(option.substr(strlen("-Xgc:")), ',', gc_options);
+  Split(option.substr(strlen("-Xgc:")), ',', &gc_options);
   for (const std::string& gc_option : gc_options) {
     gc::CollectorType collector_type = ParseCollectorType(gc_option);
     if (collector_type != gc::kCollectorTypeNone) {
@@ -175,82 +258,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 +276,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 +310,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 +456,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)) {
@@ -474,42 +503,39 @@
       is_explicit_gc_disabled_ = true;
     } else if (StartsWith(option, "-verbose:")) {
       std::vector<std::string> verbose_options;
-      Split(option.substr(strlen("-verbose:")), ',', verbose_options);
-      for (size_t i = 0; i < verbose_options.size(); ++i) {
-        if (verbose_options[i] == "class") {
+      Split(option.substr(strlen("-verbose:")), ',', &verbose_options);
+      for (size_t j = 0; j < verbose_options.size(); ++j) {
+        if (verbose_options[j] == "class") {
           gLogVerbosity.class_linker = true;
-        } else if (verbose_options[i] == "compiler") {
+        } else if (verbose_options[j] == "compiler") {
           gLogVerbosity.compiler = true;
-        } else if (verbose_options[i] == "gc") {
+        } else if (verbose_options[j] == "gc") {
           gLogVerbosity.gc = true;
-        } else if (verbose_options[i] == "heap") {
+        } else if (verbose_options[j] == "heap") {
           gLogVerbosity.heap = true;
-        } else if (verbose_options[i] == "jdwp") {
+        } else if (verbose_options[j] == "jdwp") {
           gLogVerbosity.jdwp = true;
-        } else if (verbose_options[i] == "jni") {
+        } else if (verbose_options[j] == "jni") {
           gLogVerbosity.jni = true;
-        } else if (verbose_options[i] == "monitor") {
+        } else if (verbose_options[j] == "monitor") {
           gLogVerbosity.monitor = true;
-        } else if (verbose_options[i] == "profiler") {
+        } else if (verbose_options[j] == "profiler") {
           gLogVerbosity.profiler = true;
-        } else if (verbose_options[i] == "signals") {
+        } else if (verbose_options[j] == "signals") {
           gLogVerbosity.signals = true;
-        } else if (verbose_options[i] == "startup") {
+        } else if (verbose_options[j] == "startup") {
           gLogVerbosity.startup = true;
-        } else if (verbose_options[i] == "third-party-jni") {
+        } else if (verbose_options[j] == "third-party-jni") {
           gLogVerbosity.third_party_jni = true;
-        } else if (verbose_options[i] == "threads") {
+        } else if (verbose_options[j] == "threads") {
           gLogVerbosity.threads = true;
-        } else if (verbose_options[i] == "verifier") {
+        } else if (verbose_options[j] == "verifier") {
           gLogVerbosity.verifier = true;
         } else {
-          Usage("Unknown -verbose option %s\n", verbose_options[i].c_str());
+          Usage("Unknown -verbose option %s\n", verbose_options[j].c_str());
           return false;
         }
       }
-    } else if (StartsWith(option, "-verbose-methods:")) {
-      gLogVerbosity.compiler = false;
-      Split(option.substr(strlen("-verbose-methods:")), ',', gVerboseMethods);
     } else if (StartsWith(option, "-Xlockprofthreshold:")) {
       if (!ParseUnsignedInteger(option, ':', &lock_profiling_threshold_)) {
         return false;
@@ -642,7 +668,6 @@
                StartsWith(option, "-Xint:") ||
                StartsWith(option, "-Xdexopt:") ||
                (option == "-Xnoquithandler") ||
-               StartsWith(option, "-Xjniopts:") ||
                StartsWith(option, "-Xjnigreflimit:") ||
                (option == "-Xgenregmap") ||
                (option == "-Xnogenregmap") ||
@@ -716,7 +741,7 @@
 }
 
 void ParsedOptions::UsageMessageV(FILE* stream, const char* fmt, va_list ap) {
-  hook_vfprintf_(stderr, fmt, ap);
+  hook_vfprintf_(stream, fmt, ap);
 }
 
 void ParsedOptions::UsageMessage(FILE* stream, const char* fmt, ...) {
@@ -765,7 +790,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 +806,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 +816,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 +931,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/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 5154d69..61481b1 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -64,7 +64,7 @@
   EXPECT_EQ(2048U, parsed->heap_initial_size_);
   EXPECT_EQ(4 * KB, parsed->heap_maximum_size_);
   EXPECT_EQ(1 * MB, parsed->stack_size_);
-  EXPECT_EQ(0.75, parsed->heap_target_utilization_);
+  EXPECT_DOUBLE_EQ(0.75, parsed->heap_target_utilization_);
   EXPECT_TRUE(test_vfprintf == parsed->hook_vfprintf_);
   EXPECT_TRUE(test_exit == parsed->hook_exit_);
   EXPECT_TRUE(test_abort == parsed->hook_abort_);
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..e399195 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -16,9 +16,11 @@
 
 #include "profiler.h"
 
-#include <fstream>
-#include <sys/uio.h>
 #include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include <fstream>
 
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
@@ -95,7 +97,7 @@
   switch (profile_options.GetProfileType()) {
     case kProfilerMethod: {
       mirror::ArtMethod* method = thread->GetCurrentMethod(nullptr);
-      if (false && method == nullptr) {
+      if ((false) && method == nullptr) {
         LOG(INFO) << "No current method available";
         std::ostringstream os;
         thread->Dump(os);
@@ -119,12 +121,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 +194,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 +241,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();
@@ -745,7 +744,7 @@
     return;
   }
   std::vector<std::string> summary_info;
-  Split(line, '/', summary_info);
+  Split(line, '/', &summary_info);
   if (summary_info.size() != 3) {
     // Bad summary info.  It should be count/nullcount/bootcount
     return;
@@ -760,7 +759,7 @@
       break;
     }
     std::vector<std::string> info;
-    Split(line, '/', info);
+    Split(line, '/', &info);
     if (info.size() != 3 && info.size() != 4) {
       // Malformed.
       break;
@@ -773,10 +772,10 @@
       context_map = new PreviousContextMap();
       std::string context_counts_str = info[3].substr(1, info[3].size() - 2);
       std::vector<std::string> context_count_pairs;
-      Split(context_counts_str, '#', context_count_pairs);
+      Split(context_counts_str, '#', &context_count_pairs);
       for (uint32_t i = 0; i < context_count_pairs.size(); ++i) {
         std::vector<std::string> context_count;
-        Split(context_count_pairs[i], ':', context_count);
+        Split(context_count_pairs[i], ':', &context_count);
         if (context_count.size() == 2) {
           // Handles the situtation when the profile file doesn't contain context information.
           uint32_t dexpc = strtoul(context_count[0].c_str(), nullptr, 10);
@@ -822,7 +821,7 @@
     return false;
   }
   std::vector<std::string> summary_info;
-  Split(line, '/', summary_info);
+  Split(line, '/', &summary_info);
   if (summary_info.size() != 3) {
     // Bad summary info.  It should be total/null/boot.
     return false;
@@ -840,7 +839,7 @@
       break;
     }
     std::vector<std::string> info;
-    Split(line, '/', info);
+    Split(line, '/', &info);
     if (info.size() != 3 && info.size() != 4) {
       // Malformed.
       return false;
diff --git a/runtime/profiler_options.h b/runtime/profiler_options.h
index e3ef697..1db2f05 100644
--- a/runtime/profiler_options.h
+++ b/runtime/profiler_options.h
@@ -26,6 +26,7 @@
   kProfilerMethod,          // Method only
   kProfilerBoundedStack,    // Methods with Dex PC on top of the stack
 };
+std::ostream& operator<<(std::ostream& os, const ProfileDataType& rhs);
 
 class ProfilerOptions {
  public:
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/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index d8fc277..3415e8f 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -35,50 +35,38 @@
 
 namespace art {
 
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET),
-               check_iget_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE),
-               check_iget_wide_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT),
-               check_iget_object_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BOOLEAN),
-               check_iget_boolean_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BYTE),
-               check_iget_byte_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_CHAR),
-               check_iget_char_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_SHORT),
-               check_iget_short_type);
-
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT),
-               check_iput_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_WIDE),
-               check_iput_wide_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_OBJECT),
-               check_iput_object_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BOOLEAN),
-               check_iput_boolean_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BYTE),
-               check_iput_byte_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_CHAR),
-               check_iput_char_type);
-COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_SHORT),
-               check_iput_short_type);
-
-COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET) ==
-    InlineMethodAnalyser::IPutVariant(Instruction::IPUT), check_iget_iput_variant);
-COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE) ==
-    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE), check_iget_iput_wide_variant);
-COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT) ==
-    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT), check_iget_iput_object_variant);
-COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN) ==
-    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN), check_iget_iput_boolean_variant);
-COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE) ==
-    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE), check_iget_iput_byte_variant);
-COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR) ==
-    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR), check_iget_iput_char_variant);
-COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
-    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), check_iget_iput_short_variant);
+static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET), "iget type");
+static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE), "iget_wide type");
+static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT),
+              "iget_object type");
+static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BOOLEAN),
+              "iget_boolean type");
+static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BYTE), "iget_byte type");
+static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_CHAR), "iget_char type");
+static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_SHORT), "iget_short type");
+static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT), "iput type");
+static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_WIDE), "iput_wide type");
+static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_OBJECT),
+              "iput_object type");
+static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BOOLEAN),
+              "iput_boolean type");
+static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BYTE), "iput_byte type");
+static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_CHAR), "iput_char type");
+static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_SHORT), "iput_short type");
+static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT), "iget/iput variant");
+static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE), "iget/iput_wide variant");
+static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT), "iget/iput_object variant");
+static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN), "iget/iput_boolean variant");
+static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE), "iget/iput_byte variant");
+static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR), "iget/iput_char variant");
+static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant");
 
 // This is used by compiler and debugger. We look into the dex cache for resolved methods and
 // fields. However, in the context of the debugger, not all methods and fields are resolved. Since
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index a2ae397..a8d4308 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -118,7 +118,7 @@
   uint32_t is_volatile : 1;
   uint32_t field_offset : 31;
 };
-COMPILE_ASSERT(sizeof(InlineIGetIPutData) == sizeof(uint64_t), InvalidSizeOfInlineIGetIPutData);
+static_assert(sizeof(InlineIGetIPutData) == sizeof(uint64_t), "Invalid size of InlineIGetIPutData");
 
 struct InlineReturnArgData {
   uint16_t arg;
@@ -127,7 +127,8 @@
   uint16_t reserved : 14;
   uint32_t reserved2;
 };
-COMPILE_ASSERT(sizeof(InlineReturnArgData) == sizeof(uint64_t), InvalidSizeOfInlineReturnArgData);
+static_assert(sizeof(InlineReturnArgData) == sizeof(uint64_t),
+              "Invalid size of InlineReturnArgData");
 
 struct InlineMethod {
   InlineMethodOpcode opcode;
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index d4713a0..90c9fe7 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -19,6 +19,7 @@
 #include "arch/context.h"
 #include "dex_instruction.h"
 #include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "handle_scope-inl.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
@@ -96,7 +97,7 @@
       if (found_dex_pc != DexFile::kDexNoIndex) {
         exception_handler_->SetHandlerMethod(method.Get());
         exception_handler_->SetHandlerDexPc(found_dex_pc);
-        exception_handler_->SetHandlerQuickFramePc(method->ToNativePc(found_dex_pc));
+        exception_handler_->SetHandlerQuickFramePc(method->ToNativeQuickPc(found_dex_pc));
         exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
         return false;  // End stack walk.
       }
@@ -206,13 +207,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) {
@@ -295,10 +297,10 @@
 // Unwinds all instrumentation stack frame prior to catch handler or upcall.
 class InstrumentationStackVisitor : public StackVisitor {
  public:
-  InstrumentationStackVisitor(Thread* self, bool is_deoptimization, size_t frame_depth)
+  InstrumentationStackVisitor(Thread* self, size_t frame_depth)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       : StackVisitor(self, nullptr),
-        self_(self), frame_depth_(frame_depth),
+        frame_depth_(frame_depth),
         instrumentation_frames_to_pop_(0) {
     CHECK_NE(frame_depth_, kInvalidFrameDepth);
   }
@@ -307,7 +309,7 @@
     size_t current_frame_depth = GetFrameDepth();
     if (current_frame_depth < frame_depth_) {
       CHECK(GetMethod() != nullptr);
-      if (UNLIKELY(GetQuickInstrumentationExitPc() == GetReturnPc())) {
+      if (UNLIKELY(reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == GetReturnPc())) {
         ++instrumentation_frames_to_pop_;
       }
       return true;
@@ -322,7 +324,6 @@
   }
 
  private:
-  Thread* const self_;
   const size_t frame_depth_;
   size_t instrumentation_frames_to_pop_;
 
@@ -331,7 +332,7 @@
 
 void QuickExceptionHandler::UpdateInstrumentationStack() {
   if (method_tracing_active_) {
-    InstrumentationStackVisitor visitor(self_, is_deoptimization_, handler_frame_depth_);
+    InstrumentationStackVisitor visitor(self_, handler_frame_depth_);
     visitor.WalkStack(true);
 
     size_t instrumentation_frames_to_pop = visitor.GetInstrumentationFramesToPop();
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index b93769c..cf1ecbf 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -40,6 +40,7 @@
 
   ~QuickExceptionHandler() {
     LOG(FATAL) << "UNREACHABLE";  // Expected to take long jump.
+    UNREACHABLE();
   }
 
   void FindCatch(const ThrowLocation& throw_location, mirror::Throwable* exception,
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index fd43d78..0dc31e7 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -27,9 +27,7 @@
 inline MirrorType* ReadBarrier::Barrier(
     mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) {
   // Unused for now.
-  UNUSED(obj);
-  UNUSED(offset);
-  UNUSED(ref_addr);
+  UNUSED(obj, offset, ref_addr);
   const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
   if (with_read_barrier && kUseBakerReadBarrier) {
     // To be implemented.
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..44d1bc4 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -19,6 +19,7 @@
 #include "class_linker.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
+#include "entrypoints/entrypoint_utils.h"
 #include "jni_internal.h"
 #include "method_helper-inl.h"
 #include "mirror/art_field-inl.h"
@@ -218,8 +219,7 @@
                      PrettyDescriptor(found_descriptor).c_str()).c_str());
   }
 
-  bool BuildArgArrayFromObjectArray(const ScopedObjectAccessAlreadyRunnable& soa,
-                                    mirror::Object* receiver,
+  bool BuildArgArrayFromObjectArray(mirror::Object* receiver,
                                     mirror::ObjectArray<mirror::Object>* args, MethodHelper& mh)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     const DexFile::TypeList* classes = mh.GetMethod()->GetParameterTypeList();
@@ -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);
 }
@@ -501,7 +528,7 @@
 }
 
 void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset,
-                           MethodHelper& mh, JValue* result) {
+                           MethodHelper* mh, JValue* result) {
   // We want to make sure that the stack is not within a small distance from the
   // protected region in case we are calling into a leaf function whose stack
   // check has been elided.
@@ -510,10 +537,10 @@
     return;
   }
 
-  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  ArgArray arg_array(mh->GetShorty(), mh->GetShortyLength());
   arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset);
   shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result,
-                                    mh.GetShorty());
+                                    mh->GetShorty());
 }
 
 jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod,
@@ -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;
   }
 
@@ -578,7 +612,7 @@
   ArgArray arg_array(shorty, shorty_len);
   StackHandleScope<1> hs(soa.Self());
   MethodHelper mh(hs.NewHandle(m));
-  if (!arg_array.BuildArgArrayFromObjectArray(soa, receiver, objects, mh)) {
+  if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, mh)) {
     CHECK(soa.Self()->IsExceptionPending());
     return nullptr;
   }
@@ -597,8 +631,8 @@
   }
 
   // Box if necessary and return.
-  return soa.AddLocalReference<jobject>(BoxPrimitive(mh.GetReturnType()->GetPrimitiveType(),
-                                                     result));
+  return soa.AddLocalReference<jobject>(
+      BoxPrimitive(Primitive::GetType(mh.GetMethod()->GetReturnTypeDescriptor()[0]), result));
 }
 
 bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) {
@@ -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..f9a7951 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"
 
@@ -64,7 +65,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset,
-                           MethodHelper& mh, JValue* result)
+                           MethodHelper* mh, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject method, jobject receiver,
@@ -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..eca1800 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -115,9 +115,9 @@
       *receiver = nullptr;
     } else {
       // 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);
+      StackHandleScope<1> hs2(self);
+      Handle<mirror::Class> h_class(hs2.NewHandle(c));
+      bool initialized = class_linker_->EnsureInitialized(self, h_class, true, true);
       CHECK(initialized);
       *receiver = c->AllocObject(self);
     }
@@ -193,19 +193,19 @@
 
     args[0].d = 0.0;
     JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(0.0, result.GetD());
+    EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = -1.0;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(-1.0, result.GetD());
+    EXPECT_DOUBLE_EQ(-1.0, result.GetD());
 
     args[0].d = DBL_MAX;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(DBL_MAX, result.GetD());
+    EXPECT_DOUBLE_EQ(DBL_MAX, result.GetD());
 
     args[0].d = DBL_MIN;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(DBL_MIN, result.GetD());
+    EXPECT_DOUBLE_EQ(DBL_MIN, result.GetD());
   }
 
   void InvokeSumIntIntMethod(bool is_static) {
@@ -375,27 +375,27 @@
     args[0].d = 0.0;
     args[1].d = 0.0;
     JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(0.0, result.GetD());
+    EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = 2.0;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(3.0, result.GetD());
+    EXPECT_DOUBLE_EQ(3.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = -2.0;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(-1.0, result.GetD());
+    EXPECT_DOUBLE_EQ(-1.0, result.GetD());
 
     args[0].d = DBL_MAX;
     args[1].d = DBL_MIN;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(1.7976931348623157e308, result.GetD());
+    EXPECT_DOUBLE_EQ(1.7976931348623157e308, result.GetD());
 
     args[0].d = DBL_MAX;
     args[1].d = DBL_MAX;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(INFINITY, result.GetD());
+    EXPECT_DOUBLE_EQ(INFINITY, result.GetD());
   }
 
   void InvokeSumDoubleDoubleDoubleMethod(bool is_static) {
@@ -409,19 +409,19 @@
     args[1].d = 0.0;
     args[2].d = 0.0;
     JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(0.0, result.GetD());
+    EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = 2.0;
     args[2].d = 3.0;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(6.0, result.GetD());
+    EXPECT_DOUBLE_EQ(6.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = -2.0;
     args[2].d = 3.0;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(2.0, result.GetD());
+    EXPECT_DOUBLE_EQ(2.0, result.GetD());
   }
 
   void InvokeSumDoubleDoubleDoubleDoubleMethod(bool is_static) {
@@ -436,21 +436,21 @@
     args[2].d = 0.0;
     args[3].d = 0.0;
     JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(0.0, result.GetD());
+    EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = 2.0;
     args[2].d = 3.0;
     args[3].d = 4.0;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(10.0, result.GetD());
+    EXPECT_DOUBLE_EQ(10.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = -2.0;
     args[2].d = 3.0;
     args[3].d = -4.0;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(-2.0, result.GetD());
+    EXPECT_DOUBLE_EQ(-2.0, result.GetD());
   }
 
   void InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(bool is_static) {
@@ -466,7 +466,7 @@
     args[3].d = 0.0;
     args[4].d = 0.0;
     JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(0.0, result.GetD());
+    EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = 2.0;
@@ -474,7 +474,7 @@
     args[3].d = 4.0;
     args[4].d = 5.0;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(15.0, result.GetD());
+    EXPECT_DOUBLE_EQ(15.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = -2.0;
@@ -482,7 +482,7 @@
     args[3].d = -4.0;
     args[4].d = 5.0;
     result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
-    EXPECT_EQ(3.0, result.GetD());
+    EXPECT_DOUBLE_EQ(3.0, result.GetD());
   }
 
   JavaVMExt* vm_;
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index 001598f..cdf8d54 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 053769a..d338ad7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -43,10 +43,14 @@
 #include "arch/x86/registers_x86.h"
 #include "arch/x86_64/quick_method_frame_info_x86_64.h"
 #include "arch/x86_64/registers_x86_64.h"
+#include "asm_support.h"
 #include "atomic.h"
+#include "base/dumpable.h"
+#include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "debugger.h"
 #include "elf_file.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "fault_handler.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/heap.h"
@@ -65,6 +69,31 @@
 #include "mirror/throwable.h"
 #include "monitor.h"
 #include "native_bridge_art_interface.h"
+#include "native/dalvik_system_DexFile.h"
+#include "native/dalvik_system_VMDebug.h"
+#include "native/dalvik_system_VMRuntime.h"
+#include "native/dalvik_system_VMStack.h"
+#include "native/dalvik_system_ZygoteHooks.h"
+#include "native/java_lang_Class.h"
+#include "native/java_lang_DexCache.h"
+#include "native/java_lang_Object.h"
+#include "native/java_lang_ref_FinalizerReference.h"
+#include "native/java_lang_reflect_Array.h"
+#include "native/java_lang_reflect_Constructor.h"
+#include "native/java_lang_reflect_Field.h"
+#include "native/java_lang_reflect_Method.h"
+#include "native/java_lang_reflect_Proxy.h"
+#include "native/java_lang_ref_Reference.h"
+#include "native/java_lang_Runtime.h"
+#include "native/java_lang_String.h"
+#include "native/java_lang_System.h"
+#include "native/java_lang_Thread.h"
+#include "native/java_lang_Throwable.h"
+#include "native/java_lang_VMClassLoader.h"
+#include "native/java_util_concurrent_atomic_AtomicLong.h"
+#include "native/org_apache_harmony_dalvik_ddmc_DdmServer.h"
+#include "native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
+#include "native/sun_misc_Unsafe.h"
 #include "parsed_options.h"
 #include "oat_file.h"
 #include "os.h"
@@ -92,10 +121,9 @@
 
 namespace art {
 
+// If a signal isn't handled properly, enable a handler that attempts to dump the Java stack.
 static constexpr bool kEnableJavaStackTraceHandler = false;
-const char* Runtime::kDefaultInstructionSetFeatures =
-    STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES);
-Runtime* Runtime::instance_ = NULL;
+Runtime* Runtime::instance_ = nullptr;
 
 Runtime::Runtime()
     : instruction_set_(kNone),
@@ -140,15 +168,13 @@
       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),
       implicit_so_checks_(false),
       implicit_suspend_checks_(false),
       is_native_bridge_loaded_(false) {
+  CheckAsmSupportOffsetsAndSizes();
 }
 
 Runtime::~Runtime() {
@@ -205,14 +231,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) const {
     if (gAborting > 1) {
       os << "Runtime aborting --- recursively, so no thread-specific detail!\n";
       return;
@@ -242,7 +264,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) const NO_THREAD_SAFETY_ANALYSIS {
+    DCHECK(Locks::mutator_lock_->IsExclusiveHeld(self) || Locks::mutator_lock_->IsSharedHeld(self));
     self->Dump(os);
     if (self->IsExceptionPending()) {
       ThrowLocation throw_location;
@@ -253,7 +277,7 @@
     }
   }
 
-  void DumpAllThreads(std::ostream& os, Thread* self) NO_THREAD_SAFETY_ANALYSIS {
+  void DumpAllThreads(std::ostream& os, Thread* self) const {
     Runtime* runtime = Runtime::Current();
     if (runtime != nullptr) {
       ThreadList* thread_list = runtime->GetThreadList();
@@ -267,7 +291,7 @@
               << "\n";
         }
         os << "All threads:\n";
-        thread_list->DumpLocked(os);
+        thread_list->Dump(os);
       }
     }
   }
@@ -345,7 +369,7 @@
   return true;
 }
 
-jobject CreateSystemClassLoader() {
+static jobject CreateSystemClassLoader() {
   if (Runtime::Current()->UseCompileTimeClassPath()) {
     return NULL;
   }
@@ -353,36 +377,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());
 }
@@ -391,9 +413,9 @@
   if (!patchoat_executable_.empty()) {
     return patchoat_executable_;
   }
-  std::string patchoat_executable_(GetAndroidRoot());
-  patchoat_executable_ += (kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat");
-  return patchoat_executable_;
+  std::string patchoat_executable(GetAndroidRoot());
+  patchoat_executable += (kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat");
+  return patchoat_executable;
 }
 
 std::string Runtime::GetCompilerExecutable() const {
@@ -425,7 +447,7 @@
     ScopedObjectAccess soa(self);
     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
@@ -580,8 +602,7 @@
   VLOG(startup) << "Runtime::StartDaemonThreads exiting";
 }
 
-static bool OpenDexFilesFromImage(const std::vector<std::string>& dex_filenames,
-                                  const std::string& image_location,
+static bool OpenDexFilesFromImage(const std::string& image_location,
                                   std::vector<const DexFile*>& dex_files,
                                   size_t* failures) {
   std::string system_filename;
@@ -643,8 +664,7 @@
                            const std::string& image_location,
                            std::vector<const DexFile*>& dex_files) {
   size_t failure_count = 0;
-  if (!image_location.empty() && OpenDexFilesFromImage(dex_filenames, image_location, dex_files,
-                                                       &failure_count)) {
+  if (!image_location.empty() && OpenDexFilesFromImage(image_location, dex_files, &failure_count)) {
     return failure_count;
   }
   failure_count = 0;
@@ -728,6 +748,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_,
@@ -757,7 +779,8 @@
     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.
@@ -774,16 +797,19 @@
 
     // 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) {
@@ -817,7 +843,7 @@
     }
   } else if (!IsCompiler() || !image_dex2oat_enabled_) {
     std::vector<std::string> dex_filenames;
-    Split(boot_class_path_string_, ':', dex_filenames);
+    Split(boot_class_path_string_, ':', &dex_filenames);
     std::vector<const DexFile*> boot_class_path;
     OpenDexFiles(dex_filenames, options->image_, boot_class_path);
     class_linker_->InitWithoutImage(boot_class_path);
@@ -826,7 +852,7 @@
     for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
       Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
       if (!HasCalleeSaveMethod(type)) {
-        SetCalleeSaveMethod(CreateCalleeSaveMethod(type), type);
+        SetCalleeSaveMethod(CreateCalleeSaveMethod(), type);
       }
     }
   } else {
@@ -835,6 +861,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_;
@@ -856,14 +887,14 @@
   // Pre-allocate an OutOfMemoryError for the double-OOME case.
   self->ThrowNewException(ThrowLocation(), "Ljava/lang/OutOfMemoryError;",
                           "OutOfMemoryError thrown while trying to throw OutOfMemoryError; "
-                          "no stack available");
+                          "no stack trace available");
   pre_allocated_OutOfMemoryError_ = GcRoot<mirror::Throwable>(self->GetException(NULL));
   self->ClearException();
 
   // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class
   // ahead of checking the application's class loader.
   self->ThrowNewException(ThrowLocation(), "Ljava/lang/NoClassDefFoundError;",
-                          "Class not found using the boot class loader; no stack available");
+                          "Class not found using the boot class loader; no stack trace available");
   pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException(NULL));
   self->ClearException();
 
@@ -922,13 +953,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 (!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.
@@ -968,34 +995,31 @@
 }
 
 void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
-#define REGISTER(FN) extern void FN(JNIEnv*); FN(env)
-  // Register Throwable first so that registration of other native methods can throw exceptions
-  REGISTER(register_java_lang_Throwable);
-  REGISTER(register_dalvik_system_DexFile);
-  REGISTER(register_dalvik_system_VMDebug);
-  REGISTER(register_dalvik_system_VMRuntime);
-  REGISTER(register_dalvik_system_VMStack);
-  REGISTER(register_dalvik_system_ZygoteHooks);
-  REGISTER(register_java_lang_Class);
-  REGISTER(register_java_lang_DexCache);
-  REGISTER(register_java_lang_Object);
-  REGISTER(register_java_lang_Runtime);
-  REGISTER(register_java_lang_String);
-  REGISTER(register_java_lang_System);
-  REGISTER(register_java_lang_Thread);
-  REGISTER(register_java_lang_VMClassLoader);
-  REGISTER(register_java_lang_ref_FinalizerReference);
-  REGISTER(register_java_lang_ref_Reference);
-  REGISTER(register_java_lang_reflect_Array);
-  REGISTER(register_java_lang_reflect_Constructor);
-  REGISTER(register_java_lang_reflect_Field);
-  REGISTER(register_java_lang_reflect_Method);
-  REGISTER(register_java_lang_reflect_Proxy);
-  REGISTER(register_java_util_concurrent_atomic_AtomicLong);
-  REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmServer);
-  REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmVmInternal);
-  REGISTER(register_sun_misc_Unsafe);
-#undef REGISTER
+  register_dalvik_system_DexFile(env);
+  register_dalvik_system_VMDebug(env);
+  register_dalvik_system_VMRuntime(env);
+  register_dalvik_system_VMStack(env);
+  register_dalvik_system_ZygoteHooks(env);
+  register_java_lang_Class(env);
+  register_java_lang_DexCache(env);
+  register_java_lang_Object(env);
+  register_java_lang_ref_FinalizerReference(env);
+  register_java_lang_reflect_Array(env);
+  register_java_lang_reflect_Constructor(env);
+  register_java_lang_reflect_Field(env);
+  register_java_lang_reflect_Method(env);
+  register_java_lang_reflect_Proxy(env);
+  register_java_lang_ref_Reference(env);
+  register_java_lang_Runtime(env);
+  register_java_lang_String(env);
+  register_java_lang_System(env);
+  register_java_lang_Thread(env);
+  register_java_lang_Throwable(env);
+  register_java_lang_VMClassLoader(env);
+  register_java_util_concurrent_atomic_AtomicLong(env);
+  register_org_apache_harmony_dalvik_ddmc_DdmServer(env);
+  register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(env);
+  register_sun_misc_Unsafe(env);
 }
 
 void Runtime::DumpForSigQuit(std::ostream& os) {
@@ -1154,6 +1178,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());
@@ -1224,15 +1252,11 @@
   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());
+    method->SetEntryPointFromPortableCompiledCode(GetPortableImtConflictStub());
+    method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub());
   }
   return method.Get();
 }
@@ -1248,20 +1272,16 @@
   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());
+    method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionStub());
+    method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
   }
   return method.Get();
 }
 
-mirror::ArtMethod* Runtime::CreateCalleeSaveMethod(CalleeSaveType type) {
+mirror::ArtMethod* Runtime::CreateCalleeSaveMethod() {
   Thread* self = Thread::Current();
   Runtime* runtime = Runtime::Current();
   ClassLinker* class_linker = runtime->GetClassLinker();
@@ -1270,9 +1290,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();
@@ -1378,6 +1396,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());
@@ -1447,9 +1493,10 @@
   instruction_set += GetInstructionSetString(kRuntimeISA);
   argv->push_back(instruction_set);
 
-  std::string features("--instruction-set-features=");
-  features += GetDefaultInstructionSetFeatures();
-  argv->push_back(features);
+  std::unique_ptr<const InstructionSetFeatures> features(InstructionSetFeatures::FromCppDefines());
+  std::string feature_string("--instruction-set-features=");
+  feature_string += features->GetFeatureString();
+  argv->push_back(feature_string);
 }
 
 void Runtime::UpdateProfilerState(int state) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 60cb529..3cbe1e5 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -55,8 +55,8 @@
   class Throwable;
 }  // namespace mirror
 namespace verifier {
-class MethodVerifier;
-}
+  class MethodVerifier;
+}  // namespace verifier
 class ClassLinker;
 class DexFile;
 class InternTable;
@@ -177,10 +177,7 @@
 
   // Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most
   // callers should prefer.
-  // This isn't marked ((noreturn)) because then gcc will merge multiple calls
-  // in a single function together. This reduces code size slightly, but means
-  // that the native stack trace we get may point at the wrong call site.
-  static void Abort() LOCKS_EXCLUDED(Locks::abort_lock_);
+  [[noreturn]] static void Abort() LOCKS_EXCLUDED(Locks::abort_lock_);
 
   // Returns the "main" ThreadGroup, used when attaching user threads.
   jobject GetMainThreadGroup() const;
@@ -200,8 +197,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 +243,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()
@@ -377,8 +379,7 @@
 
   void SetCalleeSaveMethod(mirror::ArtMethod* method, CalleeSaveType type);
 
-  mirror::ArtMethod* CreateCalleeSaveMethod(CalleeSaveType type)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::ArtMethod* CreateCalleeSaveMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   int32_t GetStat(int kind);
 
@@ -431,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,
@@ -457,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 {
@@ -485,10 +486,6 @@
     return target_sdk_version_;
   }
 
-  static const char* GetDefaultInstructionSetFeatures() {
-    return kDefaultInstructionSetFeatures;
-  }
-
  private:
   static void InitPlatformSignalHandlers();
 
@@ -508,8 +505,6 @@
   // A pointer to the active runtime or NULL.
   static Runtime* instance_;
 
-  static const char* kDefaultInstructionSetFeatures;
-
   // NOTE: these must match the gc::ProcessState values as they come directly from the framework.
   static constexpr int kProfileForground = 0;
   static constexpr int kProfileBackgrouud = 1;
@@ -524,6 +519,10 @@
   GcRoot<mirror::ArtMethod> imt_unimplemented_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];
 
@@ -630,9 +629,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_;
@@ -658,6 +654,7 @@
 
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };
+std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
 
 }  // namespace art
 
diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc
index 079d7e5..33600dd 100644
--- a/runtime/runtime_android.cc
+++ b/runtime/runtime_android.cc
@@ -32,13 +32,12 @@
 
 struct sigaction old_action;
 void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
-  static bool handlingUnexpectedSignal = false;
-  if (handlingUnexpectedSignal) {
-    LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1);
-    LogMessage::LogLine(data, "HandleUnexpectedSignal reentered\n");
+  static bool handling_unexpected_signal = false;
+  if (handling_unexpected_signal) {
+    LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, "HandleUnexpectedSignal reentered\n");
     _exit(1);
   }
-  handlingUnexpectedSignal = true;
+  handling_unexpected_signal = true;
   gAborting++;  // set before taking any locks
   MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_);
 
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 46ee274..1de035c 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -21,6 +21,9 @@
 #include <sys/utsname.h>
 #include <inttypes.h>
 
+#include <sstream>
+
+#include "base/dumpable.h"
 #include "base/logging.h"
 #include "base/mutex.h"
 #include "base/stringprintf.h"
@@ -32,13 +35,13 @@
 static constexpr bool kDumpHeapObjectOnSigsevg = false;
 
 struct Backtrace {
-  void Dump(std::ostream& os) {
+  void Dump(std::ostream& os) const {
     DumpNativeStack(os, GetTid(), "\t");
   }
 };
 
 struct OsInfo {
-  void Dump(std::ostream& os) {
+  void Dump(std::ostream& os) const {
     utsname info;
     uname(&info);
     // Linux 2.6.38.8-gg784 (x86_64)
@@ -132,9 +135,11 @@
 }
 
 struct UContext {
-  explicit UContext(void* raw_context) : context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) {}
+  explicit UContext(void* raw_context) :
+      context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) {
+  }
 
-  void Dump(std::ostream& os) {
+  void Dump(std::ostream& os) const {
     // TODO: support non-x86 hosts (not urgent because this code doesn't run on targets).
 #if defined(__APPLE__) && defined(__i386__)
     DumpRegister32(os, "eax", context->__ss.__eax);
@@ -228,15 +233,15 @@
 #endif
   }
 
-  void DumpRegister32(std::ostream& os, const char* name, uint32_t value) {
+  void DumpRegister32(std::ostream& os, const char* name, uint32_t value) const {
     os << StringPrintf(" %6s: 0x%08x", name, value);
   }
 
-  void DumpRegister64(std::ostream& os, const char* name, uint64_t value) {
+  void DumpRegister64(std::ostream& os, const char* name, uint64_t value) const {
     os << StringPrintf(" %6s: 0x%016" PRIx64, name, value);
   }
 
-  void DumpX86Flags(std::ostream& os, uint32_t flags) {
+  void DumpX86Flags(std::ostream& os, uint32_t flags) const {
     os << " [";
     if ((flags & (1 << 0)) != 0) {
       os << " CF";
@@ -274,8 +279,7 @@
 void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
   static bool handlingUnexpectedSignal = false;
   if (handlingUnexpectedSignal) {
-    LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1);
-    LogMessage::LogLine(data, "HandleUnexpectedSignal reentered\n");
+    LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, "HandleUnexpectedSignal reentered\n");
     _exit(1);
   }
   handlingUnexpectedSignal = true;
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..d4ec803 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -25,6 +25,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <sstream>
+
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "gc/heap.h"
@@ -118,17 +120,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";
@@ -142,21 +133,13 @@
 
   runtime->DumpForSigQuit(os);
 
-  if (false) {
+  if ((false)) {
     std::string maps;
     if (ReadFileToString("/proc/self/maps", &maps)) {
       os << "/proc/self/maps:\n" << maps;
     }
   }
   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..4408609 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -18,6 +18,7 @@
 
 #include "arch/context.h"
 #include "base/hex_dump.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object.h"
@@ -115,18 +116,23 @@
 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());
+          reinterpret_cast<char*>(cur_quick_frame_) + m->GetHandleScopeOffset().SizeValue());
       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.
+    UNIMPLEMENTED(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;
@@ -139,7 +145,7 @@
 
 size_t StackVisitor::GetNativePcOffset() const {
   DCHECK(!IsShadowFrame());
-  return GetMethod()->NativePcOffset(cur_quick_frame_pc_);
+  return GetMethod()->NativeQuickPcOffset(cur_quick_frame_pc_);
 }
 
 bool StackVisitor::GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind,
@@ -388,23 +394,23 @@
 }
 
 uintptr_t StackVisitor::GetReturnPc() const {
-  byte* sp = reinterpret_cast<byte*>(GetCurrentQuickFrame());
+  uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame());
   DCHECK(sp != NULL);
-  byte* pc_addr = sp + GetMethod()->GetReturnPcOffsetInBytes();
+  uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue();
   return *reinterpret_cast<uintptr_t*>(pc_addr);
 }
 
 void StackVisitor::SetReturnPc(uintptr_t new_ret_pc) {
-  byte* sp = reinterpret_cast<byte*>(GetCurrentQuickFrame());
+  uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame());
   CHECK(sp != NULL);
-  byte* pc_addr = sp + GetMethod()->GetReturnPcOffsetInBytes();
+  uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue();
   *reinterpret_cast<uintptr_t*>(pc_addr) = new_ret_pc;
 }
 
 size_t StackVisitor::ComputeNumFrames(Thread* thread) {
   struct NumFramesVisitor : public StackVisitor {
-    explicit NumFramesVisitor(Thread* thread)
-        : StackVisitor(thread, NULL), frames(0) {}
+    explicit NumFramesVisitor(Thread* thread_in)
+        : StackVisitor(thread_in, NULL), frames(0) {}
 
     bool VisitFrame() OVERRIDE {
       frames++;
@@ -455,8 +461,8 @@
 
 void StackVisitor::DescribeStack(Thread* thread) {
   struct DescribeStackVisitor : public StackVisitor {
-    explicit DescribeStackVisitor(Thread* thread)
-        : StackVisitor(thread, NULL) {}
+    explicit DescribeStackVisitor(Thread* thread_in)
+        : StackVisitor(thread_in, NULL) {}
 
     bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
       LOG(INFO) << "Frame Id=" << GetFrameId() << " " << DescribeLocation();
@@ -505,7 +511,7 @@
       // const size_t kMaxExpectedFrameSize = (256 + 2 + 3 + 3) * sizeof(word);
       const size_t kMaxExpectedFrameSize = 2 * KB;
       CHECK_LE(frame_size, kMaxExpectedFrameSize);
-      size_t return_pc_offset = method->GetReturnPcOffsetInBytes();
+      size_t return_pc_offset = method->GetReturnPcOffset().SizeValue();
       CHECK_LT(return_pc_offset, frame_size);
     }
   }
@@ -521,7 +527,7 @@
        current_fragment = current_fragment->GetLink()) {
     cur_shadow_frame_ = current_fragment->GetTopShadowFrame();
     cur_quick_frame_ = current_fragment->GetTopQuickFrame();
-    cur_quick_frame_pc_ = current_fragment->GetTopQuickFramePc();
+    cur_quick_frame_pc_ = 0;
 
     if (cur_quick_frame_ != NULL) {  // Handle quick stack frames.
       // Can't be both a shadow and a quick fragment.
@@ -539,13 +545,13 @@
         }
         size_t frame_size = method->GetFrameSizeInBytes();
         // Compute PC for next stack frame from return PC.
-        size_t return_pc_offset = method->GetReturnPcOffsetInBytes(frame_size);
-        byte* return_pc_addr = reinterpret_cast<byte*>(cur_quick_frame_) + return_pc_offset;
+        size_t return_pc_offset = method->GetReturnPcOffset(frame_size).SizeValue();
+        uint8_t* return_pc_addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + return_pc_offset;
         uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr);
         if (UNLIKELY(exit_stubs_installed)) {
           // While profiling, the return pc is restored from the side stack, except when walking
           // the stack for an exception where the side stack will be unwound in VisitFrame.
-          if (GetQuickInstrumentationExitPc() == return_pc) {
+          if (reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == return_pc) {
             const instrumentation::InstrumentationStackFrame& instrumentation_frame =
                 GetInstrumentationStackFrame(thread_, instrumentation_stack_depth);
             instrumentation_stack_depth++;
@@ -570,7 +576,7 @@
           }
         }
         cur_quick_frame_pc_ = return_pc;
-        byte* next_frame = reinterpret_cast<byte*>(cur_quick_frame_) + frame_size;
+        uint8_t* next_frame = reinterpret_cast<uint8_t*>(cur_quick_frame_) + frame_size;
         cur_quick_frame_ = reinterpret_cast<StackReference<mirror::ArtMethod>*>(next_frame);
         cur_depth_++;
         method = cur_quick_frame_->AsMirrorPtr();
diff --git a/runtime/stack.h b/runtime/stack.h
index e58caee..66c840d 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -53,50 +53,7 @@
   kImpreciseConstant,
   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,
-};
+std::ostream& operator<<(std::ostream& os, const VRegKind& rhs);
 
 // A reference from the shadow stack to a MirrorType object within the Java heap.
 template<class MirrorType>
@@ -380,9 +337,7 @@
   }
 
 #if defined(ART_USE_PORTABLE_COMPILER)
-  enum ShadowFrameFlag {
-    kHasReferenceArray = 1ul << 31
-  };
+  constexpr uint32_t kHasReferenceArray = 1ul << 31;
   // TODO: make const in the portable case.
   uint32_t number_of_vregs_;
 #else
@@ -404,7 +359,7 @@
 class PACKED(4) ManagedStack {
  public:
   ManagedStack()
-      : link_(NULL), top_shadow_frame_(NULL), top_quick_frame_(NULL), top_quick_frame_pc_(0) {}
+      : top_quick_frame_(nullptr), link_(nullptr), top_shadow_frame_(nullptr) {}
 
   void PushManagedStackFragment(ManagedStack* fragment) {
     // Copy this top fragment into given fragment.
@@ -430,29 +385,16 @@
   }
 
   void SetTopQuickFrame(StackReference<mirror::ArtMethod>* top) {
-    DCHECK(top_shadow_frame_ == NULL);
+    DCHECK(top_shadow_frame_ == nullptr);
     top_quick_frame_ = top;
   }
 
-  uintptr_t GetTopQuickFramePc() const {
-    return top_quick_frame_pc_;
-  }
-
-  void SetTopQuickFramePc(uintptr_t pc) {
-    DCHECK(top_shadow_frame_ == NULL);
-    top_quick_frame_pc_ = pc;
-  }
-
   static size_t TopQuickFrameOffset() {
     return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_);
   }
 
-  static size_t TopQuickFramePcOffset() {
-    return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_pc_);
-  }
-
   ShadowFrame* PushShadowFrame(ShadowFrame* new_top_frame) {
-    DCHECK(top_quick_frame_ == NULL);
+    DCHECK(top_quick_frame_ == nullptr);
     ShadowFrame* old_frame = top_shadow_frame_;
     top_shadow_frame_ = new_top_frame;
     new_top_frame->SetLink(old_frame);
@@ -460,8 +402,8 @@
   }
 
   ShadowFrame* PopShadowFrame() {
-    DCHECK(top_quick_frame_ == NULL);
-    CHECK(top_shadow_frame_ != NULL);
+    DCHECK(top_quick_frame_ == nullptr);
+    CHECK(top_shadow_frame_ != nullptr);
     ShadowFrame* frame = top_shadow_frame_;
     top_shadow_frame_ = frame->GetLink();
     return frame;
@@ -472,7 +414,7 @@
   }
 
   void SetTopShadowFrame(ShadowFrame* top) {
-    DCHECK(top_quick_frame_ == NULL);
+    DCHECK(top_quick_frame_ == nullptr);
     top_shadow_frame_ = top;
   }
 
@@ -485,10 +427,9 @@
   bool ShadowFramesContain(StackReference<mirror::Object>* shadow_frame_entry) const;
 
  private:
+  StackReference<mirror::ArtMethod>* top_quick_frame_;
   ManagedStack* link_;
   ShadowFrame* top_shadow_frame_;
-  StackReference<mirror::ArtMethod>* top_quick_frame_;
-  uintptr_t top_quick_frame_pc_;
 };
 
 class StackVisitor {
@@ -528,10 +469,10 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Callee saves are held at the top of the frame
     DCHECK(GetMethod() != nullptr);
-    byte* save_addr =
-        reinterpret_cast<byte*>(cur_quick_frame_) + frame_size - ((num + 1) * kPointerSize);
+    uint8_t* save_addr =
+        reinterpret_cast<uint8_t*>(cur_quick_frame_) + frame_size - ((num + 1) * sizeof(void*));
 #if defined(__i386__) || defined(__x86_64__)
-    save_addr -= kPointerSize;  // account for return address
+    save_addr -= sizeof(void*);  // account for return address
 #endif
     return reinterpret_cast<uintptr_t*>(save_addr);
   }
@@ -601,7 +542,7 @@
                         uint16_t vreg) const {
     int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg, kRuntimeISA);
     DCHECK_EQ(cur_quick_frame, GetCurrentQuickFrame());
-    byte* vreg_addr = reinterpret_cast<byte*>(cur_quick_frame) + offset;
+    uint8_t* vreg_addr = reinterpret_cast<uint8_t*>(cur_quick_frame) + offset;
     return reinterpret_cast<uint32_t*>(vreg_addr);
   }
 
@@ -612,75 +553,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.
@@ -690,6 +632,7 @@
   }
 
   static int GetOutVROffset(uint16_t out_num, InstructionSet isa) {
+    UNUSED(isa);
     // According to stack model, the first out is above the Method referernce.
     return sizeof(StackReference<mirror::ArtMethod>) + (out_num * sizeof(uint32_t));
   }
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7d3a48f..a58ecab 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,15 +77,18 @@
  * 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,
+    kInFpuRegister,
     kConstant
   };
 
@@ -114,8 +117,8 @@
 
   MemoryRegion region_;
 
-  template <typename T> friend class CodeInfo;
-  template <typename T> friend class StackMapStream;
+  friend class CodeInfo;
+  friend class StackMapStream;
 };
 
 /**
@@ -127,12 +130,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 +147,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 +201,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 +213,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 +262,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());
+    UNREACHABLE();
   }
 
-  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());
+    UNREACHABLE();
   }
 
  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 +314,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..94f7585 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.
@@ -58,7 +78,9 @@
 
 inline void Thread::AssertThreadSuspensionIsAllowable(bool check_locks) const {
   if (kIsDebugBuild) {
-    CHECK_EQ(0u, tls32_.no_thread_suspension) << tlsPtr_.last_no_thread_suspension_cause;
+    if (gAborting == 0) {
+      CHECK_EQ(0u, tls32_.no_thread_suspension) << tlsPtr_.last_no_thread_suspension_cause;
+    }
     if (check_locks) {
       bool bad_mutexes_held = false;
       for (int i = kLockLevelCount - 1; i >= 0; --i) {
@@ -72,7 +94,9 @@
           }
         }
       }
-      CHECK(!bad_mutexes_held);
+      if (gAborting == 0) {
+        CHECK(!bad_mutexes_held);
+      }
     }
   }
 }
@@ -178,9 +202,9 @@
   DCHECK_LE(tlsPtr_.thread_local_alloc_stack_top, tlsPtr_.thread_local_alloc_stack_end);
   if (tlsPtr_.thread_local_alloc_stack_top < tlsPtr_.thread_local_alloc_stack_end) {
     // There's room.
-    DCHECK_LE(reinterpret_cast<byte*>(tlsPtr_.thread_local_alloc_stack_top) +
+    DCHECK_LE(reinterpret_cast<uint8_t*>(tlsPtr_.thread_local_alloc_stack_top) +
                   sizeof(mirror::Object*),
-              reinterpret_cast<byte*>(tlsPtr_.thread_local_alloc_stack_end));
+              reinterpret_cast<uint8_t*>(tlsPtr_.thread_local_alloc_stack_end));
     DCHECK(*tlsPtr_.thread_local_alloc_stack_top == nullptr);
     *tlsPtr_.thread_local_alloc_stack_top = obj;
     ++tlsPtr_.thread_local_alloc_stack_top;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 129c311..cb3da8b 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -29,9 +29,11 @@
 #include <cerrno>
 #include <iostream>
 #include <list>
+#include <sstream>
 
 #include "arch/context.h"
 #include "base/mutex.h"
+#include "base/to_str.h"
 #include "class_linker-inl.h"
 #include "class_linker.h"
 #include "debugger.h"
@@ -156,7 +158,7 @@
     // Check that if we got here we cannot be shutting down (as shutdown should never have started
     // while threads are being born).
     CHECK(!runtime->IsShuttingDownLocked());
-    self->Init(runtime->GetThreadList(), runtime->GetJavaVM());
+    CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM()));
     Runtime::Current()->EndThreadBirth();
   }
   {
@@ -238,7 +240,7 @@
 }
 
 // Global variable to prevent the compiler optimizing away the page reads for the stack.
-byte dont_optimize_this;
+uint8_t dont_optimize_this;
 
 // Install a protected region in the stack.  This is used to trigger a SIGSEGV if a stack
 // overflow is detected.  It is located right below the stack_begin_.
@@ -252,9 +254,9 @@
 // this by reading every page from the stack bottom (highest address) to the stack top.
 // We then madvise this away.
 void Thread::InstallImplicitProtection() {
-  byte* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize;
-  byte* stack_himem = tlsPtr_.stack_end;
-  byte* stack_top = reinterpret_cast<byte*>(reinterpret_cast<uintptr_t>(&stack_himem) &
+  uint8_t* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize;
+  uint8_t* stack_himem = tlsPtr_.stack_end;
+  uint8_t* stack_top = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(&stack_himem) &
       ~(kPageSize - 1));    // Page containing current top of stack.
 
   // First remove the protection on the protected region as will want to read and
@@ -268,7 +270,7 @@
   // a segv.
 
   // Read every page from the high address to the low.
-  for (byte* p = stack_top; p >= pregion; p -= kPageSize) {
+  for (uint8_t* p = stack_top; p >= pregion; p -= kPageSize) {
     dont_optimize_this = *p;
   }
 
@@ -346,40 +348,46 @@
   }
 }
 
-void Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) {
+bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) {
   // This function does all the initialization that must be run by the native thread it applies to.
   // (When we create a new thread from managed code, we allocate the Thread* in Thread::Create so
   // we can handshake with the corresponding native thread when it's ready.) Check this native
   // thread hasn't been through here already...
   CHECK(Thread::Current() == nullptr);
+
+  // Set pthread_self_ ahead of pthread_setspecific, that makes Thread::Current function, this
+  // avoids pthread_self_ ever being invalid when discovered from Thread::Current().
+  tlsPtr_.pthread_self = pthread_self();
+  CHECK(is_started_);
+
   SetUpAlternateSignalStack();
+  if (!InitStackHwm()) {
+    return false;
+  }
   InitCpu();
   InitTlsEntryPoints();
   RemoveSuspendTrigger();
   InitCardTable();
   InitTid();
-  // Set pthread_self_ ahead of pthread_setspecific, that makes Thread::Current function, this
-  // avoids pthread_self_ ever being invalid when discovered from Thread::Current().
-  tlsPtr_.pthread_self = pthread_self();
-  CHECK(is_started_);
+
   CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, this), "attach self");
   DCHECK_EQ(Thread::Current(), this);
 
   tls32_.thin_lock_thread_id = thread_list->AllocThreadId(this);
-  InitStackHwm();
 
   tlsPtr_.jni_env = new JNIEnvExt(this, java_vm);
   thread_list->Register(this);
+  return true;
 }
 
 Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_group,
                        bool create_peer) {
-  Thread* self;
   Runtime* runtime = Runtime::Current();
   if (runtime == nullptr) {
     LOG(ERROR) << "Thread attaching to non-existent runtime: " << thread_name;
     return nullptr;
   }
+  Thread* self;
   {
     MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_);
     if (runtime->IsShuttingDownLocked()) {
@@ -388,8 +396,12 @@
     } else {
       Runtime::Current()->StartThreadBirth();
       self = new Thread(as_daemon);
-      self->Init(runtime->GetThreadList(), runtime->GetJavaVM());
+      bool init_success = self->Init(runtime->GetThreadList(), runtime->GetJavaVM());
       Runtime::Current()->EndThreadBirth();
+      if (!init_success) {
+        delete self;
+        return nullptr;
+      }
     }
   }
 
@@ -429,6 +441,7 @@
     thread_group = runtime->GetMainThreadGroup();
   }
   ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
+  // Add missing null check in case of OOM b/18297817
   if (thread_name.get() == nullptr) {
     CHECK(IsExceptionPending());
     return;
@@ -458,7 +471,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
@@ -496,7 +509,7 @@
   Dbg::DdmSendThreadNotification(this, CHUNK_TYPE("THNM"));
 }
 
-void Thread::InitStackHwm() {
+bool Thread::InitStackHwm() {
   void* read_stack_base;
   size_t read_stack_size;
   size_t read_guard_size;
@@ -508,7 +521,7 @@
                                 PrettySize(read_stack_size).c_str(),
                                 PrettySize(read_guard_size).c_str());
 
-  tlsPtr_.stack_begin = reinterpret_cast<byte*>(read_stack_base);
+  tlsPtr_.stack_begin = reinterpret_cast<uint8_t*>(read_stack_base);
   tlsPtr_.stack_size = read_stack_size;
 
   // The minimum stack size we can cope with is the overflow reserved bytes (typically
@@ -518,41 +531,12 @@
   uint32_t min_stack = GetStackOverflowReservedBytes(kRuntimeISA) + kStackOverflowProtectedSize
     + 4 * KB;
   if (read_stack_size <= min_stack) {
-    LOG(FATAL) << "Attempt to attach a thread with a too-small stack (" << read_stack_size
-        << " bytes)";
+    // Note, as we know the stack is small, avoid operations that could use a lot of stack.
+    LogMessage::LogLineLowStack(__PRETTY_FUNCTION__, __LINE__, ERROR,
+                                "Attempt to attach a thread with a too-small stack");
+    return false;
   }
 
-  // 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();
@@ -575,6 +559,8 @@
   // Sanity check.
   int stack_variable;
   CHECK_GT(&stack_variable, reinterpret_cast<void*>(tlsPtr_.stack_end));
+
+  return true;
 }
 
 void Thread::ShortDump(std::ostream& os) const {
@@ -635,7 +621,7 @@
     }
   }
   std::ostringstream ss;
-  Runtime::Current()->GetThreadList()->DumpLocked(ss);
+  Runtime::Current()->GetThreadList()->Dump(ss);
   LOG(FATAL) << ss.str();
 }
 
@@ -873,10 +859,11 @@
 }
 
 struct StackDumpVisitor : public StackVisitor {
-  StackDumpVisitor(std::ostream& os, Thread* thread, Context* context, bool can_allocate)
+  StackDumpVisitor(std::ostream& os_in, Thread* thread_in, Context* context, bool can_allocate_in)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : StackVisitor(thread, context), os(os), thread(thread), can_allocate(can_allocate),
-        last_method(nullptr), last_line_number(0), repetition_count(0), frame_count(0) {
+      : StackVisitor(thread_in, context), os(os_in), thread(thread_in),
+        can_allocate(can_allocate_in), last_method(nullptr), last_line_number(0),
+        repetition_count(0), frame_count(0) {
   }
 
   virtual ~StackDumpVisitor() {
@@ -1074,7 +1061,8 @@
   }
 
   // Allocate a TLS slot.
-  CHECK_PTHREAD_CALL(pthread_key_create, (&Thread::pthread_key_self_, Thread::ThreadExitCallback), "self key");
+  CHECK_PTHREAD_CALL(pthread_key_create, (&Thread::pthread_key_self_, Thread::ThreadExitCallback),
+                     "self key");
 
   // Double-check the TLS slot allocation.
   if (pthread_getspecific(pthread_key_self_) != nullptr) {
@@ -1136,6 +1124,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());
@@ -1171,6 +1165,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.
@@ -1198,22 +1207,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;
@@ -1246,7 +1249,7 @@
   delete tlsPtr_.stack_trace_sample;
   free(tlsPtr_.nested_signal_state);
 
-  Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
+  Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this);
 
   TearDownAlternateSignalStack();
 }
@@ -1274,7 +1277,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.
@@ -1298,7 +1301,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;
@@ -1307,7 +1310,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;
     }
@@ -1340,6 +1343,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;
@@ -1353,22 +1357,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;
 }
@@ -1408,6 +1415,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)
@@ -1652,7 +1666,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);
@@ -1688,7 +1703,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;
   }
@@ -1827,7 +1843,6 @@
   DO_THREAD_OFFSET(StackEndOffset<ptr_size>(), "stack_end")
   DO_THREAD_OFFSET(ThinLockIdOffset<ptr_size>(), "thin_lock_thread_id")
   DO_THREAD_OFFSET(TopOfManagedStackOffset<ptr_size>(), "top_quick_frame_method")
-  DO_THREAD_OFFSET(TopOfManagedStackPcOffset<ptr_size>(), "top_quick_frame_pc")
   DO_THREAD_OFFSET(TopShadowFrameOffset<ptr_size>(), "top_shadow_frame")
   DO_THREAD_OFFSET(TopHandleScopeOffset<ptr_size>(), "top_handle_scope")
   DO_THREAD_OFFSET(ThreadSuspendTriggerOffset<ptr_size>(), "suspend_trigger")
@@ -1880,12 +1895,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)
@@ -1968,6 +1995,7 @@
   exception_handler.UpdateInstrumentationStack();
   exception_handler.DoLongJump();
   LOG(FATAL) << "UNREACHABLE";
+  UNREACHABLE();
 }
 
 Context* Thread::GetLongJumpContext() {
@@ -2108,48 +2136,71 @@
 
     // 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);
+        uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point);
+        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->NativeQuickPcOffset(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
+          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);
+                  }
                 }
               }
             }
@@ -2182,11 +2233,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) {
@@ -2196,10 +2242,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);
   }
@@ -2278,7 +2320,7 @@
   }
 }
 
-void Thread::SetTlab(byte* start, byte* end) {
+void Thread::SetTlab(uint8_t* start, uint8_t* end) {
   DCHECK_LE(start, end);
   tlsPtr_.thread_local_start = start;
   tlsPtr_.thread_local_pos  = tlsPtr_.thread_local_start;
diff --git a/runtime/thread.h b/runtime/thread.h
index 0c64f1f..7e567fb 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -144,8 +144,17 @@
   // Reset internal state of child thread after fork.
   void InitAfterFork();
 
+  // Get the currently executing thread, frequently referred to as 'self'. This call has reasonably
+  // high cost and so we favor passing self around when possible.
+  // TODO: mark as PURE so the compiler may coalesce and remove?
   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 +331,7 @@
     return tlsPtr_.exception;
   }
 
+  void AssertPendingException() const;
   void AssertNoPendingException() const;
   void AssertNoPendingExceptionForNewException(const char* msg) const;
 
@@ -355,9 +365,8 @@
 
   ThrowLocation GetCurrentLocationForThrow() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void SetTopOfStack(StackReference<mirror::ArtMethod>* top_method, uintptr_t pc) {
+  void SetTopOfStack(StackReference<mirror::ArtMethod>* top_method) {
     tlsPtr_.managed_stack.SetTopQuickFrame(top_method);
-    tlsPtr_.managed_stack.SetTopQuickFramePc(pc);
   }
 
   void SetTopOfShadowStack(ShadowFrame* top) {
@@ -457,12 +466,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[].
@@ -578,7 +586,7 @@
     return tlsPtr_.stack_size - (tlsPtr_.stack_end - tlsPtr_.stack_begin);
   }
 
-  byte* GetStackEndForInterpreter(bool implicit_overflow_check) const {
+  uint8_t* GetStackEndForInterpreter(bool implicit_overflow_check) const {
     if (implicit_overflow_check) {
       // The interpreter needs the extra overflow bytes that stack_end does
       // not include.
@@ -588,7 +596,7 @@
     }
   }
 
-  byte* GetStackEnd() const {
+  uint8_t* GetStackEnd() const {
     return tlsPtr_.stack_end;
   }
 
@@ -628,13 +636,6 @@
         ManagedStack::TopQuickFrameOffset());
   }
 
-  template<size_t pointer_size>
-  static ThreadOffset<pointer_size> TopOfManagedStackPcOffset() {
-    return ThreadOffsetFromTlsPtr<pointer_size>(
-        OFFSETOF_MEMBER(tls_ptr_sized_values, managed_stack) +
-        ManagedStack::TopQuickFramePcOffset());
-  }
-
   const ManagedStack* GetManagedStack() const {
     return &tlsPtr_.managed_stack;
   }
@@ -673,7 +674,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;
@@ -686,7 +687,7 @@
   }
 
   void PushHandleScope(HandleScope* handle_scope) {
-    handle_scope->SetLink(tlsPtr_.top_handle_scope);
+    DCHECK_EQ(handle_scope->GetLink(), tlsPtr_.top_handle_scope);
     tlsPtr_.top_handle_scope = handle_scope;
   }
 
@@ -784,7 +785,7 @@
   size_t TlabSize() const;
   // Doesn't check that there is room.
   mirror::Object* AllocTlab(size_t bytes);
-  void SetTlab(byte* start, byte* end);
+  void SetTlab(uint8_t* start, uint8_t* end);
   bool HasTlab() const;
 
   // Remove the suspend trigger for this thread by making the suspend_trigger_ TLS value
@@ -892,14 +893,14 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void RemoveFromThreadGroup(ScopedObjectAccess& soa) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void Init(ThreadList*, JavaVMExt*) EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_);
+  bool Init(ThreadList*, JavaVMExt*) EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_);
   void InitCardTable();
   void InitCpu();
   void CleanupCpu();
   void InitTlsEntryPoints();
   void InitTid();
   void InitPthreadKeySelf();
-  void InitStackHwm();
+  bool InitStackHwm();
 
   void SetUpAlternateSignalStack();
   void TearDownAlternateSignalStack();
@@ -926,7 +927,7 @@
     // See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47409
     DISALLOW_COPY_AND_ASSIGN(StateAndFlags);
   };
-  COMPILE_ASSERT(sizeof(StateAndFlags) == sizeof(int32_t), weird_state_and_flags_size);
+  static_assert(sizeof(StateAndFlags) == sizeof(int32_t), "Weird state_and_flags size");
 
   static void ThreadExitCallback(void* arg);
 
@@ -962,8 +963,8 @@
     }
 
     union StateAndFlags state_and_flags;
-    COMPILE_ASSERT(sizeof(union StateAndFlags) == sizeof(int32_t),
-                   sizeof_state_and_flags_and_int32_are_different);
+    static_assert(sizeof(union StateAndFlags) == sizeof(int32_t),
+                  "Size of state_and_flags and int32 are different");
 
     // A non-zero value is used to tell the current thread to enter a safe point
     // at the next poll.
@@ -1029,18 +1030,22 @@
       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.
-    byte* card_table;
+    uint8_t* card_table;
 
     // The pending exception or NULL.
     mirror::Throwable* exception;
 
     // The end of this thread's stack. This is the lowest safely-addressable address on the stack.
     // We leave extra space so there's room for the code that throws StackOverflowError.
-    byte* stack_end;
+    uint8_t* stack_end;
 
     // The top of the managed stack often manipulated directly by compiler generated code.
     ManagedStack managed_stack;
@@ -1063,7 +1068,7 @@
     jobject jpeer;
 
     // The "lowest addressable byte" of the stack.
-    byte* stack_begin;
+    uint8_t* stack_begin;
 
     // Size of the stack.
     size_t stack_size;
@@ -1085,7 +1090,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;
@@ -1127,9 +1132,9 @@
     QuickEntryPoints quick_entrypoints;
 
     // Thread-local allocation pointer.
-    byte* thread_local_start;
-    byte* thread_local_pos;
-    byte* thread_local_end;
+    uint8_t* thread_local_start;
+    uint8_t* thread_local_pos;
+    uint8_t* thread_local_end;
     size_t thread_local_objects;
 
     // There are RosAlloc::kNumThreadLocalSizeBrackets thread-local size brackets per thread.
@@ -1162,7 +1167,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,8 +1175,24 @@
   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);
 
 }  // namespace art
 
diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc
index 1254056..0284364 100644
--- a/runtime/thread_linux.cc
+++ b/runtime/thread_linux.cc
@@ -16,6 +16,8 @@
 
 #include "thread.h"
 
+#include <signal.h>
+
 namespace art {
 
 void Thread::SetNativePriority(int) {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 24ff92e..675ce9a 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -25,6 +25,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <sstream>
+
 #include "base/mutex.h"
 #include "base/mutex-inl.h"
 #include "base/timing_logger.h"
@@ -95,10 +97,7 @@
 }
 
 void ThreadList::DumpForSigQuit(std::ostream& os) {
-  {
-    MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
-    DumpLocked(os);
-  }
+  Dump(os);
   DumpUnattachedThreads(os);
 }
 
@@ -140,12 +139,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) {
@@ -162,14 +203,14 @@
 
 #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";
   Locks::mutator_lock_->Dump(ss);
   ss << "\n";
-  runtime->GetThreadList()->DumpLocked(ss);
+  runtime->GetThreadList()->Dump(ss);
   LOG(FATAL) << ss.str();
   exit(0);
 }
@@ -179,7 +220,7 @@
 // individual thread requires polling. delay_us is the requested sleep and total_delay_us
 // accumulates the total time spent sleeping for timeouts. The first sleep is just a yield,
 // subsequently sleeps increase delay_us from 1ms to 500ms by doubling.
-static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us) {
+static void ThreadSuspendSleep(useconds_t* delay_us, useconds_t* total_delay_us) {
   useconds_t new_delay_us = (*delay_us) * 2;
   CHECK_GE(new_delay_us, *delay_us);
   if (new_delay_us < 500000) {  // Don't allow sleeping to be more than 0.5s.
@@ -200,7 +241,7 @@
   Locks::mutator_lock_->AssertNotExclusiveHeld(self);
   Locks::thread_list_lock_->AssertNotHeld(self);
   Locks::thread_suspend_count_lock_->AssertNotHeld(self);
-  if (kDebugLocking) {
+  if (kDebugLocking && gAborting == 0) {
     CHECK_NE(self->GetState(), kRunnable);
   }
 
@@ -244,7 +285,7 @@
       useconds_t total_delay_us = 0;
       do {
         useconds_t delay_us = 100;
-        ThreadSuspendSleep(self, &delay_us, &total_delay_us);
+        ThreadSuspendSleep(&delay_us, &total_delay_us);
       } while (!thread->IsSuspended());
       // Shouldn't need to wait for longer than 1000 microseconds.
       constexpr useconds_t kLongWaitThresholdUS = 1000;
@@ -275,12 +316,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;
   {
@@ -443,17 +482,18 @@
   VLOG(threads) << "Resume(" << reinterpret_cast<void*>(thread) << ") complete";
 }
 
-static void ThreadSuspendByPeerWarning(Thread* self, int level, const char* message, jobject peer) {
+static void ThreadSuspendByPeerWarning(Thread* self, LogSeverity severity, const char* message,
+                                       jobject peer) {
   JNIEnvExt* env = self->GetJniEnv();
   ScopedLocalRef<jstring>
       scoped_name_string(env, (jstring)env->GetObjectField(peer,
                                                           WellKnownClasses::java_lang_Thread_name));
   ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
   if (scoped_name_chars.c_str() == NULL) {
-      LOG(level) << message << ": " << peer;
+      LOG(severity) << message << ": " << peer;
       env->ExceptionClear();
   } else {
-      LOG(level) << message << ": " << peer << ":" << scoped_name_chars.c_str();
+      LOG(severity) << message << ": " << peer << ":" << scoped_name_chars.c_str();
   }
 }
 
@@ -475,7 +515,7 @@
       // than request thread suspension, to avoid potential cycles in threads requesting each other
       // suspend.
       ScopedObjectAccess soa(self);
-      MutexLock mu(self, *Locks::thread_list_lock_);
+      MutexLock thread_list_mu(self, *Locks::thread_list_lock_);
       thread = Thread::FromManagedThread(soa, peer);
       if (thread == nullptr) {
         ThreadSuspendByPeerWarning(self, WARNING, "No such thread for suspend", peer);
@@ -488,7 +528,7 @@
       }
       VLOG(threads) << "SuspendThreadByPeer found thread: " << *thread;
       {
-        MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+        MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
         if (request_suspension) {
           thread->ModifySuspendCount(self, +1, debug_suspension);
           request_suspension = false;
@@ -521,12 +561,13 @@
       // Release locks and come out of runnable state.
     }
     VLOG(threads) << "SuspendThreadByPeer sleeping to allow thread chance to suspend";
-    ThreadSuspendSleep(self, &delay_us, &total_delay_us);
+    ThreadSuspendSleep(&delay_us, &total_delay_us);
   }
 }
 
-static void ThreadSuspendByThreadIdWarning(int level, const char* message, uint32_t thread_id) {
-  LOG(level) << StringPrintf("%s: %d", message, thread_id);
+static void ThreadSuspendByThreadIdWarning(LogSeverity severity, const char* message,
+                                           uint32_t thread_id) {
+  LOG(severity) << StringPrintf("%s: %d", message, thread_id);
 }
 
 Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension,
@@ -547,7 +588,7 @@
       // than request thread suspension, to avoid potential cycles in threads requesting each other
       // suspend.
       ScopedObjectAccess soa(self);
-      MutexLock mu(self, *Locks::thread_list_lock_);
+      MutexLock thread_list_mu(self, *Locks::thread_list_lock_);
       Thread* thread = nullptr;
       for (const auto& it : list_) {
         if (it->GetThreadId() == thread_id) {
@@ -565,7 +606,7 @@
       VLOG(threads) << "SuspendThreadByThreadId found thread: " << *thread;
       DCHECK(Contains(thread));
       {
-        MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+        MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
         if (suspended_thread == nullptr) {
           thread->ModifySuspendCount(self, +1, debug_suspension);
           suspended_thread = thread;
@@ -598,7 +639,7 @@
       // Release locks and come out of runnable state.
     }
     VLOG(threads) << "SuspendThreadByThreadId sleeping to allow thread chance to suspend";
-    ThreadSuspendSleep(self, &delay_us, &total_delay_us);
+    ThreadSuspendSleep(&delay_us, &total_delay_us);
   }
 }
 
@@ -621,9 +662,9 @@
   VLOG(threads) << *self << " SuspendAllForDebugger starting...";
 
   {
-    MutexLock mu(self, *Locks::thread_list_lock_);
+    MutexLock thread_list_mu(self, *Locks::thread_list_lock_);
     {
-      MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+      MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
       // Update global suspend all state for attaching threads.
       DCHECK_GE(suspend_all_count_, debug_suspend_all_count_);
       ++suspend_all_count_;
@@ -728,9 +769,9 @@
   Locks::mutator_lock_->AssertNotExclusiveHeld(self);
 
   {
-    MutexLock mu(self, *Locks::thread_list_lock_);
+    MutexLock thread_list_mu(self, *Locks::thread_list_lock_);
     {
-      MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+      MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
       // Update global suspend all state for attaching threads.
       DCHECK_GE(suspend_all_count_, debug_suspend_all_count_);
       needs_resume = (debug_suspend_all_count_ > 0);
@@ -882,6 +923,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;
 
@@ -898,21 +941,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 9f515a8..a7f2c53 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/thread_pool.cc b/runtime/thread_pool.cc
index e8c9ff8..587eb32 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -42,14 +42,14 @@
 }
 
 ThreadPoolWorker::~ThreadPoolWorker() {
-  CHECK_PTHREAD_CALL(pthread_join, (pthread_, NULL), "thread pool worker shutdown");
+  CHECK_PTHREAD_CALL(pthread_join, (pthread_, nullptr), "thread pool worker shutdown");
 }
 
 void ThreadPoolWorker::Run() {
   Thread* self = Thread::Current();
-  Task* task = NULL;
+  Task* task = nullptr;
   thread_pool_->creation_barier_.Wait(self);
-  while ((task = thread_pool_->GetTask(self)) != NULL) {
+  while ((task = thread_pool_->GetTask(self)) != nullptr) {
     task->Run(self);
     task->Finalize();
   }
@@ -58,11 +58,11 @@
 void* ThreadPoolWorker::Callback(void* arg) {
   ThreadPoolWorker* worker = reinterpret_cast<ThreadPoolWorker*>(arg);
   Runtime* runtime = Runtime::Current();
-  CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, NULL, false));
+  CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false));
   // Do work until its time to shut down.
   worker->Run();
   runtime->DetachCurrentThread();
-  return NULL;
+  return nullptr;
 }
 
 void ThreadPool::AddTask(Thread* self, Task* task) {
@@ -89,8 +89,9 @@
     max_active_workers_(num_threads) {
   Thread* self = Thread::Current();
   while (GetThreadCount() < num_threads) {
-    const std::string name = StringPrintf("%s worker thread %zu", name_.c_str(), GetThreadCount());
-    threads_.push_back(new ThreadPoolWorker(this, name, ThreadPoolWorker::kDefaultStackSize));
+    const std::string worker_name = StringPrintf("%s worker thread %zu", name_.c_str(),
+                                                 GetThreadCount());
+    threads_.push_back(new ThreadPoolWorker(this, worker_name, ThreadPoolWorker::kDefaultStackSize));
   }
   // Wait for all of the threads to attach.
   creation_barier_.Wait(self);
@@ -137,8 +138,8 @@
     const size_t active_threads = thread_count - waiting_count_;
     // <= since self is considered an active worker.
     if (active_threads <= max_active_workers_) {
-      Task* task = TryGetTaskLocked(self);
-      if (task != NULL) {
+      Task* task = TryGetTaskLocked();
+      if (task != nullptr) {
         return task;
       }
     }
@@ -157,28 +158,28 @@
     --waiting_count_;
   }
 
-  // We are shutting down, return NULL to tell the worker thread to stop looping.
-  return NULL;
+  // We are shutting down, return nullptr to tell the worker thread to stop looping.
+  return nullptr;
 }
 
 Task* ThreadPool::TryGetTask(Thread* self) {
   MutexLock mu(self, task_queue_lock_);
-  return TryGetTaskLocked(self);
+  return TryGetTaskLocked();
 }
 
-Task* ThreadPool::TryGetTaskLocked(Thread* self) {
+Task* ThreadPool::TryGetTaskLocked() {
   if (started_ && !tasks_.empty()) {
     Task* task = tasks_.front();
     tasks_.pop_front();
     return task;
   }
-  return NULL;
+  return nullptr;
 }
 
 void ThreadPool::Wait(Thread* self, bool do_work, bool may_hold_locks) {
   if (do_work) {
-    Task* task = NULL;
-    while ((task = TryGetTask(self)) != NULL) {
+    Task* task = nullptr;
+    while ((task = TryGetTask(self)) != nullptr) {
       task->Run(self);
       task->Finalize();
     }
@@ -201,17 +202,17 @@
 
 WorkStealingWorker::WorkStealingWorker(ThreadPool* thread_pool, const std::string& name,
                                        size_t stack_size)
-    : ThreadPoolWorker(thread_pool, name, stack_size), task_(NULL) {}
+    : ThreadPoolWorker(thread_pool, name, stack_size), task_(nullptr) {}
 
 void WorkStealingWorker::Run() {
   Thread* self = Thread::Current();
-  Task* task = NULL;
+  Task* task = nullptr;
   WorkStealingThreadPool* thread_pool = down_cast<WorkStealingThreadPool*>(thread_pool_);
-  while ((task = thread_pool_->GetTask(self)) != NULL) {
+  while ((task = thread_pool_->GetTask(self)) != nullptr) {
     WorkStealingTask* stealing_task = down_cast<WorkStealingTask*>(task);
 
     {
-      CHECK(task_ == NULL);
+      CHECK(task_ == nullptr);
       MutexLock mu(self, thread_pool->work_steal_lock_);
       // Register that we are running the task
       ++stealing_task->ref_count_;
@@ -221,7 +222,7 @@
     // Mark ourselves as not running a task so that nobody tries to steal from us.
     // There is a race condition that someone starts stealing from us at this point. This is okay
     // due to the reference counting.
-    task_ = NULL;
+    task_ = nullptr;
 
     bool finalize;
 
@@ -229,13 +230,13 @@
     // all that happens when the race occurs is that we steal some work instead of processing a
     // task from the queue.
     while (thread_pool->GetTaskCount(self) == 0) {
-      WorkStealingTask* steal_from_task  = NULL;
+      WorkStealingTask* steal_from_task  = nullptr;
 
       {
         MutexLock mu(self, thread_pool->work_steal_lock_);
         // Try finding a task to steal from.
-        steal_from_task = thread_pool->FindTaskToStealFrom(self);
-        if (steal_from_task != NULL) {
+        steal_from_task = thread_pool->FindTaskToStealFrom();
+        if (steal_from_task != nullptr) {
           CHECK_NE(stealing_task, steal_from_task)
               << "Attempting to steal from completed self task";
           steal_from_task->ref_count_++;
@@ -244,7 +245,7 @@
         }
       }
 
-      if (steal_from_task != NULL) {
+      if (steal_from_task != nullptr) {
         // Task which completed earlier is going to steal some work.
         stealing_task->StealFrom(self, steal_from_task);
 
@@ -279,12 +280,13 @@
       work_steal_lock_("work stealing lock"),
       steal_index_(0) {
   while (GetThreadCount() < num_threads) {
-    const std::string name = StringPrintf("Work stealing worker %zu", GetThreadCount());
-    threads_.push_back(new WorkStealingWorker(this, name, ThreadPoolWorker::kDefaultStackSize));
+    const std::string worker_name = StringPrintf("Work stealing worker %zu", GetThreadCount());
+    threads_.push_back(new WorkStealingWorker(this, worker_name,
+                                              ThreadPoolWorker::kDefaultStackSize));
   }
 }
 
-WorkStealingTask* WorkStealingThreadPool::FindTaskToStealFrom(Thread* self) {
+WorkStealingTask* WorkStealingThreadPool::FindTaskToStealFrom() {
   const size_t thread_count = GetThreadCount();
   for (size_t i = 0; i < thread_count; ++i) {
     // TODO: Use CAS instead of lock.
@@ -301,7 +303,7 @@
     }
   }
   // Couldn't find something to steal.
-  return NULL;
+  return nullptr;
 }
 
 WorkStealingThreadPool::~WorkStealingThreadPool() {}
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index c816c84..d6330c8 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -101,7 +101,7 @@
 
   // Try to get a task, returning NULL if there is none available.
   Task* TryGetTask(Thread* self);
-  Task* TryGetTaskLocked(Thread* self) EXCLUSIVE_LOCKS_REQUIRED(task_queue_lock_);
+  Task* TryGetTaskLocked() EXCLUSIVE_LOCKS_REQUIRED(task_queue_lock_);
 
   // Are we shutting down?
   bool IsShuttingDown() const EXCLUSIVE_LOCKS_REQUIRED(task_queue_lock_) {
@@ -178,7 +178,7 @@
   size_t steal_index_;
 
   // Find a task to steal from
-  WorkStealingTask* FindTaskToStealFrom(Thread* self) EXCLUSIVE_LOCKS_REQUIRED(work_steal_lock_);
+  WorkStealingTask* FindTaskToStealFrom() EXCLUSIVE_LOCKS_REQUIRED(work_steal_lock_);
 
   friend class WorkStealingWorker;
 };
diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc
index 4bd44dc..d5f17d1 100644
--- a/runtime/thread_pool_test.cc
+++ b/runtime/thread_pool_test.cc
@@ -95,11 +95,7 @@
   EXPECT_EQ(0, bad_count.LoadSequentiallyConsistent());
   // Allow tasks to finish up and delete themselves.
   thread_pool.StartWorkers(self);
-  while (count.LoadSequentiallyConsistent() != num_tasks &&
-      bad_count.LoadSequentiallyConsistent() != 1) {
-    usleep(200);
-  }
-  thread_pool.StopWorkers(self);
+  thread_pool.Wait(self, false, false);
 }
 
 class TreeTask : public Task {
diff --git a/runtime/thread_state.h b/runtime/thread_state.h
index 0e47d21..6e5deeb 100644
--- a/runtime/thread_state.h
+++ b/runtime/thread_state.h
@@ -17,6 +17,8 @@
 #ifndef ART_RUNTIME_THREAD_STATE_H_
 #define ART_RUNTIME_THREAD_STATE_H_
 
+#include <ostream>
+
 namespace art {
 
 enum ThreadState {
@@ -43,6 +45,7 @@
   kNative,                          // RUNNABLE       TS_RUNNING   running in a JNI native method
   kSuspended,                       // RUNNABLE       TS_RUNNING   suspended by GC or debugger
 };
+std::ostream& operator<<(std::ostream& os, const ThreadState& rhs);
 
 }  // namespace art
 
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 4c5e909..29c01e4 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -18,6 +18,9 @@
 
 #include <sys/uio.h>
 
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include "cutils/trace.h"
+
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
@@ -241,7 +244,8 @@
   the_trace->CompareAndUpdateStackTrace(thread, stack_trace);
 }
 
-static void ClearThreadStackTraceAndClockBase(Thread* thread, void* arg) {
+static void ClearThreadStackTraceAndClockBase(Thread* thread ATTRIBUTE_UNUSED,
+                                              void* arg ATTRIBUTE_UNUSED) {
   thread->SetTraceClockBase(0);
   std::vector<mirror::ArtMethod*>* stack_trace = thread->GetStackTraceSample();
   thread->SetStackTraceSample(NULL);
@@ -558,27 +562,30 @@
 
 void Trace::DexPcMoved(Thread* thread, mirror::Object* this_object,
                        mirror::ArtMethod* method, uint32_t new_dex_pc) {
+  UNUSED(thread, this_object, method, 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,
+void Trace::FieldRead(Thread* thread, mirror::Object* this_object,
                        mirror::ArtMethod* method, uint32_t dex_pc, mirror::ArtField* field)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  UNUSED(thread, this_object, method, dex_pc, field);
   // We're not recorded to listen to this kind of event, so complain.
   LOG(ERROR) << "Unexpected field read event in tracing " << PrettyMethod(method) << " " << dex_pc;
 }
 
-void Trace::FieldWritten(Thread* /*thread*/, mirror::Object* this_object,
+void Trace::FieldWritten(Thread* thread, mirror::Object* this_object,
                           mirror::ArtMethod* method, uint32_t dex_pc, mirror::ArtField* field,
                           const JValue& field_value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  UNUSED(thread, this_object, method, dex_pc, field, field_value);
   // We're not recorded to listen to this kind of event, so complain.
   LOG(ERROR) << "Unexpected field write event in tracing " << PrettyMethod(method) << " " << dex_pc;
 }
 
-void Trace::MethodEntered(Thread* thread, mirror::Object* this_object,
-                          mirror::ArtMethod* method, uint32_t dex_pc) {
+void Trace::MethodEntered(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED,
+                          mirror::ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED) {
   uint32_t thread_clock_diff = 0;
   uint32_t wall_clock_diff = 0;
   ReadClocks(thread, &thread_clock_diff, &wall_clock_diff);
@@ -586,10 +593,9 @@
                       thread_clock_diff, wall_clock_diff);
 }
 
-void Trace::MethodExited(Thread* thread, mirror::Object* this_object,
-                         mirror::ArtMethod* method, uint32_t dex_pc,
-                         const JValue& return_value) {
-  UNUSED(return_value);
+void Trace::MethodExited(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED,
+                         mirror::ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED,
+                         const JValue& return_value ATTRIBUTE_UNUSED) {
   uint32_t thread_clock_diff = 0;
   uint32_t wall_clock_diff = 0;
   ReadClocks(thread, &thread_clock_diff, &wall_clock_diff);
@@ -597,8 +603,8 @@
                       thread_clock_diff, wall_clock_diff);
 }
 
-void Trace::MethodUnwind(Thread* thread, mirror::Object* this_object,
-                         mirror::ArtMethod* method, uint32_t dex_pc) {
+void Trace::MethodUnwind(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED,
+                         mirror::ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED) {
   uint32_t thread_clock_diff = 0;
   uint32_t wall_clock_diff = 0;
   ReadClocks(thread, &thread_clock_diff, &wall_clock_diff);
@@ -610,6 +616,7 @@
                             mirror::ArtMethod* catch_method, uint32_t catch_dex_pc,
                             mirror::Throwable* exception_object)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  UNUSED(thread, throw_location, catch_method, catch_dex_pc, exception_object);
   LOG(ERROR) << "Unexpected exception caught event in tracing";
 }
 
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 50f1eca..478066f 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);
@@ -110,7 +144,7 @@
   LogInternedString(log);
 }
 
-void Transaction::LogInternedString(InternStringLog& log) {
+void Transaction::LogInternedString(const InternStringLog& log) {
   Locks::intern_table_lock_->AssertExclusiveHeld(Thread::Current());
   MutexLock mu(Thread::Current(), log_lock_);
   intern_string_logs_.push_front(log);
@@ -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,
@@ -307,7 +384,7 @@
       }
       break;
     default:
-      LOG(FATAL) << "Unknown value kind " << field_value.kind;
+      LOG(FATAL) << "Unknown value kind " << static_cast<int>(field_value.kind);
       break;
   }
 }
@@ -329,38 +406,38 @@
 void Transaction::InternStringLog::Undo(InternTable* intern_table) {
   DCHECK(intern_table != nullptr);
   switch (string_op_) {
-      case InternStringLog::kInsert: {
-        switch (string_kind_) {
-          case InternStringLog::kStrongString:
-            intern_table->RemoveStrongFromTransaction(str_);
-            break;
-          case InternStringLog::kWeakString:
-            intern_table->RemoveWeakFromTransaction(str_);
-            break;
-          default:
-            LOG(FATAL) << "Unknown interned string kind";
-            break;
-        }
-        break;
+    case InternStringLog::kInsert: {
+      switch (string_kind_) {
+        case InternStringLog::kStrongString:
+          intern_table->RemoveStrongFromTransaction(str_);
+          break;
+        case InternStringLog::kWeakString:
+          intern_table->RemoveWeakFromTransaction(str_);
+          break;
+        default:
+          LOG(FATAL) << "Unknown interned string kind";
+          break;
       }
-      case InternStringLog::kRemove: {
-        switch (string_kind_) {
-          case InternStringLog::kStrongString:
-            intern_table->InsertStrongFromTransaction(str_);
-            break;
-          case InternStringLog::kWeakString:
-            intern_table->InsertWeakFromTransaction(str_);
-            break;
-          default:
-            LOG(FATAL) << "Unknown interned string kind";
-            break;
-        }
-        break;
-      }
-      default:
-        LOG(FATAL) << "Unknown interned string op";
-        break;
+      break;
     }
+    case InternStringLog::kRemove: {
+      switch (string_kind_) {
+        case InternStringLog::kStrongString:
+          intern_table->InsertStrongFromTransaction(str_);
+          break;
+        case InternStringLog::kWeakString:
+          intern_table->InsertWeakFromTransaction(str_);
+          break;
+        default:
+          LOG(FATAL) << "Unknown interned string kind";
+          break;
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unknown interned string op";
+      break;
+  }
 }
 
 void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 6625390..566f231 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -19,6 +19,7 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "base/value_object.h"
 #include "object_callbacks.h"
 #include "offsets.h"
 #include "primitive.h"
@@ -35,12 +36,24 @@
 }
 class InternTable;
 
-class Transaction {
+class Transaction FINAL {
  public:
   Transaction();
   ~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_);
@@ -80,8 +93,12 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
-  class ObjectLog {
+  class ObjectLog : public ValueObject {
    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,17 +112,22 @@
 
    private:
     enum FieldValueKind {
+      kBoolean,
+      kByte,
+      kChar,
+      kShort,
       k32Bits,
       k64Bits,
       kReference
     };
-    struct FieldValue {
+    struct FieldValue : public ValueObject {
       // TODO use JValue instead ?
       uint64_t value;
       FieldValueKind kind;
       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_);
 
@@ -113,7 +135,7 @@
     std::map<uint32_t, FieldValue> field_values_;
   };
 
-  class ArrayLog {
+  class ArrayLog : public ValueObject {
    public:
     void LogValue(size_t index, uint64_t value);
 
@@ -132,7 +154,7 @@
     std::map<size_t, uint64_t> array_values_;
   };
 
-  class InternStringLog {
+  class InternStringLog : public ValueObject {
    public:
     enum StringKind {
       kStrongString,
@@ -154,11 +176,11 @@
 
    private:
     mirror::String* str_;
-    StringKind string_kind_;
-    StringOp string_op_;
+    const StringKind string_kind_;
+    const StringOp string_op_;
   };
 
-  void LogInternedString(InternStringLog& log)
+  void LogInternedString(const InternStringLog& log)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
       LOCKS_EXCLUDED(log_lock_);
 
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 691aec4..8c4b90d 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.
@@ -147,12 +147,12 @@
   mirror::ArtField* floatField = h_klass->FindDeclaredStaticField("floatField", "F");
   ASSERT_TRUE(floatField != nullptr);
   ASSERT_EQ(floatField->GetTypeAsPrimitiveType(), Primitive::kPrimFloat);
-  ASSERT_EQ(floatField->GetFloat(h_klass.Get()), static_cast<float>(0.0f));
+  ASSERT_FLOAT_EQ(floatField->GetFloat(h_klass.Get()), static_cast<float>(0.0f));
 
   mirror::ArtField* doubleField = h_klass->FindDeclaredStaticField("doubleField", "D");
   ASSERT_TRUE(doubleField != nullptr);
   ASSERT_EQ(doubleField->GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
-  ASSERT_EQ(doubleField->GetDouble(h_klass.Get()), static_cast<double>(0.0));
+  ASSERT_DOUBLE_EQ(doubleField->GetDouble(h_klass.Get()), static_cast<double>(0.0));
 
   mirror::ArtField* objectField = h_klass->FindDeclaredStaticField("objectField",
                                                                       "Ljava/lang/Object;");
@@ -190,8 +190,8 @@
   EXPECT_EQ(shortField->GetShort(h_klass.Get()), 0);
   EXPECT_EQ(intField->GetInt(h_klass.Get()), 0);
   EXPECT_EQ(longField->GetLong(h_klass.Get()), static_cast<int64_t>(0));
-  EXPECT_EQ(floatField->GetFloat(h_klass.Get()), static_cast<float>(0.0f));
-  EXPECT_EQ(doubleField->GetDouble(h_klass.Get()), static_cast<double>(0.0));
+  EXPECT_FLOAT_EQ(floatField->GetFloat(h_klass.Get()), static_cast<float>(0.0f));
+  EXPECT_DOUBLE_EQ(doubleField->GetDouble(h_klass.Get()), static_cast<double>(0.0));
   EXPECT_EQ(objectField->GetObject(h_klass.Get()), nullptr);
 }
 
@@ -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.
@@ -246,12 +246,12 @@
   mirror::ArtField* floatField = h_klass->FindDeclaredInstanceField("floatField", "F");
   ASSERT_TRUE(floatField != nullptr);
   ASSERT_EQ(floatField->GetTypeAsPrimitiveType(), Primitive::kPrimFloat);
-  ASSERT_EQ(floatField->GetFloat(h_instance.Get()), static_cast<float>(0.0f));
+  ASSERT_FLOAT_EQ(floatField->GetFloat(h_instance.Get()), static_cast<float>(0.0f));
 
   mirror::ArtField* doubleField = h_klass->FindDeclaredInstanceField("doubleField", "D");
   ASSERT_TRUE(doubleField != nullptr);
   ASSERT_EQ(doubleField->GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
-  ASSERT_EQ(doubleField->GetDouble(h_instance.Get()), static_cast<double>(0.0));
+  ASSERT_DOUBLE_EQ(doubleField->GetDouble(h_instance.Get()), static_cast<double>(0.0));
 
   mirror::ArtField* objectField = h_klass->FindDeclaredInstanceField("objectField",
                                                                         "Ljava/lang/Object;");
@@ -289,8 +289,8 @@
   EXPECT_EQ(shortField->GetShort(h_instance.Get()), 0);
   EXPECT_EQ(intField->GetInt(h_instance.Get()), 0);
   EXPECT_EQ(longField->GetLong(h_instance.Get()), static_cast<int64_t>(0));
-  EXPECT_EQ(floatField->GetFloat(h_instance.Get()), static_cast<float>(0.0f));
-  EXPECT_EQ(doubleField->GetDouble(h_instance.Get()), static_cast<double>(0.0));
+  EXPECT_FLOAT_EQ(floatField->GetFloat(h_instance.Get()), static_cast<float>(0.0f));
+  EXPECT_DOUBLE_EQ(doubleField->GetDouble(h_instance.Get()), static_cast<double>(0.0));
   EXPECT_EQ(objectField->GetObject(h_instance.Get()), nullptr);
 }
 
@@ -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.
@@ -356,14 +356,14 @@
   mirror::FloatArray* floatArray = floatArrayField->GetObject(h_klass.Get())->AsFloatArray();
   ASSERT_TRUE(floatArray != nullptr);
   ASSERT_EQ(floatArray->GetLength(), 1);
-  ASSERT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f));
+  ASSERT_FLOAT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f));
 
   mirror::ArtField* doubleArrayField = h_klass->FindDeclaredStaticField("doubleArrayField", "[D");
   ASSERT_TRUE(doubleArrayField != nullptr);
   mirror::DoubleArray* doubleArray = doubleArrayField->GetObject(h_klass.Get())->AsDoubleArray();
   ASSERT_TRUE(doubleArray != nullptr);
   ASSERT_EQ(doubleArray->GetLength(), 1);
-  ASSERT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
+  ASSERT_DOUBLE_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
 
   mirror::ArtField* objectArrayField = h_klass->FindDeclaredStaticField("objectArrayField",
                                                                            "[Ljava/lang/Object;");
@@ -404,8 +404,8 @@
   EXPECT_EQ(shortArray->GetWithoutChecks(0), 0);
   EXPECT_EQ(intArray->GetWithoutChecks(0), 0);
   EXPECT_EQ(longArray->GetWithoutChecks(0), static_cast<int64_t>(0));
-  EXPECT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f));
-  EXPECT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
+  EXPECT_FLOAT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f));
+  EXPECT_DOUBLE_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
   EXPECT_EQ(objectArray->GetWithoutChecks(0), nullptr);
 }
 
@@ -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/utf.cc b/runtime/utf.cc
index 797c717..05b847b 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -70,19 +70,19 @@
 
 int32_t ComputeUtf16Hash(mirror::CharArray* chars, int32_t offset,
                          size_t char_count) {
-  int32_t hash = 0;
+  uint32_t hash = 0;
   for (size_t i = 0; i < char_count; i++) {
     hash = hash * 31 + chars->Get(offset + i);
   }
-  return hash;
+  return static_cast<int32_t>(hash);
 }
 
 int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count) {
-  int32_t hash = 0;
+  uint32_t hash = 0;
   while (char_count--) {
     hash = hash * 31 + *chars++;
   }
-  return hash;
+  return static_cast<int32_t>(hash);
 }
 
 size_t ComputeModifiedUtf8Hash(const char* chars) {
@@ -90,7 +90,7 @@
   while (*chars != '\0') {
     hash = hash * 31 + *chars++;
   }
-  return hash;
+  return static_cast<int32_t>(hash);
 }
 
 int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2) {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 3765759..f2d710d 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
 }
@@ -91,7 +92,7 @@
   // (On Mac OS 10.7, it's the end.)
   int stack_variable;
   if (stack_addr > &stack_variable) {
-    *stack_base = reinterpret_cast<byte*>(stack_addr) - *stack_size;
+    *stack_base = reinterpret_cast<uint8_t*>(stack_addr) - *stack_size;
   } else {
     *stack_base = stack_addr;
   }
@@ -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,18 +952,18 @@
 }
 
 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) {
+void Split(const std::string& s, char separator, std::vector<std::string>* result) {
   const char* p = s.data();
   const char* end = p + s.size();
   while (p != end) {
@@ -917,12 +974,12 @@
       while (++p != end && *p != separator) {
         // Skip to the next occurrence of the separator.
       }
-      result.push_back(std::string(start, p - start));
+      result->push_back(std::string(start, p - start));
     }
   }
 }
 
-std::string Trim(std::string s) {
+std::string Trim(const std::string& s) {
   std::string result;
   unsigned int start_index = 0;
   unsigned int end_index = s.size() - 1;
@@ -952,7 +1009,7 @@
 }
 
 template <typename StringT>
-std::string Join(std::vector<StringT>& strings, char separator) {
+std::string Join(const std::vector<StringT>& strings, char separator) {
   if (strings.empty()) {
     return "";
   }
@@ -966,9 +1023,8 @@
 }
 
 // Explicit instantiations.
-template std::string Join<std::string>(std::vector<std::string>& strings, char separator);
-template std::string Join<const char*>(std::vector<const char*>& strings, char separator);
-template std::string Join<char*>(std::vector<char*>& strings, char separator);
+template std::string Join<std::string>(const std::vector<std::string>& strings, char separator);
+template std::string Join<const char*>(const std::vector<const char*>& strings, char separator);
 
 bool StartsWith(const std::string& s, const char* prefix) {
   return s.compare(0, strlen(prefix), prefix) == 0;
@@ -1002,7 +1058,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);
@@ -1030,7 +1086,7 @@
   stats = stats.substr(stats.find(')') + 2);
   // Extract the three fields we care about.
   std::vector<std::string> fields;
-  Split(stats, ' ', fields);
+  Split(stats, ' ', &fields);
   *state = fields[0][0];
   *utime = strtoull(fields[11].c_str(), NULL, 10);
   *stime = strtoull(fields[12].c_str(), NULL, 10);
@@ -1047,14 +1103,14 @@
     return "";
   }
   std::vector<std::string> cgroup_lines;
-  Split(cgroup_file, '\n', cgroup_lines);
+  Split(cgroup_file, '\n', &cgroup_lines);
   for (size_t i = 0; i < cgroup_lines.size(); ++i) {
     std::vector<std::string> cgroup_fields;
-    Split(cgroup_lines[i], ':', cgroup_fields);
+    Split(cgroup_lines[i], ':', &cgroup_fields);
     std::vector<std::string> cgroups;
-    Split(cgroup_fields[1], ',', cgroups);
-    for (size_t i = 0; i < cgroups.size(); ++i) {
-      if (cgroups[i] == "cpu") {
+    Split(cgroup_fields[1], ',', &cgroups);
+    for (size_t j = 0; j < cgroups.size(); ++j) {
+      if (cgroups[j] == "cpu") {
         return cgroup_fields[2].substr(1);  // Skip the leading slash.
       }
     }
@@ -1064,11 +1120,12 @@
 
 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__
+  // b/18119146
+  if (RUNNING_ON_VALGRIND != 0) {
+    return;
+  }
+
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
   if (!backtrace->Unwind(0)) {
     os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n";
@@ -1099,7 +1156,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->PcIsWithinQuickCode(it->pc)) {
         const void* start_of_code = current_method->GetEntryPointFromQuickCompiledCode();
         os << JniLongName(current_method) << "+"
            << (it->pc - reinterpret_cast<uintptr_t>(start_of_code));
@@ -1110,6 +1169,8 @@
     }
     os << "\n";
   }
+#else
+  UNUSED(os, tid, prefix, current_method);
 #endif
 }
 
@@ -1134,7 +1195,7 @@
   }
 
   std::vector<std::string> kernel_stack_frames;
-  Split(kernel_stack, '\n', kernel_stack_frames);
+  Split(kernel_stack, '\n', &kernel_stack_frames);
   // We skip the last stack frame because it's always equivalent to "[<ffffffff>] 0xffffffff",
   // which looking at the source appears to be the kernel's way of saying "that's all, folks!".
   kernel_stack_frames.pop_back();
@@ -1314,11 +1375,11 @@
 }
 
 bool IsDexMagic(uint32_t magic) {
-  return DexFile::IsMagicValid(reinterpret_cast<const byte*>(&magic));
+  return DexFile::IsMagicValid(reinterpret_cast<const uint8_t*>(&magic));
 }
 
 bool IsOatMagic(uint32_t magic) {
-  return (memcmp(reinterpret_cast<const byte*>(magic),
+  return (memcmp(reinterpret_cast<const uint8_t*>(magic),
                  OatHeader::kOatMagic,
                  sizeof(OatHeader::kOatMagic)) == 0);
 }
@@ -1376,6 +1437,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..669fe6c 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;
@@ -88,7 +84,7 @@
 
 template<int n, typename T>
 static inline bool IsAligned(T x) {
-  COMPILE_ASSERT((n & (n - 1)) == 0, n_not_power_of_two);
+  static_assert((n & (n - 1)) == 0, "n is not a power of two");
   return (x & (n - 1)) == 0;
 }
 
@@ -112,23 +108,23 @@
   DCHECK(::art::IsAlignedParam(value, alignment)) << reinterpret_cast<const void*>(value)
 
 // Check whether an N-bit two's-complement representation can hold value.
-static inline bool IsInt(int N, word value) {
+static inline bool IsInt(int N, intptr_t value) {
   CHECK_LT(0, N);
-  CHECK_LT(N, kBitsPerWord);
-  word limit = static_cast<word>(1) << (N - 1);
+  CHECK_LT(N, kBitsPerIntPtrT);
+  intptr_t limit = static_cast<intptr_t>(1) << (N - 1);
   return (-limit <= value) && (value < limit);
 }
 
-static inline bool IsUint(int N, word value) {
+static inline bool IsUint(int N, intptr_t value) {
   CHECK_LT(0, N);
-  CHECK_LT(N, kBitsPerWord);
-  word limit = static_cast<word>(1) << N;
+  CHECK_LT(N, kBitsPerIntPtrT);
+  intptr_t limit = static_cast<intptr_t>(1) << N;
   return (0 <= value) && (value < limit);
 }
 
-static inline bool IsAbsoluteUint(int N, word value) {
+static inline bool IsAbsoluteUint(int N, intptr_t value) {
   CHECK_LT(0, N);
-  CHECK_LT(N, kBitsPerWord);
+  CHECK_LT(N, kBitsPerIntPtrT);
   if (value < 0) value = -value;
   return IsUint(N, value);
 }
@@ -149,18 +145,6 @@
   return static_cast<uint32_t>(value >> 32);
 }
 
-// A static if which determines whether to return type A or B based on the condition boolean.
-template <bool condition, typename A, typename B>
-struct TypeStaticIf {
-  typedef A type;
-};
-
-// Specialization to handle the false case.
-template <typename A, typename B>
-struct TypeStaticIf<false, A,  B> {
-  typedef B type;
-};
-
 // Type identity.
 template <typename T>
 struct TypeIdentity {
@@ -203,18 +187,6 @@
   return reinterpret_cast<T*>(RoundUp(reinterpret_cast<uintptr_t>(x), n));
 }
 
-// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr.,
-// figure 3-3, page 48, where the function is called clp2.
-static inline uint32_t RoundUpToPowerOfTwo(uint32_t x) {
-  x = x - 1;
-  x = x | (x >> 1);
-  x = x | (x >> 2);
-  x = x | (x >> 4);
-  x = x | (x >> 8);
-  x = x | (x >> 16);
-  return x + 1;
-}
-
 template<typename T>
 static constexpr int CLZ(T x) {
   return (sizeof(T) == sizeof(uint32_t))
@@ -250,7 +222,7 @@
 // of V >= size of U (compile-time checked).
 template<typename U, typename V>
 static inline V bit_cast(U in) {
-  COMPILE_ASSERT(sizeof(U) <= sizeof(V), size_of_u_not_le_size_of_v);
+  static_assert(sizeof(U) <= sizeof(V), "Size of U not <= size of V");
   union {
     U u;
     V v;
@@ -315,6 +287,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);
 
@@ -404,13 +380,13 @@
 
 // Splits a string using the given separator character into a vector of
 // strings. Empty strings will be omitted.
-void Split(const std::string& s, char separator, std::vector<std::string>& result);
+void Split(const std::string& s, char separator, std::vector<std::string>* result);
 
 // Trims whitespace off both ends of the given string.
-std::string Trim(std::string s);
+std::string Trim(const std::string& s);
 
 // Joins a vector of strings into a single string, using the given separator.
-template <typename StringT> std::string Join(std::vector<StringT>& strings, char separator);
+template <typename StringT> std::string Join(const std::vector<StringT>& strings, char separator);
 
 // Returns the calling thread's tid. (The C libraries don't expose this.)
 pid_t GetTid();
@@ -491,18 +467,20 @@
 
   template <typename A, typename B>
   inline void operator() (A a, B b) const {
-    UNUSED(a);
-    UNUSED(b);
+    UNUSED(a, b);
   }
 
   template <typename A, typename B, typename C>
   inline void operator() (A a, B b, C c) const {
-    UNUSED(a);
-    UNUSED(b);
-    UNUSED(c);
+    UNUSED(a, b, c);
   }
 };
 
+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/utils_test.cc b/runtime/utils_test.cc
index 1b2c3ee..92323da 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -241,62 +241,62 @@
   expected.clear();
 
   actual.clear();
-  Split("", ':', actual);
+  Split("", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   actual.clear();
-  Split(":", ':', actual);
+  Split(":", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   expected.clear();
   expected.push_back("foo");
 
   actual.clear();
-  Split(":foo", ':', actual);
+  Split(":foo", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   actual.clear();
-  Split("foo:", ':', actual);
+  Split("foo:", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   actual.clear();
-  Split(":foo:", ':', actual);
+  Split(":foo:", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   expected.push_back("bar");
 
   actual.clear();
-  Split("foo:bar", ':', actual);
+  Split("foo:bar", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   actual.clear();
-  Split(":foo:bar", ':', actual);
+  Split(":foo:bar", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   actual.clear();
-  Split("foo:bar:", ':', actual);
+  Split("foo:bar:", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   actual.clear();
-  Split(":foo:bar:", ':', actual);
+  Split(":foo:bar:", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   expected.push_back("baz");
 
   actual.clear();
-  Split("foo:bar:baz", ':', actual);
+  Split("foo:bar:baz", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   actual.clear();
-  Split(":foo:bar:baz", ':', actual);
+  Split(":foo:bar:baz", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   actual.clear();
-  Split("foo:bar:baz:", ':', actual);
+  Split("foo:bar:baz:", ':', &actual);
   EXPECT_EQ(expected, actual);
 
   actual.clear();
-  Split(":foo:bar:baz:", ':', actual);
+  Split(":foo:bar:baz:", ':', &actual);
   EXPECT_EQ(expected, actual);
 }
 
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..e67067c 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,23 @@
     // 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_;
 };
 
+static_assert(sizeof(InstructionFlags) == sizeof(uint8_t),
+              "Size of InstructionFlags not equal to uint8_t");
+
 }  // 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 c88a295..a10c7cb 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"
@@ -104,7 +105,8 @@
   return false;
 }
 
-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()) {
@@ -134,20 +136,21 @@
     }
     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,
                                                         bool allow_soft_failures,
                                                         std::string* error) {
   DCHECK(class_def != nullptr);
-  const byte* class_data = dex_file->GetClassData(*class_def);
+  const uint8_t* class_data = dex_file->GetClassData(*class_def);
   if (class_data == nullptr) {
     // empty class, probably a marker interface
     return kNoFailure;
@@ -161,6 +164,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
@@ -174,17 +178,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);
@@ -205,6 +212,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
@@ -218,17 +226,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);
@@ -254,22 +265,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.
@@ -303,33 +314,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),
@@ -364,21 +383,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
@@ -388,12 +430,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);
 }
 
@@ -418,12 +462,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);
 }
 
@@ -582,9 +628,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) {
@@ -620,13 +666,17 @@
           << "'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.
-  const byte* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0);
+  const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0);
   uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
   for (uint32_t idx = 0; idx < handlers_size; idx++) {
@@ -638,16 +688,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();
         }
       }
     }
@@ -779,7 +834,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_ << ")";
@@ -788,7 +843,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_ << ")";
@@ -797,7 +852,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_ << ")";
@@ -806,7 +861,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_ << ")";
@@ -815,7 +870,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_ << ")";
@@ -830,7 +885,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_ << ")";
@@ -839,7 +894,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_ << ")";
@@ -1136,11 +1191,6 @@
   return os;
 }
 
-extern "C" void MethodVerifierGdbDump(MethodVerifier* v)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  v->Dump(std::cerr);
-}
-
 void MethodVerifier::Dump(std::ostream& os) {
   if (code_item_ == nullptr) {
     os << "Native method\n";
@@ -1157,10 +1207,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;
@@ -1200,12 +1250,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++;
   }
@@ -1232,31 +1282,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': {
@@ -1266,9 +1316,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;
       }
@@ -1330,6 +1387,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++) {
@@ -1367,8 +1425,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);
         }
       }
     }
@@ -1394,7 +1452,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).
@@ -1470,7 +1529,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";
   }
 
   /*
@@ -1506,31 +1565,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;
 
     /*
@@ -1545,13 +1604,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: {
@@ -1559,21 +1618,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;
@@ -1581,14 +1640,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));
           }
@@ -1596,15 +1655,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));
           }
@@ -1612,8 +1671,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 {
@@ -1621,7 +1680,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()) {
@@ -1643,75 +1702,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:
       /*
@@ -1735,7 +1794,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:
@@ -1749,10 +1808,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 "
@@ -1762,13 +1821,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;
@@ -1783,20 +1842,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;
@@ -1804,7 +1863,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
@@ -1816,12 +1875,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:
@@ -1837,39 +1896,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";
@@ -1885,20 +1944,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 "
@@ -1925,8 +1983,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();
@@ -1945,8 +2003,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";
@@ -1955,7 +2013,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";
@@ -2005,8 +2063,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() &&
@@ -2021,7 +2079,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
@@ -2039,17 +2097,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:
@@ -2065,7 +2123,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";
@@ -2218,20 +2276,18 @@
                        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_);
+        mirror::Class* return_type_class = h_called_method->GetReturnType(can_load_classes_);
         if (return_type_class != nullptr) {
           return_type = &reg_types_.FromClass(h_called_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) {
@@ -2239,10 +2295,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_));
       }
@@ -2256,7 +2312,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);
@@ -2266,18 +2322,16 @@
       } 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_);
+        mirror::Class* return_type_class = h_called_method->GetReturnType(can_load_classes_);
         if (return_type_class != nullptr) {
           return_type = &reg_types_.FromClass(return_type_descriptor,
                                               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) {
@@ -2288,7 +2342,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;
 
@@ -2299,7 +2353,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.
@@ -2318,14 +2372,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_));
       }
@@ -2348,10 +2402,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_));
         }
@@ -2376,7 +2429,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 {
@@ -2406,10 +2459,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_));
       }
@@ -2418,74 +2470,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:
@@ -2496,13 +2548,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:
@@ -2513,7 +2565,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;
@@ -2521,7 +2573,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:
@@ -2529,18 +2581,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;
@@ -2551,27 +2600,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:
@@ -2581,14 +2621,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:
@@ -2596,32 +2636,31 @@
     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;
     case Instruction::ADD_INT_LIT16:
-    case Instruction::RSUB_INT:
+    case Instruction::RSUB_INT_LIT16:
     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:
@@ -2631,12 +2670,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.
@@ -2660,6 +2701,18 @@
     case Instruction::IPUT_QUICK:
       VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Integer(), true);
       break;
+    case Instruction::IPUT_BOOLEAN_QUICK:
+      VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Boolean(), true);
+      break;
+    case Instruction::IPUT_BYTE_QUICK:
+      VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Byte(), true);
+      break;
+    case Instruction::IPUT_CHAR_QUICK:
+      VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Char(), true);
+      break;
+    case Instruction::IPUT_SHORT_QUICK:
+      VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Short(), true);
+      break;
     case Instruction::IPUT_WIDE_QUICK:
       VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.LongLo(), true);
       break;
@@ -2672,10 +2725,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_));
         }
@@ -2693,10 +2745,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:
@@ -2741,7 +2789,7 @@
    * not expensive and it makes our debugging output cleaner.)
    */
   if (!just_set_result) {
-    work_line_->SetResultTypeToUnknown();
+    work_line_->SetResultTypeToUnknown(this);
   }
 
 
@@ -2766,7 +2814,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 */
@@ -2812,7 +2860,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)) {
@@ -2838,17 +2886,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();
         }
       }
       /*
@@ -2885,7 +2932,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;
@@ -2904,12 +2952,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());
         }
       }
     }
@@ -2932,7 +2980,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;
     }
   }
@@ -2944,7 +2992,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;
@@ -2956,21 +3005,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
@@ -2983,10 +3031,10 @@
   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);
+    const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0);
     uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
     for (uint32_t i = 0; i < handlers_size; i++) {
       CatchHandlerIterator iterator(handlers_ptr);
@@ -2995,7 +3043,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.
@@ -3035,7 +3083,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);
@@ -3046,8 +3094,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);
@@ -3060,7 +3108,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
@@ -3153,7 +3201,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;
@@ -3174,7 +3222,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;
@@ -3183,7 +3231,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);
       }
@@ -3216,18 +3264,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_) {
@@ -3304,7 +3351,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_)
@@ -3332,7 +3379,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;
@@ -3392,7 +3439,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;
   }
@@ -3417,7 +3464,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)) {
@@ -3453,9 +3500,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;
@@ -3481,7 +3528,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 {
@@ -3490,14 +3537,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) {
@@ -3505,41 +3552,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";
@@ -3556,9 +3604,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_));
         }
       }
@@ -3566,12 +3614,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();
@@ -3583,7 +3631,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;
@@ -3593,7 +3641,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;
@@ -3618,19 +3666,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);
@@ -3642,7 +3690,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);
         }
       }
     }
@@ -3652,7 +3700,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),
@@ -3663,14 +3711,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())) {
@@ -3684,10 +3732,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),
@@ -3698,14 +3746,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())) {
@@ -3726,7 +3774,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() &&
@@ -3752,20 +3800,20 @@
 }
 
 template <MethodVerifier::FieldAccessType kAccType>
-void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyISFieldAccess(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);
     if (UNLIKELY(have_pending_hard_failure_)) {
       return;
     }
   }
-  RegType* field_type = nullptr;
+  const RegType* field_type = nullptr;
   if (field != nullptr) {
     if (kAccType == FieldAccessType::kAccPut) {
       if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
@@ -3775,10 +3823,9 @@
       }
     }
 
-    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_);
     }
@@ -3786,14 +3833,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();
@@ -3811,7 +3858,7 @@
                                                 << "' in put-object";
         return;
       }
-      work_line_->VerifyRegisterType(vregA, *field_type);
+      work_line_->VerifyRegisterType(this, vregA, *field_type);
     }
   } else if (kAccType == FieldAccessType::kAccGet) {
     // sget or iget.
@@ -3836,14 +3883,14 @@
                                           << " 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_));
     }
   } else {
     LOG(FATAL) << "Unexpected case.";
@@ -3857,8 +3904,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;
@@ -3874,7 +3925,7 @@
 }
 
 template <MethodVerifier::FieldAccessType kAccType>
-void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, const RegType& insn_type,
                                             bool is_primitive) {
   DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_);
 
@@ -3894,7 +3945,7 @@
   }
 
   // Get the field type.
-  RegType* field_type;
+  const RegType* field_type;
   {
     mirror::Class* field_type_class;
     {
@@ -3927,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();
@@ -3969,7 +4020,7 @@
                                           << "' in put-object";
         return;
       }
-      work_line_->VerifyRegisterType(vregA, *field_type);
+      work_line_->VerifyRegisterType(this, vregA, *field_type);
     }
   } else if (kAccType == FieldAccessType::kAccGet) {
     if (is_primitive) {
@@ -3993,14 +4044,14 @@
                                           << " 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_));
     }
   } else {
     LOG(FATAL) << "Unexpected case.";
@@ -4015,6 +4066,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;
@@ -4029,7 +4093,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.
@@ -4037,33 +4101,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);
@@ -4079,23 +4143,17 @@
   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) {
+      mirror::Class* return_type_class = mirror_method_->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) {
@@ -4103,23 +4161,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_;
@@ -4130,16 +4188,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);
@@ -4170,7 +4231,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 d7dd4b8..357acf0 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,22 +490,22 @@
       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
@@ -512,7 +517,7 @@
     kAccPut
   };
   template <FieldAccessType kAccType>
-  void VerifyISFieldAccess(const Instruction* inst, RegType& insn_type,
+  void VerifyISFieldAccess(const Instruction* inst, const RegType& insn_type,
                            bool is_primitive, bool is_static)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -522,12 +527,12 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   template <FieldAccessType kAccType>
-  void VerifyQuickFieldAccess(const Instruction* inst, RegType& insn_type, bool is_primitive)
+  void VerifyQuickFieldAccess(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_);
 
   /*
@@ -535,7 +540,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_);
 
   /*
@@ -601,6 +606,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
@@ -621,16 +641,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_;
@@ -647,17 +670,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 4bb99c1..1dfbe51 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,12 @@
   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, ComputeModifiedUtf8Hash(descriptor), loader);
+    klass = class_linker->LookupClass(self, descriptor, ComputeModifiedUtf8Hash(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 +158,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 +171,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 +212,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 +221,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 +239,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 +253,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 +273,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 +283,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 +316,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 +353,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 +372,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 +403,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 +421,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 +431,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 +445,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 +471,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 +490,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 +508,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 +526,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 +550,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 +561,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 +580,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..8f7823a 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,13 +114,11 @@
     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));
-    while (!monitors_.empty()) {
-      monitors_.pop_back();
-    }
+    monitors_.clear();
     reg_to_lock_depths_.clear();
   }
 
@@ -126,7 +128,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 +136,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 +153,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 +175,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 +207,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 +250,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 +270,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 +342,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 +363,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..16338c4 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -18,9 +18,12 @@
 
 #include <stdlib.h>
 
+#include <sstream>
+
 #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 +31,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,10 +50,11 @@
 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;
+jclass WellKnownClasses::java_util_ArrayList;
 jclass WellKnownClasses::java_util_Collections;
 jclass WellKnownClasses::libcore_util_EmptyArray;
 jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk;
@@ -77,7 +81,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 +90,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;
@@ -94,8 +98,10 @@
 jfieldID WellKnownClasses::java_lang_Thread_priority;
 jfieldID WellKnownClasses::java_lang_Thread_uncaughtHandler;
 jfieldID WellKnownClasses::java_lang_Thread_nativePeer;
+jfieldID WellKnownClasses::java_lang_ThreadGroup_groups;
 jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
 jfieldID WellKnownClasses::java_lang_ThreadGroup_name;
+jfieldID WellKnownClasses::java_lang_ThreadGroup_parent;
 jfieldID WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
 jfieldID WellKnownClasses::java_lang_Throwable_cause;
 jfieldID WellKnownClasses::java_lang_Throwable_detailMessage;
@@ -107,6 +113,8 @@
 jfieldID WellKnownClasses::java_lang_reflect_Proxy_h;
 jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity;
 jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress;
+jfieldID WellKnownClasses::java_util_ArrayList_array;
+jfieldID WellKnownClasses::java_util_ArrayList_size;
 jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST;
 jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
 jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data;
@@ -122,18 +130,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 +170,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,10 +189,12 @@
   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");
+  java_util_ArrayList = CacheClass(env, "java/util/ArrayList");
   java_util_Collections = CacheClass(env, "java/util/Collections");
   libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray");
   org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
@@ -192,7 +216,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 +225,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;");
@@ -209,8 +233,10 @@
   java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
   java_lang_Thread_uncaughtHandler = CacheField(env, java_lang_Thread, false, "uncaughtHandler", "Ljava/lang/Thread$UncaughtExceptionHandler;");
   java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
+  java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "Ljava/util/List;");
   java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
   java_lang_ThreadGroup_name = CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;");
+  java_lang_ThreadGroup_parent = CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;");
   java_lang_ThreadGroup_systemThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
   java_lang_Throwable_cause = CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;");
   java_lang_Throwable_detailMessage = CacheField(env, java_lang_Throwable, false, "detailMessage", "Ljava/lang/String;");
@@ -222,6 +248,8 @@
   java_lang_reflect_Proxy_h = CacheField(env, java_lang_reflect_Proxy, false, "h", "Ljava/lang/reflect/InvocationHandler;");
   java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I");
   java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "effectiveDirectAddress", "J");
+  java_util_ArrayList_array = CacheField(env, java_util_ArrayList, false, "array", "[Ljava/lang/Object;");
+  java_util_ArrayList_size = CacheField(env, java_util_ArrayList, false, "size", "I");
   java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
   libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
   org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 3780733..d651b90 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,8 +62,9 @@
   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_ArrayList;
   static jclass java_util_Collections;
   static jclass java_nio_DirectByteBuffer;
   static jclass libcore_util_EmptyArray;
@@ -91,7 +92,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 +100,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;
@@ -111,8 +112,10 @@
   static jfieldID java_lang_Thread_priority;
   static jfieldID java_lang_Thread_uncaughtHandler;
   static jfieldID java_lang_Thread_nativePeer;
+  static jfieldID java_lang_ThreadGroup_groups;
   static jfieldID java_lang_ThreadGroup_mainThreadGroup;
   static jfieldID java_lang_ThreadGroup_name;
+  static jfieldID java_lang_ThreadGroup_parent;
   static jfieldID java_lang_ThreadGroup_systemThreadGroup;
   static jfieldID java_lang_Throwable_cause;
   static jfieldID java_lang_Throwable_detailMessage;
@@ -121,6 +124,8 @@
   static jfieldID java_lang_Throwable_suppressedExceptions;
   static jfieldID java_nio_DirectByteBuffer_capacity;
   static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress;
+  static jfieldID java_util_ArrayList_array;
+  static jfieldID java_util_ArrayList_size;
   static jfieldID java_util_Collections_EMPTY_LIST;
   static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_data;
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/Android.mk b/sigchainlib/Android.mk
index e52adfc..b7ff360 100644
--- a/sigchainlib/Android.mk
+++ b/sigchainlib/Android.mk
@@ -30,6 +30,18 @@
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
 include $(BUILD_SHARED_LIBRARY)
 
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
+LOCAL_SRC_FILES := sigchain.cc
+LOCAL_CLANG = $(ART_TARGET_CLANG)
+LOCAL_MODULE:= libsigchain
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
+include $(BUILD_STATIC_LIBRARY)
+
 # Build host library.
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
@@ -43,3 +55,17 @@
 LOCAL_LDLIBS = -ldl
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+LOCAL_MODULE_TAGS := optional
+LOCAL_IS_HOST_MODULE := true
+LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+LOCAL_CLANG = $(ART_HOST_CLANG)
+LOCAL_SRC_FILES := sigchain.cc
+LOCAL_MODULE:= libsigchain
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_LDLIBS = -ldl
+LOCAL_MULTILIB := both
+include external/libcxx/libcxx.mk
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 4f16c7f..601e321 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -30,6 +30,7 @@
 
 #if defined(__APPLE__)
 #define _NSIG NSIG
+#define sighandler_t sig_t
 #endif
 
 namespace art {
@@ -38,7 +39,7 @@
 
 class SignalAction {
  public:
-  SignalAction() : claimed_(false) {
+  SignalAction() : claimed_(false), uses_old_style_(false) {
   }
 
   // Claim the signal and keep the action specified.
@@ -64,13 +65,22 @@
   }
 
   // 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
@@ -122,7 +132,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 {
@@ -154,8 +164,6 @@
   }
 }
 
-// These functions are C linkage since they replace the functions in libc.
-
 extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
   // If this signal has been claimed as a signal chain, record the user's
   // action but don't pass it on to the kernel.
@@ -166,7 +174,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;
   }
@@ -190,6 +198,45 @@
   return linked_sigaction(signal, new_action, old_action);
 }
 
+extern "C" 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);
+}
+
 extern "C" 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;
@@ -253,5 +300,6 @@
   }
   initialized = true;
 }
+
 }   // namespace art
 
diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h
index 59a1f1e..79b76a7 100644
--- a/sigchainlib/sigchain.h
+++ b/sigchainlib/sigchain.h
@@ -19,14 +19,18 @@
 
 #include <signal.h>
 
+namespace art {
+
 extern "C" void InitializeSignalChain();
 
 extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction);
 
-extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action);
-
 extern "C" void UnclaimSignalChain(int signal);
 
 extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
 
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action);
+
+}  // namespace art
+
 #endif  // ART_SIGCHAINLIB_SIGCHAIN_H_
diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc
index b0a6ebb..76779ab 100644
--- a/sigchainlib/sigchain_dummy.cc
+++ b/sigchainlib/sigchain_dummy.cc
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <stdio.h>
+#include <stdlib.h>
+
 #ifdef HAVE_ANDROID_OS
 #include <android/log.h>
 #else
@@ -21,11 +24,10 @@
 #include <iostream>
 #endif
 
-#include <stdlib.h>
-#include <stdio.h>
-
 #include "sigchain.h"
 
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+
 static void log(const char* format, ...) {
   char buf[256];
   va_list ap;
@@ -39,22 +41,23 @@
   va_end(ap);
 }
 
-extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) {
+namespace art {
+
+
+extern "C" void ClaimSignalChain(int signal ATTRIBUTE_UNUSED,
+                                 struct sigaction* oldaction ATTRIBUTE_UNUSED) {
   log("ClaimSignalChain is not exported by the main executable.");
   abort();
 }
 
-extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
-  log("EnsureFrontOfChain is not exported by the main executable.");
-  abort();
-}
-
-extern "C" void UnclaimSignalChain(int signal) {
+extern "C" void UnclaimSignalChain(int signal ATTRIBUTE_UNUSED) {
   log("UnclaimSignalChain is not exported by the main executable.");
   abort();
 }
 
-extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
+extern "C" void InvokeUserSignalHandler(int sig ATTRIBUTE_UNUSED,
+                                        siginfo_t* info ATTRIBUTE_UNUSED,
+                                        void* context ATTRIBUTE_UNUSED) {
   log("InvokeUserSignalHandler is not exported by the main executable.");
   abort();
 }
@@ -63,3 +66,11 @@
   log("InitializeSignalChain is not exported by the main executable.");
   abort();
 }
+
+extern "C" void EnsureFrontOfChain(int signal ATTRIBUTE_UNUSED,
+                                   struct sigaction* expected_action ATTRIBUTE_UNUSED) {
+  log("EnsureFrontOfChain is not exported by the main executable.");
+  abort();
+}
+
+}  // namespace art
diff --git a/sigchainlib/version-script.txt b/sigchainlib/version-script.txt
index 5c72a3e..ce15054 100644
--- a/sigchainlib/version-script.txt
+++ b/sigchainlib/version-script.txt
@@ -1,10 +1,10 @@
 {
 global:
   ClaimSignalChain;
-  EnsureFrontOfChain;
   UnclaimSignalChain;
   InvokeUserSignalHandler;
   InitializeSignalChain;
+  EnsureFrontOfChain;
   sigaction;
   signal;
   sigprocmask;
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..c2877be 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -172,7 +172,7 @@
 constexpr size_t kByteReturnSize = 7;
 jbyte byte_returns[kByteReturnSize] = { 0, 1, 2, 127, -1, -2, -128 };
 
-extern "C" jbyte JNICALL Java_Main_byteMethod(JNIEnv* env, jclass klass, jbyte b1, jbyte b2,
+extern "C" jbyte JNICALL Java_Main_byteMethod(JNIEnv*, jclass, jbyte b1, jbyte b2,
                                               jbyte b3, jbyte b4, jbyte b5, jbyte b6,
                                               jbyte b7, jbyte b8, jbyte b9, jbyte b10) {
   // We use b1 to drive the output.
@@ -197,7 +197,7 @@
     static_cast<jshort>(0x8000) };
 // The weird static_cast is because short int is only guaranteed down to -32767, not Java's -32768.
 
-extern "C" jshort JNICALL Java_Main_shortMethod(JNIEnv* env, jclass klass, jshort s1, jshort s2,
+extern "C" jshort JNICALL Java_Main_shortMethod(JNIEnv*, jclass, jshort s1, jshort s2,
                                                 jshort s3, jshort s4, jshort s5, jshort s6,
                                                 jshort s7, jshort s8, jshort s9, jshort s10) {
   // We use s1 to drive the output.
@@ -217,7 +217,7 @@
   return short_returns[s1];
 }
 
-extern "C" jboolean JNICALL Java_Main_booleanMethod(JNIEnv* env, jclass klass, jboolean b1,
+extern "C" jboolean JNICALL Java_Main_booleanMethod(JNIEnv*, jclass, jboolean b1,
                                                     jboolean b2, jboolean b3, jboolean b4,
                                                     jboolean b5, jboolean b6, jboolean b7,
                                                     jboolean b8, jboolean b9, jboolean b10) {
@@ -239,7 +239,7 @@
 constexpr size_t kCharReturnSize = 8;
 jchar char_returns[kCharReturnSize] = { 0, 1, 2, 127, 255, 256, 15000, 34000 };
 
-extern "C" jchar JNICALL Java_Main_charMethod(JNIEnv* env, jclass klacc, jchar c1, jchar c2,
+extern "C" jchar JNICALL Java_Main_charMethod(JNIEnv*, jclass, jchar c1, jchar c2,
                                               jchar c3, jchar c4, jchar c5, jchar c6, jchar c7,
                                               jchar c8, jchar c9, jchar c10) {
   // We use c1 to drive the output.
@@ -312,7 +312,7 @@
 }
 
 // http://b/16867274
-extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetCallingClassLoader(JNIEnv* env,
+extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetCallingClassLoader(JNIEnv*,
                                                                                    jclass) {
   PthreadHelper(&testShallowGetCallingClassLoader);
 }
@@ -350,6 +350,201 @@
   // ourselves.
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv* env, jclass) {
+extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv*, 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..631c4be 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -14,138 +14,62 @@
  * 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(dex_pc, abort_if_not_found, ...) do { \
+  int t[] = {__VA_ARGS__}; \
+  int t_size = sizeof(t) / sizeof(*t); \
+  uintptr_t native_quick_pc = m->ToNativeQuickPc(dex_pc, abort_if_not_found); \
+  if (native_quick_pc != UINTPTR_MAX) { \
+    CheckReferences(t, t_size, m->NativeQuickPcOffset(native_quick_pc)); \
+  } \
+} 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, true, 8);  // v8: this
+      CHECK_REGS_CONTAIN_REFS(0x06U, true, 8, 1);  // v8: this, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x08U, true, 8, 3, 1);  // v8: this, v3: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x0cU, true, 8, 3, 1);  // v8: this, v3: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x0eU, true, 8, 3, 1);  // v8: this, v3: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x10U, true, 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);
+      // We eliminate the non-live registers at a return, so only v3 is live.
+      // Note that it is OK for a compiler to not have a dex map at this dex PC because
+      // a return is not a safepoint.
+      CHECK_REGS_CONTAIN_REFS(0x13U, false);  // v3: y
+      CHECK_REGS_CONTAIN_REFS(0x18U, true, 8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
+      CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1, 0);  // v8: this, v5: x[1], v2: y, v1: x, v0: ex
+      CHECK_REGS_CONTAIN_REFS(0x1dU, true, 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, true, 8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
+      CHECK_REGS_CONTAIN_REFS(0x21U, true, 8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
+      CHECK_REGS_CONTAIN_REFS(0x27U, true, 8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x29U, true, 8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x2cU, true, 8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x2fU, true, 8, 4, 3, 2, 1);  // v8: this, v4: ex, v3: y, v2: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x32U, true, 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..31371f6 100644
--- a/test/004-SignalTest/signaltest.cc
+++ b/test/004-SignalTest/signaltest.cc
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
+#include <jni.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/ucontext.h>
 #include <unistd.h>
 
-#include "jni.h"
-
-#include <sys/ucontext.h>
+#include "base/macros.h"
 
 static int signal_count;
 static const int kMaxSignal = 2;
@@ -47,7 +47,8 @@
 #endif
 #endif
 
-static void signalhandler(int sig, siginfo_t* info, void* context) {
+static void signalhandler(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                          void* context) {
   printf("signal caught\n");
   ++signal_count;
   if (signal_count > kMaxSignal) {
@@ -87,12 +88,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/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index 8c8ad16..743d62c 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -25,14 +25,14 @@
   private static void check(int actual, int expected, String msg) {
     if (actual != expected) {
       System.out.println(msg + " : " + actual + " != " + expected);
-      System.exit(-1);
+      System.exit(1);
     }
   }
 
   private static void check(long actual, long expected, String msg) {
     if (actual != expected) {
       System.out.println(msg + " : " + actual + " != " + expected);
-      System.exit(-1);
+      System.exit(1);
     }
   }
 
diff --git a/test/005-annotations/build b/test/005-annotations/build
index 1690213..2474055 100644
--- a/test/005-annotations/build
+++ b/test/005-annotations/build
@@ -23,5 +23,6 @@
 ${JAVAC} -d classes `find src -name '*.java'`
 
 # ...but not at run time.
-rm classes/android/test/anno/MissingAnnotation.class
+rm 'classes/android/test/anno/MissingAnnotation.class'
+rm 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class'
 ${DX} -JXmx256m --debug --dex --output=$TEST_NAME.jar classes
diff --git a/test/005-annotations/src/android/test/anno/ClassWithInnerAnnotationClass.java b/test/005-annotations/src/android/test/anno/ClassWithInnerAnnotationClass.java
new file mode 100644
index 0000000..c69e01a
--- /dev/null
+++ b/test/005-annotations/src/android/test/anno/ClassWithInnerAnnotationClass.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+public class ClassWithInnerAnnotationClass {
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface MissingInnerAnnotationClass {}
+}
diff --git a/test/005-annotations/src/android/test/anno/TestAnnotations.java b/test/005-annotations/src/android/test/anno/TestAnnotations.java
index 4eabb12..1deff33 100644
--- a/test/005-annotations/src/android/test/anno/TestAnnotations.java
+++ b/test/005-annotations/src/android/test/anno/TestAnnotations.java
@@ -149,26 +149,24 @@
 
         testArrays();
         testArrayProblem();
-        //System.exit(0);
 
         System.out.println(
             "AnnoSimpleField " + AnnoSimpleField.class.isAnnotation() +
             ", SimplyNoted " + SimplyNoted.class.isAnnotation());
 
-        Class clazz;
-        clazz = SimplyNoted.class;
-        printAnnotations(clazz);
-        clazz = INoted.class;
-        printAnnotations(clazz);
-        clazz = SubNoted.class;
-        printAnnotations(clazz);
-        clazz = FullyNoted.class;
-        printAnnotations(clazz);
+        printAnnotations(SimplyNoted.class);
+        printAnnotations(INoted.class);
+        printAnnotations(SubNoted.class);
+        printAnnotations(FullyNoted.class);
 
-        Annotation anno;
+        try {
+            ClassWithInnerAnnotationClass.class.getDeclaredClasses();
+            throw new AssertionError();
+        } catch (NoClassDefFoundError expected) {
+        }
 
         // this is expected to be non-null
-        anno = SimplyNoted.class.getAnnotation(AnnoSimpleType.class);
+        Annotation anno = SimplyNoted.class.getAnnotation(AnnoSimpleType.class);
         System.out.println("SimplyNoted.get(AnnoSimpleType) = " + anno);
         // this is non-null if the @Inherited tag is present
         anno = SubNoted.class.getAnnotation(AnnoSimpleType.class);
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/023-many-interfaces/build b/test/023-many-interfaces/build
index 4ea52d9..ad42a2d 100644
--- a/test/023-many-interfaces/build
+++ b/test/023-many-interfaces/build
@@ -18,7 +18,7 @@
 set -e
 
 # Write out a bunch of interface source files.
-gcc -o iface-gen iface-gen.c
+gcc -Wall -Werror -o iface-gen iface-gen.c
 ./iface-gen
 
 mkdir classes
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..e3cf4ee 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -120,6 +120,60 @@
 
         System.out.println("reborn: " + FinalizerTest.mReborn);
         System.out.println("wimp: " + wimpString(wimp));
+        // Test runFinalization with multiple objects.
+        runFinalizationTest();
+    }
+
+    static class FinalizeCounter {
+      public static final int maxCount = 1024;
+      public static boolean finalized[] = new boolean[maxCount];
+      private static Object finalizeLock = new Object();
+      private static volatile int finalizeCount = 0;
+      private int index;
+      static int getCount() {
+        return finalizeCount;
+      }
+      static void printNonFinalized() {
+        for (int i = 0; i < maxCount; ++i) {
+          if (!FinalizeCounter.finalized[i]) {
+            System.err.println("Element " + i + " was not finalized");
+          }
+        }
+      }
+      FinalizeCounter(int index) {
+        this.index = index;
+      }
+      protected void finalize() {
+        synchronized(finalizeLock) {
+          ++finalizeCount;
+          finalized[index] = true;
+        }
+      }
+    }
+
+    private static void allocFinalizableObjects(int count) {
+      Object[] objs = new Object[count];
+      for (int i = 0; i < count; ++i) {
+        objs[i] = new FinalizeCounter(i);
+      }
+    }
+
+    private static void runFinalizationTest() {
+      allocFinalizableObjects(FinalizeCounter.maxCount);
+      Runtime.getRuntime().gc();
+      System.runFinalization();
+      System.out.println("Finalized " + FinalizeCounter.getCount() + " / "  + FinalizeCounter.maxCount);
+      if (FinalizeCounter.getCount() != FinalizeCounter.maxCount) {
+        // Print out all the finalized elements.
+        FinalizeCounter.printNonFinalized();
+        // Try to sleep for a couple seconds to see if the objects became finalized after.
+        try {
+          java.lang.Thread.sleep(2000);
+        } catch (InterruptedException e) {
+        }
+        System.out.println("After sleep finalized " + FinalizeCounter.getCount() + " / "  + FinalizeCounter.maxCount);
+        FinalizeCounter.printNonFinalized();
+      }
     }
 
     public static class FinalizerTest {
diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java
index 11eb773..3fe3881 100644
--- a/test/046-reflect/src/Main.java
+++ b/test/046-reflect/src/Main.java
@@ -693,10 +693,35 @@
         }
     }
 
+    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();
diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc
index 2f5fffc..2b8e675 100644
--- a/test/051-thread/thread_test.cc
+++ b/test/051-thread/thread_test.cc
@@ -14,16 +14,20 @@
  * limitations under the License.
  */
 
+#include "base/macros.h"
 #include "jni.h"
 #include "thread-inl.h"
 
 namespace art {
 
-extern "C" JNIEXPORT jint JNICALL Java_Main_getNativePriority(JNIEnv* env, jclass) {
+extern "C" JNIEXPORT jint JNICALL Java_Main_getNativePriority(JNIEnv* env,
+                                                              jclass clazz ATTRIBUTE_UNUSED) {
   return ThreadForEnv(env)->GetNativePriority();
 }
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(JNIEnv* env, jclass) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(
+    JNIEnv* env ATTRIBUTE_UNUSED,
+    jclass clazz ATTRIBUTE_UNUSED) {
 #if defined(HAVE_ANDROID_OS)
   return JNI_TRUE;
 #else
diff --git a/test/074-gc-thrash/src/Main.java b/test/074-gc-thrash/src/Main.java
index 78413f3..32fbf2d 100644
--- a/test/074-gc-thrash/src/Main.java
+++ b/test/074-gc-thrash/src/Main.java
@@ -183,7 +183,11 @@
     }
 
     private String makeString(int val) {
-        return new String("Robin" + val);
+        try {
+            return new String("Robin" + val);
+        } catch (OutOfMemoryError e) {
+            return null;
+        }
     }
 }
 
@@ -251,58 +255,63 @@
      * valid and invalid references on the stack.
      */
     private String dive(int depth, int iteration) {
-        String str0;
-        String str1;
-        String str2;
-        String str3;
-        String str4;
-        String str5;
-        String str6;
-        String str7;
-        String funStr;
+        try {
+            String str0;
+            String str1;
+            String str2;
+            String str3;
+            String str4;
+            String str5;
+            String str6;
+            String str7;
+            String funStr = "";
+            switch (iteration % 8) {
+                case 0:
+                    funStr = str0 = makeString(iteration);
+                    break;
+                case 1:
+                    funStr = str1 = makeString(iteration);
+                    break;
+                case 2:
+                    funStr = str2 = makeString(iteration);
+                    break;
+                case 3:
+                    funStr = str3 = makeString(iteration);
+                    break;
+                case 4:
+                    funStr = str4 = makeString(iteration);
+                    break;
+                case 5:
+                    funStr = str5 = makeString(iteration);
+                    break;
+                case 6:
+                    funStr = str6 = makeString(iteration);
+                    break;
+                case 7:
+                    funStr = str7 = makeString(iteration);
+                    break;
+            }
 
-        funStr = "";
-
-        switch (iteration % 8) {
-            case 0:
-                funStr = str0 = makeString(iteration);
-                break;
-            case 1:
-                funStr = str1 = makeString(iteration);
-                break;
-            case 2:
-                funStr = str2 = makeString(iteration);
-                break;
-            case 3:
-                funStr = str3 = makeString(iteration);
-                break;
-            case 4:
-                funStr = str4 = makeString(iteration);
-                break;
-            case 5:
-                funStr = str5 = makeString(iteration);
-                break;
-            case 6:
-                funStr = str6 = makeString(iteration);
-                break;
-            case 7:
-                funStr = str7 = makeString(iteration);
-                break;
+            strong[depth] = funStr;
+            weak[depth] = new WeakReference(funStr);
+            if (depth+1 < MAX_DEPTH)
+                dive(depth+1, iteration+1);
+            else
+                Main.sleep(100);
+            return funStr;
+        } catch (OutOfMemoryError e) {
+            // Silently ignore OOME since gc stress mode causes them to occur but shouldn't be a
+            // test failure.
         }
-
-        strong[depth] = funStr;
-        weak[depth] = new WeakReference(funStr);
-
-        if (depth+1 < MAX_DEPTH)
-            dive(depth+1, iteration+1);
-        else
-            Main.sleep(100);
-
-        return funStr;
+        return "";
     }
 
     private String makeString(int val) {
-        return new String("Deep" + val);
+        try {
+            return new String("Deep" + val);
+        } catch (OutOfMemoryError e) {
+            return null;
+        }
     }
 }
 
@@ -319,13 +328,16 @@
         Main.startupDelay();
 
         while (!Main.quit) {
-            chunk = new byte[100000];
-            pretendToUse(chunk);
+            try {
+                chunk = new byte[100000];
+                pretendToUse(chunk);
 
-            count++;
-            if ((count % 500) == 0) {
-                Main.sleep(400);
-                sleepCount++;
+                count++;
+                if ((count % 500) == 0) {
+                    Main.sleep(400);
+                    sleepCount++;
+                }
+            } catch (OutOfMemoryError e) {
             }
         }
 
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/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt
index 5251c17..51bf847 100644
--- a/test/083-compiler-regressions/expected.txt
+++ b/test/083-compiler-regressions/expected.txt
@@ -17,6 +17,7 @@
 b13679511Test finishing
 b16177324TestWrapper caught NPE as expected.
 b16230771TestWrapper caught NPE as expected.
+b17969907TestWrapper caught NPE as expected.
 largeFrame passes
 largeFrameFloat passes
 mulBy1Test passes
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index 8010711..9ad8ea7 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -39,6 +39,7 @@
         b13679511Test();
         b16177324TestWrapper();
         b16230771TestWrapper();
+        b17969907TestWrapper();
         largeFrameTest();
         largeFrameTestFloat();
         mulBy1Test();
@@ -990,6 +991,24 @@
       }
     }
 
+    static void b17969907TestWrapper() {
+      try {
+        b17969907Test();
+        System.out.println("b17969907Test unexpectedly didn't throw NPE.");
+      } catch (NullPointerException expected) {
+        System.out.println("b17969907TestWrapper caught NPE as expected.");
+      }
+    }
+
+    public static void b17969907Test() {
+      Integer i = new Integer(1);
+      int sum = 0;
+      while (sum < 100) {
+        sum += i;
+        i = null;
+      }
+    }
+
     static double TooManyArgs(
           long l00,
           long l01,
diff --git a/test/089-many-methods/check b/test/089-many-methods/check
new file mode 100755
index 0000000..65b7139
--- /dev/null
+++ b/test/089-many-methods/check
@@ -0,0 +1,20 @@
+#!/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.
+
+# Strip build error debug messages, as they are environment-specific.
+sed -e '/^Failed to build/d' -e '/^Non-canonical tmpdir/d' -e '/^Args:/d' -e '/^Max filename/d' -e '/^Max pathlength/d' "$2" > "$2.tmp"
+
+diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
\ No newline at end of file
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index 23145e3..6bcc1f5 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -18,13 +18,14 @@
 
 #include <algorithm>
 #include <dlfcn.h>
+#include <jni.h>
 #include <vector>
 
-#include "jni.h"
 #include "stdio.h"
 #include "unistd.h"
 #include "sys/stat.h"
 
+#include "base/macros.h"
 #include "nativebridge/native_bridge.h"
 
 struct NativeBridgeMethod {
@@ -209,7 +210,8 @@
 
 // NativeBridgeCallbacks implementations
 extern "C" bool native_bridge_initialize(const android::NativeBridgeRuntimeCallbacks* art_cbs,
-                                         const char* app_code_cache_dir, const char* isa) {
+                                         const char* app_code_cache_dir,
+                                         const char* isa ATTRIBUTE_UNUSED) {
   struct stat st;
   if ((app_code_cache_dir != nullptr)
       && (stat(app_code_cache_dir, &st) == 0)
@@ -248,7 +250,7 @@
 }
 
 extern "C" void* native_bridge_getTrampoline(void* handle, const char* name, const char* shorty,
-                                             uint32_t len) {
+                                             uint32_t len ATTRIBUTE_UNUSED) {
   printf("Getting trampoline for %s with shorty %s.\n", name, shorty);
 
   // The name here is actually the JNI name, so we can directly do the lookup.
diff --git a/test/115-native-bridge/run b/test/115-native-bridge/run
index e475cd6..32a9975 100644
--- a/test/115-native-bridge/run
+++ b/test/115-native-bridge/run
@@ -18,9 +18,9 @@
 
 # Use libnativebridgetest as a native bridge, start NativeBridgeMain (Main is JniTest main file).
 LIBPATH=$(echo ${ARGS} | sed -r 's/.*Djava.library.path=([^ ]*) .*/\1/')
-cp ${LIBPATH}/libnativebridgetest.so .
+ln -s ${LIBPATH}/libnativebridgetest.so .
 touch libarttest.so
-cp ${LIBPATH}/libarttest.so libarttest2.so
+ln -s ${LIBPATH}/libarttest.so libarttest2.so
 
 # pwd likely has /, so it's a pain to put that into a sed rule.
 LEFT=$(echo ${ARGS} | sed -r 's/-Djava.library.path.*//')
diff --git a/test/116-nodex2oat/nodex2oat.cc b/test/116-nodex2oat/nodex2oat.cc
index 04cac45..564d58d 100644
--- a/test/116-nodex2oat/nodex2oat.cc
+++ b/test/116-nodex2oat/nodex2oat.cc
@@ -38,7 +38,7 @@
   return NoDex2OatTest::hasOat(cls);
 }
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isDex2OatEnabled(JNIEnv*, jclass cls) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isDex2OatEnabled(JNIEnv*, jclass) {
   return Runtime::Current()->IsDex2OatEnabled();
 }
 
diff --git a/test/118-noimage-dex2oat/noimage-dex2oat.cc b/test/118-noimage-dex2oat/noimage-dex2oat.cc
index 7340d9e..c49a13e 100644
--- a/test/118-noimage-dex2oat/noimage-dex2oat.cc
+++ b/test/118-noimage-dex2oat/noimage-dex2oat.cc
@@ -34,11 +34,11 @@
   }
 };
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasImage(JNIEnv*, jclass cls) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasImage(JNIEnv*, jclass) {
   return Runtime::Current()->GetHeap()->HasImageSpace();
 }
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isImageDex2OatEnabled(JNIEnv*, jclass cls) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isImageDex2OatEnabled(JNIEnv*, jclass) {
   return Runtime::Current()->IsImageDex2OatEnabled();
 }
 
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/124-missing-classes/build b/test/124-missing-classes/build
new file mode 100644
index 0000000..62e57c8
--- /dev/null
+++ b/test/124-missing-classes/build
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+
+# Some classes are available at compile time...
+${JAVAC} -d classes `find src -name '*.java'`
+
+# ...but not at run time.
+rm 'classes/MissingClass.class'
+rm 'classes/Main$MissingInnerClass.class'
+${DX} -JXmx256m --debug --dex --output=$TEST_NAME.jar classes
diff --git a/test/124-missing-classes/expected.txt b/test/124-missing-classes/expected.txt
new file mode 100644
index 0000000..ce761c3
--- /dev/null
+++ b/test/124-missing-classes/expected.txt
@@ -0,0 +1,6 @@
+Test Started
+testMissingFieldType caught NoClassDefFoundError
+testMissingMethodReturnType caught NoClassDefFoundError
+testMissingMethodParameterType caught NoClassDefFoundError
+testMissingInnerClass caught NoClassDefFoundError
+Test Finished
diff --git a/test/124-missing-classes/info.txt b/test/124-missing-classes/info.txt
new file mode 100644
index 0000000..a734f99
--- /dev/null
+++ b/test/124-missing-classes/info.txt
@@ -0,0 +1 @@
+Tests the effects of missing classes.
diff --git a/test/124-missing-classes/src/Main.java b/test/124-missing-classes/src/Main.java
new file mode 100644
index 0000000..1667d2d
--- /dev/null
+++ b/test/124-missing-classes/src/Main.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+public final class Main {
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("Test Started");
+        testMissingFieldType();
+        testMissingMethodReturnType();
+        testMissingMethodParameterType();
+        testMissingInnerClass();
+        System.out.println("Test Finished");
+    }
+
+    private static class ClassWithMissingFieldType {
+        MissingClass field;
+    }
+
+    private static void testMissingFieldType() throws Exception {
+        try {
+            ClassWithMissingFieldType.class.getDeclaredFields();
+            throw new AssertionError();
+        } catch (NoClassDefFoundError e) {
+            System.out.println("testMissingFieldType caught NoClassDefFoundError");
+        }
+    }
+
+    private static class ClassWithMissingMethodReturnType {
+        MissingClass method() {
+            return null;
+        }
+    }
+
+    private static void testMissingMethodReturnType() throws Exception {
+        try {
+            ClassWithMissingMethodReturnType.class.getDeclaredMethods();
+            throw new AssertionError();
+        } catch (NoClassDefFoundError e) {
+            System.out.println("testMissingMethodReturnType caught NoClassDefFoundError");
+        }
+    }
+
+    private static class ClassWithMissingMethodParameterType {
+        void method(MissingClass arg) {}
+    }
+
+    private static void testMissingMethodParameterType() throws Exception {
+        try {
+            ClassWithMissingMethodParameterType.class.getDeclaredMethods();
+            throw new AssertionError();
+        } catch (NoClassDefFoundError e) {
+            System.out.println("testMissingMethodParameterType caught NoClassDefFoundError");
+        }
+    }
+
+    private static final class MissingInnerClass {
+    }
+
+    private static void testMissingInnerClass() throws Exception {
+        try {
+            Main.class.getDeclaredClasses();
+            throw new AssertionError();
+        } catch (NoClassDefFoundError e) {
+            System.out.println("testMissingInnerClass caught NoClassDefFoundError");
+        }
+    }
+}
diff --git a/runtime/log_severity.h b/test/124-missing-classes/src/MissingClass.java
similarity index 68%
copy from runtime/log_severity.h
copy to test/124-missing-classes/src/MissingClass.java
index 31682df..33aaa56 100644
--- a/runtime/log_severity.h
+++ b/test/124-missing-classes/src/MissingClass.java
@@ -14,12 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
-
-typedef int LogSeverity;
-
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
-
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+public final class MissingClass {
+    public static final class MissingInnerClass {
+    }
+}
diff --git a/test/125-gc-and-classloading/expected.txt b/test/125-gc-and-classloading/expected.txt
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/test/125-gc-and-classloading/expected.txt
@@ -0,0 +1 @@
+PASS
diff --git a/test/125-gc-and-classloading/info.txt b/test/125-gc-and-classloading/info.txt
new file mode 100644
index 0000000..bb6bf12
--- /dev/null
+++ b/test/125-gc-and-classloading/info.txt
@@ -0,0 +1 @@
+Tests class loading and GC running in parallel.
diff --git a/test/125-gc-and-classloading/src/Main.java b/test/125-gc-and-classloading/src/Main.java
new file mode 100644
index 0000000..61e123d
--- /dev/null
+++ b/test/125-gc-and-classloading/src/Main.java
@@ -0,0 +1,3072 @@
+/*
+ * 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.
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class Main {
+
+    public static void main(String[] args) throws Exception {
+        // Try to cause a class loading/linking while GC is running.
+        CountDownLatch cdl = new CountDownLatch(1);
+        GcThread gcThread = new GcThread(cdl);
+        ClassLoadingThread classLoadingThread = new ClassLoadingThread(cdl);
+        gcThread.start();
+        classLoadingThread.start();
+        gcThread.join();
+        classLoadingThread.join();
+        System.out.println("PASS");
+    }
+
+    static class GcThread extends Thread {
+        CountDownLatch cdl;
+
+        GcThread(CountDownLatch cdl) {
+            this.cdl = cdl;
+        }
+
+        public void run() {
+            for (int i = 0; i < 10; ++i) {
+                Runtime.getRuntime().gc();
+                if (i == 0) {
+                    cdl.countDown();
+                }
+            }
+        }
+    }
+
+    static class ClassLoadingThread extends Thread {
+        CountDownLatch cdl;
+
+        ClassLoadingThread(CountDownLatch cdl) {
+            this.cdl = cdl;
+        }
+
+        public void run() {
+            try {
+                cdl.await();
+                Class c0 = Class.forName("Main$BigClass");
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    // A class with lots of fields so that the class loading/linking takes a long time.
+    // It is an abstract class to exercise the non-embedded imt/vtable case.
+    static abstract class BigClass {
+        static Object sf1;
+        static Object sf2;
+        static Object sf3;
+        static Object sf4;
+        static Object sf5;
+        static Object sf6;
+        static Object sf7;
+        static Object sf8;
+        static Object sf9;
+        static Object sf10;
+        static Object sf11;
+        static Object sf12;
+        static Object sf13;
+        static Object sf14;
+        static Object sf15;
+        static Object sf16;
+        static Object sf17;
+        static Object sf18;
+        static Object sf19;
+        static Object sf20;
+        static Object sf21;
+        static Object sf22;
+        static Object sf23;
+        static Object sf24;
+        static Object sf25;
+        static Object sf26;
+        static Object sf27;
+        static Object sf28;
+        static Object sf29;
+        static Object sf30;
+        static Object sf31;
+        static Object sf32;
+        static Object sf33;
+        static Object sf34;
+        static Object sf35;
+        static Object sf36;
+        static Object sf37;
+        static Object sf38;
+        static Object sf39;
+        static Object sf40;
+        static Object sf41;
+        static Object sf42;
+        static Object sf43;
+        static Object sf44;
+        static Object sf45;
+        static Object sf46;
+        static Object sf47;
+        static Object sf48;
+        static Object sf49;
+        static Object sf50;
+        static Object sf51;
+        static Object sf52;
+        static Object sf53;
+        static Object sf54;
+        static Object sf55;
+        static Object sf56;
+        static Object sf57;
+        static Object sf58;
+        static Object sf59;
+        static Object sf60;
+        static Object sf61;
+        static Object sf62;
+        static Object sf63;
+        static Object sf64;
+        static Object sf65;
+        static Object sf66;
+        static Object sf67;
+        static Object sf68;
+        static Object sf69;
+        static Object sf70;
+        static Object sf71;
+        static Object sf72;
+        static Object sf73;
+        static Object sf74;
+        static Object sf75;
+        static Object sf76;
+        static Object sf77;
+        static Object sf78;
+        static Object sf79;
+        static Object sf80;
+        static Object sf81;
+        static Object sf82;
+        static Object sf83;
+        static Object sf84;
+        static Object sf85;
+        static Object sf86;
+        static Object sf87;
+        static Object sf88;
+        static Object sf89;
+        static Object sf90;
+        static Object sf91;
+        static Object sf92;
+        static Object sf93;
+        static Object sf94;
+        static Object sf95;
+        static Object sf96;
+        static Object sf97;
+        static Object sf98;
+        static Object sf99;
+        static Object sf100;
+        static Object sf101;
+        static Object sf102;
+        static Object sf103;
+        static Object sf104;
+        static Object sf105;
+        static Object sf106;
+        static Object sf107;
+        static Object sf108;
+        static Object sf109;
+        static Object sf110;
+        static Object sf111;
+        static Object sf112;
+        static Object sf113;
+        static Object sf114;
+        static Object sf115;
+        static Object sf116;
+        static Object sf117;
+        static Object sf118;
+        static Object sf119;
+        static Object sf120;
+        static Object sf121;
+        static Object sf122;
+        static Object sf123;
+        static Object sf124;
+        static Object sf125;
+        static Object sf126;
+        static Object sf127;
+        static Object sf128;
+        static Object sf129;
+        static Object sf130;
+        static Object sf131;
+        static Object sf132;
+        static Object sf133;
+        static Object sf134;
+        static Object sf135;
+        static Object sf136;
+        static Object sf137;
+        static Object sf138;
+        static Object sf139;
+        static Object sf140;
+        static Object sf141;
+        static Object sf142;
+        static Object sf143;
+        static Object sf144;
+        static Object sf145;
+        static Object sf146;
+        static Object sf147;
+        static Object sf148;
+        static Object sf149;
+        static Object sf150;
+        static Object sf151;
+        static Object sf152;
+        static Object sf153;
+        static Object sf154;
+        static Object sf155;
+        static Object sf156;
+        static Object sf157;
+        static Object sf158;
+        static Object sf159;
+        static Object sf160;
+        static Object sf161;
+        static Object sf162;
+        static Object sf163;
+        static Object sf164;
+        static Object sf165;
+        static Object sf166;
+        static Object sf167;
+        static Object sf168;
+        static Object sf169;
+        static Object sf170;
+        static Object sf171;
+        static Object sf172;
+        static Object sf173;
+        static Object sf174;
+        static Object sf175;
+        static Object sf176;
+        static Object sf177;
+        static Object sf178;
+        static Object sf179;
+        static Object sf180;
+        static Object sf181;
+        static Object sf182;
+        static Object sf183;
+        static Object sf184;
+        static Object sf185;
+        static Object sf186;
+        static Object sf187;
+        static Object sf188;
+        static Object sf189;
+        static Object sf190;
+        static Object sf191;
+        static Object sf192;
+        static Object sf193;
+        static Object sf194;
+        static Object sf195;
+        static Object sf196;
+        static Object sf197;
+        static Object sf198;
+        static Object sf199;
+        static Object sf200;
+        static Object sf201;
+        static Object sf202;
+        static Object sf203;
+        static Object sf204;
+        static Object sf205;
+        static Object sf206;
+        static Object sf207;
+        static Object sf208;
+        static Object sf209;
+        static Object sf210;
+        static Object sf211;
+        static Object sf212;
+        static Object sf213;
+        static Object sf214;
+        static Object sf215;
+        static Object sf216;
+        static Object sf217;
+        static Object sf218;
+        static Object sf219;
+        static Object sf220;
+        static Object sf221;
+        static Object sf222;
+        static Object sf223;
+        static Object sf224;
+        static Object sf225;
+        static Object sf226;
+        static Object sf227;
+        static Object sf228;
+        static Object sf229;
+        static Object sf230;
+        static Object sf231;
+        static Object sf232;
+        static Object sf233;
+        static Object sf234;
+        static Object sf235;
+        static Object sf236;
+        static Object sf237;
+        static Object sf238;
+        static Object sf239;
+        static Object sf240;
+        static Object sf241;
+        static Object sf242;
+        static Object sf243;
+        static Object sf244;
+        static Object sf245;
+        static Object sf246;
+        static Object sf247;
+        static Object sf248;
+        static Object sf249;
+        static Object sf250;
+        static Object sf251;
+        static Object sf252;
+        static Object sf253;
+        static Object sf254;
+        static Object sf255;
+        static Object sf256;
+        static Object sf257;
+        static Object sf258;
+        static Object sf259;
+        static Object sf260;
+        static Object sf261;
+        static Object sf262;
+        static Object sf263;
+        static Object sf264;
+        static Object sf265;
+        static Object sf266;
+        static Object sf267;
+        static Object sf268;
+        static Object sf269;
+        static Object sf270;
+        static Object sf271;
+        static Object sf272;
+        static Object sf273;
+        static Object sf274;
+        static Object sf275;
+        static Object sf276;
+        static Object sf277;
+        static Object sf278;
+        static Object sf279;
+        static Object sf280;
+        static Object sf281;
+        static Object sf282;
+        static Object sf283;
+        static Object sf284;
+        static Object sf285;
+        static Object sf286;
+        static Object sf287;
+        static Object sf288;
+        static Object sf289;
+        static Object sf290;
+        static Object sf291;
+        static Object sf292;
+        static Object sf293;
+        static Object sf294;
+        static Object sf295;
+        static Object sf296;
+        static Object sf297;
+        static Object sf298;
+        static Object sf299;
+        static Object sf300;
+        static Object sf301;
+        static Object sf302;
+        static Object sf303;
+        static Object sf304;
+        static Object sf305;
+        static Object sf306;
+        static Object sf307;
+        static Object sf308;
+        static Object sf309;
+        static Object sf310;
+        static Object sf311;
+        static Object sf312;
+        static Object sf313;
+        static Object sf314;
+        static Object sf315;
+        static Object sf316;
+        static Object sf317;
+        static Object sf318;
+        static Object sf319;
+        static Object sf320;
+        static Object sf321;
+        static Object sf322;
+        static Object sf323;
+        static Object sf324;
+        static Object sf325;
+        static Object sf326;
+        static Object sf327;
+        static Object sf328;
+        static Object sf329;
+        static Object sf330;
+        static Object sf331;
+        static Object sf332;
+        static Object sf333;
+        static Object sf334;
+        static Object sf335;
+        static Object sf336;
+        static Object sf337;
+        static Object sf338;
+        static Object sf339;
+        static Object sf340;
+        static Object sf341;
+        static Object sf342;
+        static Object sf343;
+        static Object sf344;
+        static Object sf345;
+        static Object sf346;
+        static Object sf347;
+        static Object sf348;
+        static Object sf349;
+        static Object sf350;
+        static Object sf351;
+        static Object sf352;
+        static Object sf353;
+        static Object sf354;
+        static Object sf355;
+        static Object sf356;
+        static Object sf357;
+        static Object sf358;
+        static Object sf359;
+        static Object sf360;
+        static Object sf361;
+        static Object sf362;
+        static Object sf363;
+        static Object sf364;
+        static Object sf365;
+        static Object sf366;
+        static Object sf367;
+        static Object sf368;
+        static Object sf369;
+        static Object sf370;
+        static Object sf371;
+        static Object sf372;
+        static Object sf373;
+        static Object sf374;
+        static Object sf375;
+        static Object sf376;
+        static Object sf377;
+        static Object sf378;
+        static Object sf379;
+        static Object sf380;
+        static Object sf381;
+        static Object sf382;
+        static Object sf383;
+        static Object sf384;
+        static Object sf385;
+        static Object sf386;
+        static Object sf387;
+        static Object sf388;
+        static Object sf389;
+        static Object sf390;
+        static Object sf391;
+        static Object sf392;
+        static Object sf393;
+        static Object sf394;
+        static Object sf395;
+        static Object sf396;
+        static Object sf397;
+        static Object sf398;
+        static Object sf399;
+        static Object sf400;
+        static Object sf401;
+        static Object sf402;
+        static Object sf403;
+        static Object sf404;
+        static Object sf405;
+        static Object sf406;
+        static Object sf407;
+        static Object sf408;
+        static Object sf409;
+        static Object sf410;
+        static Object sf411;
+        static Object sf412;
+        static Object sf413;
+        static Object sf414;
+        static Object sf415;
+        static Object sf416;
+        static Object sf417;
+        static Object sf418;
+        static Object sf419;
+        static Object sf420;
+        static Object sf421;
+        static Object sf422;
+        static Object sf423;
+        static Object sf424;
+        static Object sf425;
+        static Object sf426;
+        static Object sf427;
+        static Object sf428;
+        static Object sf429;
+        static Object sf430;
+        static Object sf431;
+        static Object sf432;
+        static Object sf433;
+        static Object sf434;
+        static Object sf435;
+        static Object sf436;
+        static Object sf437;
+        static Object sf438;
+        static Object sf439;
+        static Object sf440;
+        static Object sf441;
+        static Object sf442;
+        static Object sf443;
+        static Object sf444;
+        static Object sf445;
+        static Object sf446;
+        static Object sf447;
+        static Object sf448;
+        static Object sf449;
+        static Object sf450;
+        static Object sf451;
+        static Object sf452;
+        static Object sf453;
+        static Object sf454;
+        static Object sf455;
+        static Object sf456;
+        static Object sf457;
+        static Object sf458;
+        static Object sf459;
+        static Object sf460;
+        static Object sf461;
+        static Object sf462;
+        static Object sf463;
+        static Object sf464;
+        static Object sf465;
+        static Object sf466;
+        static Object sf467;
+        static Object sf468;
+        static Object sf469;
+        static Object sf470;
+        static Object sf471;
+        static Object sf472;
+        static Object sf473;
+        static Object sf474;
+        static Object sf475;
+        static Object sf476;
+        static Object sf477;
+        static Object sf478;
+        static Object sf479;
+        static Object sf480;
+        static Object sf481;
+        static Object sf482;
+        static Object sf483;
+        static Object sf484;
+        static Object sf485;
+        static Object sf486;
+        static Object sf487;
+        static Object sf488;
+        static Object sf489;
+        static Object sf490;
+        static Object sf491;
+        static Object sf492;
+        static Object sf493;
+        static Object sf494;
+        static Object sf495;
+        static Object sf496;
+        static Object sf497;
+        static Object sf498;
+        static Object sf499;
+        static Object sf500;
+        static Object sf501;
+        static Object sf502;
+        static Object sf503;
+        static Object sf504;
+        static Object sf505;
+        static Object sf506;
+        static Object sf507;
+        static Object sf508;
+        static Object sf509;
+        static Object sf510;
+        static Object sf511;
+        static Object sf512;
+        static Object sf513;
+        static Object sf514;
+        static Object sf515;
+        static Object sf516;
+        static Object sf517;
+        static Object sf518;
+        static Object sf519;
+        static Object sf520;
+        static Object sf521;
+        static Object sf522;
+        static Object sf523;
+        static Object sf524;
+        static Object sf525;
+        static Object sf526;
+        static Object sf527;
+        static Object sf528;
+        static Object sf529;
+        static Object sf530;
+        static Object sf531;
+        static Object sf532;
+        static Object sf533;
+        static Object sf534;
+        static Object sf535;
+        static Object sf536;
+        static Object sf537;
+        static Object sf538;
+        static Object sf539;
+        static Object sf540;
+        static Object sf541;
+        static Object sf542;
+        static Object sf543;
+        static Object sf544;
+        static Object sf545;
+        static Object sf546;
+        static Object sf547;
+        static Object sf548;
+        static Object sf549;
+        static Object sf550;
+        static Object sf551;
+        static Object sf552;
+        static Object sf553;
+        static Object sf554;
+        static Object sf555;
+        static Object sf556;
+        static Object sf557;
+        static Object sf558;
+        static Object sf559;
+        static Object sf560;
+        static Object sf561;
+        static Object sf562;
+        static Object sf563;
+        static Object sf564;
+        static Object sf565;
+        static Object sf566;
+        static Object sf567;
+        static Object sf568;
+        static Object sf569;
+        static Object sf570;
+        static Object sf571;
+        static Object sf572;
+        static Object sf573;
+        static Object sf574;
+        static Object sf575;
+        static Object sf576;
+        static Object sf577;
+        static Object sf578;
+        static Object sf579;
+        static Object sf580;
+        static Object sf581;
+        static Object sf582;
+        static Object sf583;
+        static Object sf584;
+        static Object sf585;
+        static Object sf586;
+        static Object sf587;
+        static Object sf588;
+        static Object sf589;
+        static Object sf590;
+        static Object sf591;
+        static Object sf592;
+        static Object sf593;
+        static Object sf594;
+        static Object sf595;
+        static Object sf596;
+        static Object sf597;
+        static Object sf598;
+        static Object sf599;
+        static Object sf600;
+        static Object sf601;
+        static Object sf602;
+        static Object sf603;
+        static Object sf604;
+        static Object sf605;
+        static Object sf606;
+        static Object sf607;
+        static Object sf608;
+        static Object sf609;
+        static Object sf610;
+        static Object sf611;
+        static Object sf612;
+        static Object sf613;
+        static Object sf614;
+        static Object sf615;
+        static Object sf616;
+        static Object sf617;
+        static Object sf618;
+        static Object sf619;
+        static Object sf620;
+        static Object sf621;
+        static Object sf622;
+        static Object sf623;
+        static Object sf624;
+        static Object sf625;
+        static Object sf626;
+        static Object sf627;
+        static Object sf628;
+        static Object sf629;
+        static Object sf630;
+        static Object sf631;
+        static Object sf632;
+        static Object sf633;
+        static Object sf634;
+        static Object sf635;
+        static Object sf636;
+        static Object sf637;
+        static Object sf638;
+        static Object sf639;
+        static Object sf640;
+        static Object sf641;
+        static Object sf642;
+        static Object sf643;
+        static Object sf644;
+        static Object sf645;
+        static Object sf646;
+        static Object sf647;
+        static Object sf648;
+        static Object sf649;
+        static Object sf650;
+        static Object sf651;
+        static Object sf652;
+        static Object sf653;
+        static Object sf654;
+        static Object sf655;
+        static Object sf656;
+        static Object sf657;
+        static Object sf658;
+        static Object sf659;
+        static Object sf660;
+        static Object sf661;
+        static Object sf662;
+        static Object sf663;
+        static Object sf664;
+        static Object sf665;
+        static Object sf666;
+        static Object sf667;
+        static Object sf668;
+        static Object sf669;
+        static Object sf670;
+        static Object sf671;
+        static Object sf672;
+        static Object sf673;
+        static Object sf674;
+        static Object sf675;
+        static Object sf676;
+        static Object sf677;
+        static Object sf678;
+        static Object sf679;
+        static Object sf680;
+        static Object sf681;
+        static Object sf682;
+        static Object sf683;
+        static Object sf684;
+        static Object sf685;
+        static Object sf686;
+        static Object sf687;
+        static Object sf688;
+        static Object sf689;
+        static Object sf690;
+        static Object sf691;
+        static Object sf692;
+        static Object sf693;
+        static Object sf694;
+        static Object sf695;
+        static Object sf696;
+        static Object sf697;
+        static Object sf698;
+        static Object sf699;
+        static Object sf700;
+        static Object sf701;
+        static Object sf702;
+        static Object sf703;
+        static Object sf704;
+        static Object sf705;
+        static Object sf706;
+        static Object sf707;
+        static Object sf708;
+        static Object sf709;
+        static Object sf710;
+        static Object sf711;
+        static Object sf712;
+        static Object sf713;
+        static Object sf714;
+        static Object sf715;
+        static Object sf716;
+        static Object sf717;
+        static Object sf718;
+        static Object sf719;
+        static Object sf720;
+        static Object sf721;
+        static Object sf722;
+        static Object sf723;
+        static Object sf724;
+        static Object sf725;
+        static Object sf726;
+        static Object sf727;
+        static Object sf728;
+        static Object sf729;
+        static Object sf730;
+        static Object sf731;
+        static Object sf732;
+        static Object sf733;
+        static Object sf734;
+        static Object sf735;
+        static Object sf736;
+        static Object sf737;
+        static Object sf738;
+        static Object sf739;
+        static Object sf740;
+        static Object sf741;
+        static Object sf742;
+        static Object sf743;
+        static Object sf744;
+        static Object sf745;
+        static Object sf746;
+        static Object sf747;
+        static Object sf748;
+        static Object sf749;
+        static Object sf750;
+        static Object sf751;
+        static Object sf752;
+        static Object sf753;
+        static Object sf754;
+        static Object sf755;
+        static Object sf756;
+        static Object sf757;
+        static Object sf758;
+        static Object sf759;
+        static Object sf760;
+        static Object sf761;
+        static Object sf762;
+        static Object sf763;
+        static Object sf764;
+        static Object sf765;
+        static Object sf766;
+        static Object sf767;
+        static Object sf768;
+        static Object sf769;
+        static Object sf770;
+        static Object sf771;
+        static Object sf772;
+        static Object sf773;
+        static Object sf774;
+        static Object sf775;
+        static Object sf776;
+        static Object sf777;
+        static Object sf778;
+        static Object sf779;
+        static Object sf780;
+        static Object sf781;
+        static Object sf782;
+        static Object sf783;
+        static Object sf784;
+        static Object sf785;
+        static Object sf786;
+        static Object sf787;
+        static Object sf788;
+        static Object sf789;
+        static Object sf790;
+        static Object sf791;
+        static Object sf792;
+        static Object sf793;
+        static Object sf794;
+        static Object sf795;
+        static Object sf796;
+        static Object sf797;
+        static Object sf798;
+        static Object sf799;
+        static Object sf800;
+        static Object sf801;
+        static Object sf802;
+        static Object sf803;
+        static Object sf804;
+        static Object sf805;
+        static Object sf806;
+        static Object sf807;
+        static Object sf808;
+        static Object sf809;
+        static Object sf810;
+        static Object sf811;
+        static Object sf812;
+        static Object sf813;
+        static Object sf814;
+        static Object sf815;
+        static Object sf816;
+        static Object sf817;
+        static Object sf818;
+        static Object sf819;
+        static Object sf820;
+        static Object sf821;
+        static Object sf822;
+        static Object sf823;
+        static Object sf824;
+        static Object sf825;
+        static Object sf826;
+        static Object sf827;
+        static Object sf828;
+        static Object sf829;
+        static Object sf830;
+        static Object sf831;
+        static Object sf832;
+        static Object sf833;
+        static Object sf834;
+        static Object sf835;
+        static Object sf836;
+        static Object sf837;
+        static Object sf838;
+        static Object sf839;
+        static Object sf840;
+        static Object sf841;
+        static Object sf842;
+        static Object sf843;
+        static Object sf844;
+        static Object sf845;
+        static Object sf846;
+        static Object sf847;
+        static Object sf848;
+        static Object sf849;
+        static Object sf850;
+        static Object sf851;
+        static Object sf852;
+        static Object sf853;
+        static Object sf854;
+        static Object sf855;
+        static Object sf856;
+        static Object sf857;
+        static Object sf858;
+        static Object sf859;
+        static Object sf860;
+        static Object sf861;
+        static Object sf862;
+        static Object sf863;
+        static Object sf864;
+        static Object sf865;
+        static Object sf866;
+        static Object sf867;
+        static Object sf868;
+        static Object sf869;
+        static Object sf870;
+        static Object sf871;
+        static Object sf872;
+        static Object sf873;
+        static Object sf874;
+        static Object sf875;
+        static Object sf876;
+        static Object sf877;
+        static Object sf878;
+        static Object sf879;
+        static Object sf880;
+        static Object sf881;
+        static Object sf882;
+        static Object sf883;
+        static Object sf884;
+        static Object sf885;
+        static Object sf886;
+        static Object sf887;
+        static Object sf888;
+        static Object sf889;
+        static Object sf890;
+        static Object sf891;
+        static Object sf892;
+        static Object sf893;
+        static Object sf894;
+        static Object sf895;
+        static Object sf896;
+        static Object sf897;
+        static Object sf898;
+        static Object sf899;
+        static Object sf900;
+        static Object sf901;
+        static Object sf902;
+        static Object sf903;
+        static Object sf904;
+        static Object sf905;
+        static Object sf906;
+        static Object sf907;
+        static Object sf908;
+        static Object sf909;
+        static Object sf910;
+        static Object sf911;
+        static Object sf912;
+        static Object sf913;
+        static Object sf914;
+        static Object sf915;
+        static Object sf916;
+        static Object sf917;
+        static Object sf918;
+        static Object sf919;
+        static Object sf920;
+        static Object sf921;
+        static Object sf922;
+        static Object sf923;
+        static Object sf924;
+        static Object sf925;
+        static Object sf926;
+        static Object sf927;
+        static Object sf928;
+        static Object sf929;
+        static Object sf930;
+        static Object sf931;
+        static Object sf932;
+        static Object sf933;
+        static Object sf934;
+        static Object sf935;
+        static Object sf936;
+        static Object sf937;
+        static Object sf938;
+        static Object sf939;
+        static Object sf940;
+        static Object sf941;
+        static Object sf942;
+        static Object sf943;
+        static Object sf944;
+        static Object sf945;
+        static Object sf946;
+        static Object sf947;
+        static Object sf948;
+        static Object sf949;
+        static Object sf950;
+        static Object sf951;
+        static Object sf952;
+        static Object sf953;
+        static Object sf954;
+        static Object sf955;
+        static Object sf956;
+        static Object sf957;
+        static Object sf958;
+        static Object sf959;
+        static Object sf960;
+        static Object sf961;
+        static Object sf962;
+        static Object sf963;
+        static Object sf964;
+        static Object sf965;
+        static Object sf966;
+        static Object sf967;
+        static Object sf968;
+        static Object sf969;
+        static Object sf970;
+        static Object sf971;
+        static Object sf972;
+        static Object sf973;
+        static Object sf974;
+        static Object sf975;
+        static Object sf976;
+        static Object sf977;
+        static Object sf978;
+        static Object sf979;
+        static Object sf980;
+        static Object sf981;
+        static Object sf982;
+        static Object sf983;
+        static Object sf984;
+        static Object sf985;
+        static Object sf986;
+        static Object sf987;
+        static Object sf988;
+        static Object sf989;
+        static Object sf990;
+        static Object sf991;
+        static Object sf992;
+        static Object sf993;
+        static Object sf994;
+        static Object sf995;
+        static Object sf996;
+        static Object sf997;
+        static Object sf998;
+        static Object sf999;
+        static Object sf1000;
+        static Object sf1001;
+        static Object sf1002;
+        static Object sf1003;
+        static Object sf1004;
+        static Object sf1005;
+        static Object sf1006;
+        static Object sf1007;
+        static Object sf1008;
+        static Object sf1009;
+        static Object sf1010;
+        static Object sf1011;
+        static Object sf1012;
+        static Object sf1013;
+        static Object sf1014;
+        static Object sf1015;
+        static Object sf1016;
+        static Object sf1017;
+        static Object sf1018;
+        static Object sf1019;
+        static Object sf1020;
+        static Object sf1021;
+        static Object sf1022;
+        static Object sf1023;
+        static Object sf1024;
+        static Object sf1025;
+        static Object sf1026;
+        static Object sf1027;
+        static Object sf1028;
+        static Object sf1029;
+        static Object sf1030;
+        static Object sf1031;
+        static Object sf1032;
+        static Object sf1033;
+        static Object sf1034;
+        static Object sf1035;
+        static Object sf1036;
+        static Object sf1037;
+        static Object sf1038;
+        static Object sf1039;
+        static Object sf1040;
+        static Object sf1041;
+        static Object sf1042;
+        static Object sf1043;
+        static Object sf1044;
+        static Object sf1045;
+        static Object sf1046;
+        static Object sf1047;
+        static Object sf1048;
+        static Object sf1049;
+        static Object sf1050;
+        static Object sf1051;
+        static Object sf1052;
+        static Object sf1053;
+        static Object sf1054;
+        static Object sf1055;
+        static Object sf1056;
+        static Object sf1057;
+        static Object sf1058;
+        static Object sf1059;
+        static Object sf1060;
+        static Object sf1061;
+        static Object sf1062;
+        static Object sf1063;
+        static Object sf1064;
+        static Object sf1065;
+        static Object sf1066;
+        static Object sf1067;
+        static Object sf1068;
+        static Object sf1069;
+        static Object sf1070;
+        static Object sf1071;
+        static Object sf1072;
+        static Object sf1073;
+        static Object sf1074;
+        static Object sf1075;
+        static Object sf1076;
+        static Object sf1077;
+        static Object sf1078;
+        static Object sf1079;
+        static Object sf1080;
+        static Object sf1081;
+        static Object sf1082;
+        static Object sf1083;
+        static Object sf1084;
+        static Object sf1085;
+        static Object sf1086;
+        static Object sf1087;
+        static Object sf1088;
+        static Object sf1089;
+        static Object sf1090;
+        static Object sf1091;
+        static Object sf1092;
+        static Object sf1093;
+        static Object sf1094;
+        static Object sf1095;
+        static Object sf1096;
+        static Object sf1097;
+        static Object sf1098;
+        static Object sf1099;
+        static Object sf1100;
+        static Object sf1101;
+        static Object sf1102;
+        static Object sf1103;
+        static Object sf1104;
+        static Object sf1105;
+        static Object sf1106;
+        static Object sf1107;
+        static Object sf1108;
+        static Object sf1109;
+        static Object sf1110;
+        static Object sf1111;
+        static Object sf1112;
+        static Object sf1113;
+        static Object sf1114;
+        static Object sf1115;
+        static Object sf1116;
+        static Object sf1117;
+        static Object sf1118;
+        static Object sf1119;
+        static Object sf1120;
+        static Object sf1121;
+        static Object sf1122;
+        static Object sf1123;
+        static Object sf1124;
+        static Object sf1125;
+        static Object sf1126;
+        static Object sf1127;
+        static Object sf1128;
+        static Object sf1129;
+        static Object sf1130;
+        static Object sf1131;
+        static Object sf1132;
+        static Object sf1133;
+        static Object sf1134;
+        static Object sf1135;
+        static Object sf1136;
+        static Object sf1137;
+        static Object sf1138;
+        static Object sf1139;
+        static Object sf1140;
+        static Object sf1141;
+        static Object sf1142;
+        static Object sf1143;
+        static Object sf1144;
+        static Object sf1145;
+        static Object sf1146;
+        static Object sf1147;
+        static Object sf1148;
+        static Object sf1149;
+        static Object sf1150;
+        static Object sf1151;
+        static Object sf1152;
+        static Object sf1153;
+        static Object sf1154;
+        static Object sf1155;
+        static Object sf1156;
+        static Object sf1157;
+        static Object sf1158;
+        static Object sf1159;
+        static Object sf1160;
+        static Object sf1161;
+        static Object sf1162;
+        static Object sf1163;
+        static Object sf1164;
+        static Object sf1165;
+        static Object sf1166;
+        static Object sf1167;
+        static Object sf1168;
+        static Object sf1169;
+        static Object sf1170;
+        static Object sf1171;
+        static Object sf1172;
+        static Object sf1173;
+        static Object sf1174;
+        static Object sf1175;
+        static Object sf1176;
+        static Object sf1177;
+        static Object sf1178;
+        static Object sf1179;
+        static Object sf1180;
+        static Object sf1181;
+        static Object sf1182;
+        static Object sf1183;
+        static Object sf1184;
+        static Object sf1185;
+        static Object sf1186;
+        static Object sf1187;
+        static Object sf1188;
+        static Object sf1189;
+        static Object sf1190;
+        static Object sf1191;
+        static Object sf1192;
+        static Object sf1193;
+        static Object sf1194;
+        static Object sf1195;
+        static Object sf1196;
+        static Object sf1197;
+        static Object sf1198;
+        static Object sf1199;
+        static Object sf1200;
+        static Object sf1201;
+        static Object sf1202;
+        static Object sf1203;
+        static Object sf1204;
+        static Object sf1205;
+        static Object sf1206;
+        static Object sf1207;
+        static Object sf1208;
+        static Object sf1209;
+        static Object sf1210;
+        static Object sf1211;
+        static Object sf1212;
+        static Object sf1213;
+        static Object sf1214;
+        static Object sf1215;
+        static Object sf1216;
+        static Object sf1217;
+        static Object sf1218;
+        static Object sf1219;
+        static Object sf1220;
+        static Object sf1221;
+        static Object sf1222;
+        static Object sf1223;
+        static Object sf1224;
+        static Object sf1225;
+        static Object sf1226;
+        static Object sf1227;
+        static Object sf1228;
+        static Object sf1229;
+        static Object sf1230;
+        static Object sf1231;
+        static Object sf1232;
+        static Object sf1233;
+        static Object sf1234;
+        static Object sf1235;
+        static Object sf1236;
+        static Object sf1237;
+        static Object sf1238;
+        static Object sf1239;
+        static Object sf1240;
+        static Object sf1241;
+        static Object sf1242;
+        static Object sf1243;
+        static Object sf1244;
+        static Object sf1245;
+        static Object sf1246;
+        static Object sf1247;
+        static Object sf1248;
+        static Object sf1249;
+        static Object sf1250;
+        static Object sf1251;
+        static Object sf1252;
+        static Object sf1253;
+        static Object sf1254;
+        static Object sf1255;
+        static Object sf1256;
+        static Object sf1257;
+        static Object sf1258;
+        static Object sf1259;
+        static Object sf1260;
+        static Object sf1261;
+        static Object sf1262;
+        static Object sf1263;
+        static Object sf1264;
+        static Object sf1265;
+        static Object sf1266;
+        static Object sf1267;
+        static Object sf1268;
+        static Object sf1269;
+        static Object sf1270;
+        static Object sf1271;
+        static Object sf1272;
+        static Object sf1273;
+        static Object sf1274;
+        static Object sf1275;
+        static Object sf1276;
+        static Object sf1277;
+        static Object sf1278;
+        static Object sf1279;
+        static Object sf1280;
+        static Object sf1281;
+        static Object sf1282;
+        static Object sf1283;
+        static Object sf1284;
+        static Object sf1285;
+        static Object sf1286;
+        static Object sf1287;
+        static Object sf1288;
+        static Object sf1289;
+        static Object sf1290;
+        static Object sf1291;
+        static Object sf1292;
+        static Object sf1293;
+        static Object sf1294;
+        static Object sf1295;
+        static Object sf1296;
+        static Object sf1297;
+        static Object sf1298;
+        static Object sf1299;
+        static Object sf1300;
+        static Object sf1301;
+        static Object sf1302;
+        static Object sf1303;
+        static Object sf1304;
+        static Object sf1305;
+        static Object sf1306;
+        static Object sf1307;
+        static Object sf1308;
+        static Object sf1309;
+        static Object sf1310;
+        static Object sf1311;
+        static Object sf1312;
+        static Object sf1313;
+        static Object sf1314;
+        static Object sf1315;
+        static Object sf1316;
+        static Object sf1317;
+        static Object sf1318;
+        static Object sf1319;
+        static Object sf1320;
+        static Object sf1321;
+        static Object sf1322;
+        static Object sf1323;
+        static Object sf1324;
+        static Object sf1325;
+        static Object sf1326;
+        static Object sf1327;
+        static Object sf1328;
+        static Object sf1329;
+        static Object sf1330;
+        static Object sf1331;
+        static Object sf1332;
+        static Object sf1333;
+        static Object sf1334;
+        static Object sf1335;
+        static Object sf1336;
+        static Object sf1337;
+        static Object sf1338;
+        static Object sf1339;
+        static Object sf1340;
+        static Object sf1341;
+        static Object sf1342;
+        static Object sf1343;
+        static Object sf1344;
+        static Object sf1345;
+        static Object sf1346;
+        static Object sf1347;
+        static Object sf1348;
+        static Object sf1349;
+        static Object sf1350;
+        static Object sf1351;
+        static Object sf1352;
+        static Object sf1353;
+        static Object sf1354;
+        static Object sf1355;
+        static Object sf1356;
+        static Object sf1357;
+        static Object sf1358;
+        static Object sf1359;
+        static Object sf1360;
+        static Object sf1361;
+        static Object sf1362;
+        static Object sf1363;
+        static Object sf1364;
+        static Object sf1365;
+        static Object sf1366;
+        static Object sf1367;
+        static Object sf1368;
+        static Object sf1369;
+        static Object sf1370;
+        static Object sf1371;
+        static Object sf1372;
+        static Object sf1373;
+        static Object sf1374;
+        static Object sf1375;
+        static Object sf1376;
+        static Object sf1377;
+        static Object sf1378;
+        static Object sf1379;
+        static Object sf1380;
+        static Object sf1381;
+        static Object sf1382;
+        static Object sf1383;
+        static Object sf1384;
+        static Object sf1385;
+        static Object sf1386;
+        static Object sf1387;
+        static Object sf1388;
+        static Object sf1389;
+        static Object sf1390;
+        static Object sf1391;
+        static Object sf1392;
+        static Object sf1393;
+        static Object sf1394;
+        static Object sf1395;
+        static Object sf1396;
+        static Object sf1397;
+        static Object sf1398;
+        static Object sf1399;
+        static Object sf1400;
+        static Object sf1401;
+        static Object sf1402;
+        static Object sf1403;
+        static Object sf1404;
+        static Object sf1405;
+        static Object sf1406;
+        static Object sf1407;
+        static Object sf1408;
+        static Object sf1409;
+        static Object sf1410;
+        static Object sf1411;
+        static Object sf1412;
+        static Object sf1413;
+        static Object sf1414;
+        static Object sf1415;
+        static Object sf1416;
+        static Object sf1417;
+        static Object sf1418;
+        static Object sf1419;
+        static Object sf1420;
+        static Object sf1421;
+        static Object sf1422;
+        static Object sf1423;
+        static Object sf1424;
+        static Object sf1425;
+        static Object sf1426;
+        static Object sf1427;
+        static Object sf1428;
+        static Object sf1429;
+        static Object sf1430;
+        static Object sf1431;
+        static Object sf1432;
+        static Object sf1433;
+        static Object sf1434;
+        static Object sf1435;
+        static Object sf1436;
+        static Object sf1437;
+        static Object sf1438;
+        static Object sf1439;
+        static Object sf1440;
+        static Object sf1441;
+        static Object sf1442;
+        static Object sf1443;
+        static Object sf1444;
+        static Object sf1445;
+        static Object sf1446;
+        static Object sf1447;
+        static Object sf1448;
+        static Object sf1449;
+        static Object sf1450;
+        static Object sf1451;
+        static Object sf1452;
+        static Object sf1453;
+        static Object sf1454;
+        static Object sf1455;
+        static Object sf1456;
+        static Object sf1457;
+        static Object sf1458;
+        static Object sf1459;
+        static Object sf1460;
+        static Object sf1461;
+        static Object sf1462;
+        static Object sf1463;
+        static Object sf1464;
+        static Object sf1465;
+        static Object sf1466;
+        static Object sf1467;
+        static Object sf1468;
+        static Object sf1469;
+        static Object sf1470;
+        static Object sf1471;
+        static Object sf1472;
+        static Object sf1473;
+        static Object sf1474;
+        static Object sf1475;
+        static Object sf1476;
+        static Object sf1477;
+        static Object sf1478;
+        static Object sf1479;
+        static Object sf1480;
+        static Object sf1481;
+        static Object sf1482;
+        static Object sf1483;
+        static Object sf1484;
+        static Object sf1485;
+        static Object sf1486;
+        static Object sf1487;
+        static Object sf1488;
+        static Object sf1489;
+        static Object sf1490;
+        static Object sf1491;
+        static Object sf1492;
+        static Object sf1493;
+        static Object sf1494;
+        static Object sf1495;
+        static Object sf1496;
+        static Object sf1497;
+        static Object sf1498;
+        static Object sf1499;
+        static Object sf1500;
+        static Object sf1501;
+        static Object sf1502;
+        static Object sf1503;
+        static Object sf1504;
+        static Object sf1505;
+        static Object sf1506;
+        static Object sf1507;
+        static Object sf1508;
+        static Object sf1509;
+        static Object sf1510;
+        static Object sf1511;
+        static Object sf1512;
+        static Object sf1513;
+        static Object sf1514;
+        static Object sf1515;
+        static Object sf1516;
+        static Object sf1517;
+        static Object sf1518;
+        static Object sf1519;
+        static Object sf1520;
+        static Object sf1521;
+        static Object sf1522;
+        static Object sf1523;
+        static Object sf1524;
+        static Object sf1525;
+        static Object sf1526;
+        static Object sf1527;
+        static Object sf1528;
+        static Object sf1529;
+        static Object sf1530;
+        static Object sf1531;
+        static Object sf1532;
+        static Object sf1533;
+        static Object sf1534;
+        static Object sf1535;
+        static Object sf1536;
+        static Object sf1537;
+        static Object sf1538;
+        static Object sf1539;
+        static Object sf1540;
+        static Object sf1541;
+        static Object sf1542;
+        static Object sf1543;
+        static Object sf1544;
+        static Object sf1545;
+        static Object sf1546;
+        static Object sf1547;
+        static Object sf1548;
+        static Object sf1549;
+        static Object sf1550;
+        static Object sf1551;
+        static Object sf1552;
+        static Object sf1553;
+        static Object sf1554;
+        static Object sf1555;
+        static Object sf1556;
+        static Object sf1557;
+        static Object sf1558;
+        static Object sf1559;
+        static Object sf1560;
+        static Object sf1561;
+        static Object sf1562;
+        static Object sf1563;
+        static Object sf1564;
+        static Object sf1565;
+        static Object sf1566;
+        static Object sf1567;
+        static Object sf1568;
+        static Object sf1569;
+        static Object sf1570;
+        static Object sf1571;
+        static Object sf1572;
+        static Object sf1573;
+        static Object sf1574;
+        static Object sf1575;
+        static Object sf1576;
+        static Object sf1577;
+        static Object sf1578;
+        static Object sf1579;
+        static Object sf1580;
+        static Object sf1581;
+        static Object sf1582;
+        static Object sf1583;
+        static Object sf1584;
+        static Object sf1585;
+        static Object sf1586;
+        static Object sf1587;
+        static Object sf1588;
+        static Object sf1589;
+        static Object sf1590;
+        static Object sf1591;
+        static Object sf1592;
+        static Object sf1593;
+        static Object sf1594;
+        static Object sf1595;
+        static Object sf1596;
+        static Object sf1597;
+        static Object sf1598;
+        static Object sf1599;
+        static Object sf1600;
+        static Object sf1601;
+        static Object sf1602;
+        static Object sf1603;
+        static Object sf1604;
+        static Object sf1605;
+        static Object sf1606;
+        static Object sf1607;
+        static Object sf1608;
+        static Object sf1609;
+        static Object sf1610;
+        static Object sf1611;
+        static Object sf1612;
+        static Object sf1613;
+        static Object sf1614;
+        static Object sf1615;
+        static Object sf1616;
+        static Object sf1617;
+        static Object sf1618;
+        static Object sf1619;
+        static Object sf1620;
+        static Object sf1621;
+        static Object sf1622;
+        static Object sf1623;
+        static Object sf1624;
+        static Object sf1625;
+        static Object sf1626;
+        static Object sf1627;
+        static Object sf1628;
+        static Object sf1629;
+        static Object sf1630;
+        static Object sf1631;
+        static Object sf1632;
+        static Object sf1633;
+        static Object sf1634;
+        static Object sf1635;
+        static Object sf1636;
+        static Object sf1637;
+        static Object sf1638;
+        static Object sf1639;
+        static Object sf1640;
+        static Object sf1641;
+        static Object sf1642;
+        static Object sf1643;
+        static Object sf1644;
+        static Object sf1645;
+        static Object sf1646;
+        static Object sf1647;
+        static Object sf1648;
+        static Object sf1649;
+        static Object sf1650;
+        static Object sf1651;
+        static Object sf1652;
+        static Object sf1653;
+        static Object sf1654;
+        static Object sf1655;
+        static Object sf1656;
+        static Object sf1657;
+        static Object sf1658;
+        static Object sf1659;
+        static Object sf1660;
+        static Object sf1661;
+        static Object sf1662;
+        static Object sf1663;
+        static Object sf1664;
+        static Object sf1665;
+        static Object sf1666;
+        static Object sf1667;
+        static Object sf1668;
+        static Object sf1669;
+        static Object sf1670;
+        static Object sf1671;
+        static Object sf1672;
+        static Object sf1673;
+        static Object sf1674;
+        static Object sf1675;
+        static Object sf1676;
+        static Object sf1677;
+        static Object sf1678;
+        static Object sf1679;
+        static Object sf1680;
+        static Object sf1681;
+        static Object sf1682;
+        static Object sf1683;
+        static Object sf1684;
+        static Object sf1685;
+        static Object sf1686;
+        static Object sf1687;
+        static Object sf1688;
+        static Object sf1689;
+        static Object sf1690;
+        static Object sf1691;
+        static Object sf1692;
+        static Object sf1693;
+        static Object sf1694;
+        static Object sf1695;
+        static Object sf1696;
+        static Object sf1697;
+        static Object sf1698;
+        static Object sf1699;
+        static Object sf1700;
+        static Object sf1701;
+        static Object sf1702;
+        static Object sf1703;
+        static Object sf1704;
+        static Object sf1705;
+        static Object sf1706;
+        static Object sf1707;
+        static Object sf1708;
+        static Object sf1709;
+        static Object sf1710;
+        static Object sf1711;
+        static Object sf1712;
+        static Object sf1713;
+        static Object sf1714;
+        static Object sf1715;
+        static Object sf1716;
+        static Object sf1717;
+        static Object sf1718;
+        static Object sf1719;
+        static Object sf1720;
+        static Object sf1721;
+        static Object sf1722;
+        static Object sf1723;
+        static Object sf1724;
+        static Object sf1725;
+        static Object sf1726;
+        static Object sf1727;
+        static Object sf1728;
+        static Object sf1729;
+        static Object sf1730;
+        static Object sf1731;
+        static Object sf1732;
+        static Object sf1733;
+        static Object sf1734;
+        static Object sf1735;
+        static Object sf1736;
+        static Object sf1737;
+        static Object sf1738;
+        static Object sf1739;
+        static Object sf1740;
+        static Object sf1741;
+        static Object sf1742;
+        static Object sf1743;
+        static Object sf1744;
+        static Object sf1745;
+        static Object sf1746;
+        static Object sf1747;
+        static Object sf1748;
+        static Object sf1749;
+        static Object sf1750;
+        static Object sf1751;
+        static Object sf1752;
+        static Object sf1753;
+        static Object sf1754;
+        static Object sf1755;
+        static Object sf1756;
+        static Object sf1757;
+        static Object sf1758;
+        static Object sf1759;
+        static Object sf1760;
+        static Object sf1761;
+        static Object sf1762;
+        static Object sf1763;
+        static Object sf1764;
+        static Object sf1765;
+        static Object sf1766;
+        static Object sf1767;
+        static Object sf1768;
+        static Object sf1769;
+        static Object sf1770;
+        static Object sf1771;
+        static Object sf1772;
+        static Object sf1773;
+        static Object sf1774;
+        static Object sf1775;
+        static Object sf1776;
+        static Object sf1777;
+        static Object sf1778;
+        static Object sf1779;
+        static Object sf1780;
+        static Object sf1781;
+        static Object sf1782;
+        static Object sf1783;
+        static Object sf1784;
+        static Object sf1785;
+        static Object sf1786;
+        static Object sf1787;
+        static Object sf1788;
+        static Object sf1789;
+        static Object sf1790;
+        static Object sf1791;
+        static Object sf1792;
+        static Object sf1793;
+        static Object sf1794;
+        static Object sf1795;
+        static Object sf1796;
+        static Object sf1797;
+        static Object sf1798;
+        static Object sf1799;
+        static Object sf1800;
+        static Object sf1801;
+        static Object sf1802;
+        static Object sf1803;
+        static Object sf1804;
+        static Object sf1805;
+        static Object sf1806;
+        static Object sf1807;
+        static Object sf1808;
+        static Object sf1809;
+        static Object sf1810;
+        static Object sf1811;
+        static Object sf1812;
+        static Object sf1813;
+        static Object sf1814;
+        static Object sf1815;
+        static Object sf1816;
+        static Object sf1817;
+        static Object sf1818;
+        static Object sf1819;
+        static Object sf1820;
+        static Object sf1821;
+        static Object sf1822;
+        static Object sf1823;
+        static Object sf1824;
+        static Object sf1825;
+        static Object sf1826;
+        static Object sf1827;
+        static Object sf1828;
+        static Object sf1829;
+        static Object sf1830;
+        static Object sf1831;
+        static Object sf1832;
+        static Object sf1833;
+        static Object sf1834;
+        static Object sf1835;
+        static Object sf1836;
+        static Object sf1837;
+        static Object sf1838;
+        static Object sf1839;
+        static Object sf1840;
+        static Object sf1841;
+        static Object sf1842;
+        static Object sf1843;
+        static Object sf1844;
+        static Object sf1845;
+        static Object sf1846;
+        static Object sf1847;
+        static Object sf1848;
+        static Object sf1849;
+        static Object sf1850;
+        static Object sf1851;
+        static Object sf1852;
+        static Object sf1853;
+        static Object sf1854;
+        static Object sf1855;
+        static Object sf1856;
+        static Object sf1857;
+        static Object sf1858;
+        static Object sf1859;
+        static Object sf1860;
+        static Object sf1861;
+        static Object sf1862;
+        static Object sf1863;
+        static Object sf1864;
+        static Object sf1865;
+        static Object sf1866;
+        static Object sf1867;
+        static Object sf1868;
+        static Object sf1869;
+        static Object sf1870;
+        static Object sf1871;
+        static Object sf1872;
+        static Object sf1873;
+        static Object sf1874;
+        static Object sf1875;
+        static Object sf1876;
+        static Object sf1877;
+        static Object sf1878;
+        static Object sf1879;
+        static Object sf1880;
+        static Object sf1881;
+        static Object sf1882;
+        static Object sf1883;
+        static Object sf1884;
+        static Object sf1885;
+        static Object sf1886;
+        static Object sf1887;
+        static Object sf1888;
+        static Object sf1889;
+        static Object sf1890;
+        static Object sf1891;
+        static Object sf1892;
+        static Object sf1893;
+        static Object sf1894;
+        static Object sf1895;
+        static Object sf1896;
+        static Object sf1897;
+        static Object sf1898;
+        static Object sf1899;
+        static Object sf1900;
+        static Object sf1901;
+        static Object sf1902;
+        static Object sf1903;
+        static Object sf1904;
+        static Object sf1905;
+        static Object sf1906;
+        static Object sf1907;
+        static Object sf1908;
+        static Object sf1909;
+        static Object sf1910;
+        static Object sf1911;
+        static Object sf1912;
+        static Object sf1913;
+        static Object sf1914;
+        static Object sf1915;
+        static Object sf1916;
+        static Object sf1917;
+        static Object sf1918;
+        static Object sf1919;
+        static Object sf1920;
+        static Object sf1921;
+        static Object sf1922;
+        static Object sf1923;
+        static Object sf1924;
+        static Object sf1925;
+        static Object sf1926;
+        static Object sf1927;
+        static Object sf1928;
+        static Object sf1929;
+        static Object sf1930;
+        static Object sf1931;
+        static Object sf1932;
+        static Object sf1933;
+        static Object sf1934;
+        static Object sf1935;
+        static Object sf1936;
+        static Object sf1937;
+        static Object sf1938;
+        static Object sf1939;
+        static Object sf1940;
+        static Object sf1941;
+        static Object sf1942;
+        static Object sf1943;
+        static Object sf1944;
+        static Object sf1945;
+        static Object sf1946;
+        static Object sf1947;
+        static Object sf1948;
+        static Object sf1949;
+        static Object sf1950;
+        static Object sf1951;
+        static Object sf1952;
+        static Object sf1953;
+        static Object sf1954;
+        static Object sf1955;
+        static Object sf1956;
+        static Object sf1957;
+        static Object sf1958;
+        static Object sf1959;
+        static Object sf1960;
+        static Object sf1961;
+        static Object sf1962;
+        static Object sf1963;
+        static Object sf1964;
+        static Object sf1965;
+        static Object sf1966;
+        static Object sf1967;
+        static Object sf1968;
+        static Object sf1969;
+        static Object sf1970;
+        static Object sf1971;
+        static Object sf1972;
+        static Object sf1973;
+        static Object sf1974;
+        static Object sf1975;
+        static Object sf1976;
+        static Object sf1977;
+        static Object sf1978;
+        static Object sf1979;
+        static Object sf1980;
+        static Object sf1981;
+        static Object sf1982;
+        static Object sf1983;
+        static Object sf1984;
+        static Object sf1985;
+        static Object sf1986;
+        static Object sf1987;
+        static Object sf1988;
+        static Object sf1989;
+        static Object sf1990;
+        static Object sf1991;
+        static Object sf1992;
+        static Object sf1993;
+        static Object sf1994;
+        static Object sf1995;
+        static Object sf1996;
+        static Object sf1997;
+        static Object sf1998;
+        static Object sf1999;
+        static Object sf2000;
+        static Object sf2001;
+        static Object sf2002;
+        static Object sf2003;
+        static Object sf2004;
+        static Object sf2005;
+        static Object sf2006;
+        static Object sf2007;
+        static Object sf2008;
+        static Object sf2009;
+        static Object sf2010;
+        static Object sf2011;
+        static Object sf2012;
+        static Object sf2013;
+        static Object sf2014;
+        static Object sf2015;
+        static Object sf2016;
+        static Object sf2017;
+        static Object sf2018;
+        static Object sf2019;
+        static Object sf2020;
+        static Object sf2021;
+        static Object sf2022;
+        static Object sf2023;
+        static Object sf2024;
+        static Object sf2025;
+        static Object sf2026;
+        static Object sf2027;
+        static Object sf2028;
+        static Object sf2029;
+        static Object sf2030;
+        static Object sf2031;
+        static Object sf2032;
+        static Object sf2033;
+        static Object sf2034;
+        static Object sf2035;
+        static Object sf2036;
+        static Object sf2037;
+        static Object sf2038;
+        static Object sf2039;
+        static Object sf2040;
+        static Object sf2041;
+        static Object sf2042;
+        static Object sf2043;
+        static Object sf2044;
+        static Object sf2045;
+        static Object sf2046;
+        static Object sf2047;
+        static Object sf2048;
+        static Object sf2049;
+        static Object sf2050;
+        static Object sf2051;
+        static Object sf2052;
+        static Object sf2053;
+        static Object sf2054;
+        static Object sf2055;
+        static Object sf2056;
+        static Object sf2057;
+        static Object sf2058;
+        static Object sf2059;
+        static Object sf2060;
+        static Object sf2061;
+        static Object sf2062;
+        static Object sf2063;
+        static Object sf2064;
+        static Object sf2065;
+        static Object sf2066;
+        static Object sf2067;
+        static Object sf2068;
+        static Object sf2069;
+        static Object sf2070;
+        static Object sf2071;
+        static Object sf2072;
+        static Object sf2073;
+        static Object sf2074;
+        static Object sf2075;
+        static Object sf2076;
+        static Object sf2077;
+        static Object sf2078;
+        static Object sf2079;
+        static Object sf2080;
+        static Object sf2081;
+        static Object sf2082;
+        static Object sf2083;
+        static Object sf2084;
+        static Object sf2085;
+        static Object sf2086;
+        static Object sf2087;
+        static Object sf2088;
+        static Object sf2089;
+        static Object sf2090;
+        static Object sf2091;
+        static Object sf2092;
+        static Object sf2093;
+        static Object sf2094;
+        static Object sf2095;
+        static Object sf2096;
+        static Object sf2097;
+        static Object sf2098;
+        static Object sf2099;
+        static Object sf2100;
+        static Object sf2101;
+        static Object sf2102;
+        static Object sf2103;
+        static Object sf2104;
+        static Object sf2105;
+        static Object sf2106;
+        static Object sf2107;
+        static Object sf2108;
+        static Object sf2109;
+        static Object sf2110;
+        static Object sf2111;
+        static Object sf2112;
+        static Object sf2113;
+        static Object sf2114;
+        static Object sf2115;
+        static Object sf2116;
+        static Object sf2117;
+        static Object sf2118;
+        static Object sf2119;
+        static Object sf2120;
+        static Object sf2121;
+        static Object sf2122;
+        static Object sf2123;
+        static Object sf2124;
+        static Object sf2125;
+        static Object sf2126;
+        static Object sf2127;
+        static Object sf2128;
+        static Object sf2129;
+        static Object sf2130;
+        static Object sf2131;
+        static Object sf2132;
+        static Object sf2133;
+        static Object sf2134;
+        static Object sf2135;
+        static Object sf2136;
+        static Object sf2137;
+        static Object sf2138;
+        static Object sf2139;
+        static Object sf2140;
+        static Object sf2141;
+        static Object sf2142;
+        static Object sf2143;
+        static Object sf2144;
+        static Object sf2145;
+        static Object sf2146;
+        static Object sf2147;
+        static Object sf2148;
+        static Object sf2149;
+        static Object sf2150;
+        static Object sf2151;
+        static Object sf2152;
+        static Object sf2153;
+        static Object sf2154;
+        static Object sf2155;
+        static Object sf2156;
+        static Object sf2157;
+        static Object sf2158;
+        static Object sf2159;
+        static Object sf2160;
+        static Object sf2161;
+        static Object sf2162;
+        static Object sf2163;
+        static Object sf2164;
+        static Object sf2165;
+        static Object sf2166;
+        static Object sf2167;
+        static Object sf2168;
+        static Object sf2169;
+        static Object sf2170;
+        static Object sf2171;
+        static Object sf2172;
+        static Object sf2173;
+        static Object sf2174;
+        static Object sf2175;
+        static Object sf2176;
+        static Object sf2177;
+        static Object sf2178;
+        static Object sf2179;
+        static Object sf2180;
+        static Object sf2181;
+        static Object sf2182;
+        static Object sf2183;
+        static Object sf2184;
+        static Object sf2185;
+        static Object sf2186;
+        static Object sf2187;
+        static Object sf2188;
+        static Object sf2189;
+        static Object sf2190;
+        static Object sf2191;
+        static Object sf2192;
+        static Object sf2193;
+        static Object sf2194;
+        static Object sf2195;
+        static Object sf2196;
+        static Object sf2197;
+        static Object sf2198;
+        static Object sf2199;
+        static Object sf2200;
+        static Object sf2201;
+        static Object sf2202;
+        static Object sf2203;
+        static Object sf2204;
+        static Object sf2205;
+        static Object sf2206;
+        static Object sf2207;
+        static Object sf2208;
+        static Object sf2209;
+        static Object sf2210;
+        static Object sf2211;
+        static Object sf2212;
+        static Object sf2213;
+        static Object sf2214;
+        static Object sf2215;
+        static Object sf2216;
+        static Object sf2217;
+        static Object sf2218;
+        static Object sf2219;
+        static Object sf2220;
+        static Object sf2221;
+        static Object sf2222;
+        static Object sf2223;
+        static Object sf2224;
+        static Object sf2225;
+        static Object sf2226;
+        static Object sf2227;
+        static Object sf2228;
+        static Object sf2229;
+        static Object sf2230;
+        static Object sf2231;
+        static Object sf2232;
+        static Object sf2233;
+        static Object sf2234;
+        static Object sf2235;
+        static Object sf2236;
+        static Object sf2237;
+        static Object sf2238;
+        static Object sf2239;
+        static Object sf2240;
+        static Object sf2241;
+        static Object sf2242;
+        static Object sf2243;
+        static Object sf2244;
+        static Object sf2245;
+        static Object sf2246;
+        static Object sf2247;
+        static Object sf2248;
+        static Object sf2249;
+        static Object sf2250;
+        static Object sf2251;
+        static Object sf2252;
+        static Object sf2253;
+        static Object sf2254;
+        static Object sf2255;
+        static Object sf2256;
+        static Object sf2257;
+        static Object sf2258;
+        static Object sf2259;
+        static Object sf2260;
+        static Object sf2261;
+        static Object sf2262;
+        static Object sf2263;
+        static Object sf2264;
+        static Object sf2265;
+        static Object sf2266;
+        static Object sf2267;
+        static Object sf2268;
+        static Object sf2269;
+        static Object sf2270;
+        static Object sf2271;
+        static Object sf2272;
+        static Object sf2273;
+        static Object sf2274;
+        static Object sf2275;
+        static Object sf2276;
+        static Object sf2277;
+        static Object sf2278;
+        static Object sf2279;
+        static Object sf2280;
+        static Object sf2281;
+        static Object sf2282;
+        static Object sf2283;
+        static Object sf2284;
+        static Object sf2285;
+        static Object sf2286;
+        static Object sf2287;
+        static Object sf2288;
+        static Object sf2289;
+        static Object sf2290;
+        static Object sf2291;
+        static Object sf2292;
+        static Object sf2293;
+        static Object sf2294;
+        static Object sf2295;
+        static Object sf2296;
+        static Object sf2297;
+        static Object sf2298;
+        static Object sf2299;
+        static Object sf2300;
+        static Object sf2301;
+        static Object sf2302;
+        static Object sf2303;
+        static Object sf2304;
+        static Object sf2305;
+        static Object sf2306;
+        static Object sf2307;
+        static Object sf2308;
+        static Object sf2309;
+        static Object sf2310;
+        static Object sf2311;
+        static Object sf2312;
+        static Object sf2313;
+        static Object sf2314;
+        static Object sf2315;
+        static Object sf2316;
+        static Object sf2317;
+        static Object sf2318;
+        static Object sf2319;
+        static Object sf2320;
+        static Object sf2321;
+        static Object sf2322;
+        static Object sf2323;
+        static Object sf2324;
+        static Object sf2325;
+        static Object sf2326;
+        static Object sf2327;
+        static Object sf2328;
+        static Object sf2329;
+        static Object sf2330;
+        static Object sf2331;
+        static Object sf2332;
+        static Object sf2333;
+        static Object sf2334;
+        static Object sf2335;
+        static Object sf2336;
+        static Object sf2337;
+        static Object sf2338;
+        static Object sf2339;
+        static Object sf2340;
+        static Object sf2341;
+        static Object sf2342;
+        static Object sf2343;
+        static Object sf2344;
+        static Object sf2345;
+        static Object sf2346;
+        static Object sf2347;
+        static Object sf2348;
+        static Object sf2349;
+        static Object sf2350;
+        static Object sf2351;
+        static Object sf2352;
+        static Object sf2353;
+        static Object sf2354;
+        static Object sf2355;
+        static Object sf2356;
+        static Object sf2357;
+        static Object sf2358;
+        static Object sf2359;
+        static Object sf2360;
+        static Object sf2361;
+        static Object sf2362;
+        static Object sf2363;
+        static Object sf2364;
+        static Object sf2365;
+        static Object sf2366;
+        static Object sf2367;
+        static Object sf2368;
+        static Object sf2369;
+        static Object sf2370;
+        static Object sf2371;
+        static Object sf2372;
+        static Object sf2373;
+        static Object sf2374;
+        static Object sf2375;
+        static Object sf2376;
+        static Object sf2377;
+        static Object sf2378;
+        static Object sf2379;
+        static Object sf2380;
+        static Object sf2381;
+        static Object sf2382;
+        static Object sf2383;
+        static Object sf2384;
+        static Object sf2385;
+        static Object sf2386;
+        static Object sf2387;
+        static Object sf2388;
+        static Object sf2389;
+        static Object sf2390;
+        static Object sf2391;
+        static Object sf2392;
+        static Object sf2393;
+        static Object sf2394;
+        static Object sf2395;
+        static Object sf2396;
+        static Object sf2397;
+        static Object sf2398;
+        static Object sf2399;
+        static Object sf2400;
+        static Object sf2401;
+        static Object sf2402;
+        static Object sf2403;
+        static Object sf2404;
+        static Object sf2405;
+        static Object sf2406;
+        static Object sf2407;
+        static Object sf2408;
+        static Object sf2409;
+        static Object sf2410;
+        static Object sf2411;
+        static Object sf2412;
+        static Object sf2413;
+        static Object sf2414;
+        static Object sf2415;
+        static Object sf2416;
+        static Object sf2417;
+        static Object sf2418;
+        static Object sf2419;
+        static Object sf2420;
+        static Object sf2421;
+        static Object sf2422;
+        static Object sf2423;
+        static Object sf2424;
+        static Object sf2425;
+        static Object sf2426;
+        static Object sf2427;
+        static Object sf2428;
+        static Object sf2429;
+        static Object sf2430;
+        static Object sf2431;
+        static Object sf2432;
+        static Object sf2433;
+        static Object sf2434;
+        static Object sf2435;
+        static Object sf2436;
+        static Object sf2437;
+        static Object sf2438;
+        static Object sf2439;
+        static Object sf2440;
+        static Object sf2441;
+        static Object sf2442;
+        static Object sf2443;
+        static Object sf2444;
+        static Object sf2445;
+        static Object sf2446;
+        static Object sf2447;
+        static Object sf2448;
+        static Object sf2449;
+        static Object sf2450;
+        static Object sf2451;
+        static Object sf2452;
+        static Object sf2453;
+        static Object sf2454;
+        static Object sf2455;
+        static Object sf2456;
+        static Object sf2457;
+        static Object sf2458;
+        static Object sf2459;
+        static Object sf2460;
+        static Object sf2461;
+        static Object sf2462;
+        static Object sf2463;
+        static Object sf2464;
+        static Object sf2465;
+        static Object sf2466;
+        static Object sf2467;
+        static Object sf2468;
+        static Object sf2469;
+        static Object sf2470;
+        static Object sf2471;
+        static Object sf2472;
+        static Object sf2473;
+        static Object sf2474;
+        static Object sf2475;
+        static Object sf2476;
+        static Object sf2477;
+        static Object sf2478;
+        static Object sf2479;
+        static Object sf2480;
+        static Object sf2481;
+        static Object sf2482;
+        static Object sf2483;
+        static Object sf2484;
+        static Object sf2485;
+        static Object sf2486;
+        static Object sf2487;
+        static Object sf2488;
+        static Object sf2489;
+        static Object sf2490;
+        static Object sf2491;
+        static Object sf2492;
+        static Object sf2493;
+        static Object sf2494;
+        static Object sf2495;
+        static Object sf2496;
+        static Object sf2497;
+        static Object sf2498;
+        static Object sf2499;
+        static Object sf2500;
+        static Object sf2501;
+        static Object sf2502;
+        static Object sf2503;
+        static Object sf2504;
+        static Object sf2505;
+        static Object sf2506;
+        static Object sf2507;
+        static Object sf2508;
+        static Object sf2509;
+        static Object sf2510;
+        static Object sf2511;
+        static Object sf2512;
+        static Object sf2513;
+        static Object sf2514;
+        static Object sf2515;
+        static Object sf2516;
+        static Object sf2517;
+        static Object sf2518;
+        static Object sf2519;
+        static Object sf2520;
+        static Object sf2521;
+        static Object sf2522;
+        static Object sf2523;
+        static Object sf2524;
+        static Object sf2525;
+        static Object sf2526;
+        static Object sf2527;
+        static Object sf2528;
+        static Object sf2529;
+        static Object sf2530;
+        static Object sf2531;
+        static Object sf2532;
+        static Object sf2533;
+        static Object sf2534;
+        static Object sf2535;
+        static Object sf2536;
+        static Object sf2537;
+        static Object sf2538;
+        static Object sf2539;
+        static Object sf2540;
+        static Object sf2541;
+        static Object sf2542;
+        static Object sf2543;
+        static Object sf2544;
+        static Object sf2545;
+        static Object sf2546;
+        static Object sf2547;
+        static Object sf2548;
+        static Object sf2549;
+        static Object sf2550;
+        static Object sf2551;
+        static Object sf2552;
+        static Object sf2553;
+        static Object sf2554;
+        static Object sf2555;
+        static Object sf2556;
+        static Object sf2557;
+        static Object sf2558;
+        static Object sf2559;
+        static Object sf2560;
+        static Object sf2561;
+        static Object sf2562;
+        static Object sf2563;
+        static Object sf2564;
+        static Object sf2565;
+        static Object sf2566;
+        static Object sf2567;
+        static Object sf2568;
+        static Object sf2569;
+        static Object sf2570;
+        static Object sf2571;
+        static Object sf2572;
+        static Object sf2573;
+        static Object sf2574;
+        static Object sf2575;
+        static Object sf2576;
+        static Object sf2577;
+        static Object sf2578;
+        static Object sf2579;
+        static Object sf2580;
+        static Object sf2581;
+        static Object sf2582;
+        static Object sf2583;
+        static Object sf2584;
+        static Object sf2585;
+        static Object sf2586;
+        static Object sf2587;
+        static Object sf2588;
+        static Object sf2589;
+        static Object sf2590;
+        static Object sf2591;
+        static Object sf2592;
+        static Object sf2593;
+        static Object sf2594;
+        static Object sf2595;
+        static Object sf2596;
+        static Object sf2597;
+        static Object sf2598;
+        static Object sf2599;
+        static Object sf2600;
+        static Object sf2601;
+        static Object sf2602;
+        static Object sf2603;
+        static Object sf2604;
+        static Object sf2605;
+        static Object sf2606;
+        static Object sf2607;
+        static Object sf2608;
+        static Object sf2609;
+        static Object sf2610;
+        static Object sf2611;
+        static Object sf2612;
+        static Object sf2613;
+        static Object sf2614;
+        static Object sf2615;
+        static Object sf2616;
+        static Object sf2617;
+        static Object sf2618;
+        static Object sf2619;
+        static Object sf2620;
+        static Object sf2621;
+        static Object sf2622;
+        static Object sf2623;
+        static Object sf2624;
+        static Object sf2625;
+        static Object sf2626;
+        static Object sf2627;
+        static Object sf2628;
+        static Object sf2629;
+        static Object sf2630;
+        static Object sf2631;
+        static Object sf2632;
+        static Object sf2633;
+        static Object sf2634;
+        static Object sf2635;
+        static Object sf2636;
+        static Object sf2637;
+        static Object sf2638;
+        static Object sf2639;
+        static Object sf2640;
+        static Object sf2641;
+        static Object sf2642;
+        static Object sf2643;
+        static Object sf2644;
+        static Object sf2645;
+        static Object sf2646;
+        static Object sf2647;
+        static Object sf2648;
+        static Object sf2649;
+        static Object sf2650;
+        static Object sf2651;
+        static Object sf2652;
+        static Object sf2653;
+        static Object sf2654;
+        static Object sf2655;
+        static Object sf2656;
+        static Object sf2657;
+        static Object sf2658;
+        static Object sf2659;
+        static Object sf2660;
+        static Object sf2661;
+        static Object sf2662;
+        static Object sf2663;
+        static Object sf2664;
+        static Object sf2665;
+        static Object sf2666;
+        static Object sf2667;
+        static Object sf2668;
+        static Object sf2669;
+        static Object sf2670;
+        static Object sf2671;
+        static Object sf2672;
+        static Object sf2673;
+        static Object sf2674;
+        static Object sf2675;
+        static Object sf2676;
+        static Object sf2677;
+        static Object sf2678;
+        static Object sf2679;
+        static Object sf2680;
+        static Object sf2681;
+        static Object sf2682;
+        static Object sf2683;
+        static Object sf2684;
+        static Object sf2685;
+        static Object sf2686;
+        static Object sf2687;
+        static Object sf2688;
+        static Object sf2689;
+        static Object sf2690;
+        static Object sf2691;
+        static Object sf2692;
+        static Object sf2693;
+        static Object sf2694;
+        static Object sf2695;
+        static Object sf2696;
+        static Object sf2697;
+        static Object sf2698;
+        static Object sf2699;
+        static Object sf2700;
+        static Object sf2701;
+        static Object sf2702;
+        static Object sf2703;
+        static Object sf2704;
+        static Object sf2705;
+        static Object sf2706;
+        static Object sf2707;
+        static Object sf2708;
+        static Object sf2709;
+        static Object sf2710;
+        static Object sf2711;
+        static Object sf2712;
+        static Object sf2713;
+        static Object sf2714;
+        static Object sf2715;
+        static Object sf2716;
+        static Object sf2717;
+        static Object sf2718;
+        static Object sf2719;
+        static Object sf2720;
+        static Object sf2721;
+        static Object sf2722;
+        static Object sf2723;
+        static Object sf2724;
+        static Object sf2725;
+        static Object sf2726;
+        static Object sf2727;
+        static Object sf2728;
+        static Object sf2729;
+        static Object sf2730;
+        static Object sf2731;
+        static Object sf2732;
+        static Object sf2733;
+        static Object sf2734;
+        static Object sf2735;
+        static Object sf2736;
+        static Object sf2737;
+        static Object sf2738;
+        static Object sf2739;
+        static Object sf2740;
+        static Object sf2741;
+        static Object sf2742;
+        static Object sf2743;
+        static Object sf2744;
+        static Object sf2745;
+        static Object sf2746;
+        static Object sf2747;
+        static Object sf2748;
+        static Object sf2749;
+        static Object sf2750;
+        static Object sf2751;
+        static Object sf2752;
+        static Object sf2753;
+        static Object sf2754;
+        static Object sf2755;
+        static Object sf2756;
+        static Object sf2757;
+        static Object sf2758;
+        static Object sf2759;
+        static Object sf2760;
+        static Object sf2761;
+        static Object sf2762;
+        static Object sf2763;
+        static Object sf2764;
+        static Object sf2765;
+        static Object sf2766;
+        static Object sf2767;
+        static Object sf2768;
+        static Object sf2769;
+        static Object sf2770;
+        static Object sf2771;
+        static Object sf2772;
+        static Object sf2773;
+        static Object sf2774;
+        static Object sf2775;
+        static Object sf2776;
+        static Object sf2777;
+        static Object sf2778;
+        static Object sf2779;
+        static Object sf2780;
+        static Object sf2781;
+        static Object sf2782;
+        static Object sf2783;
+        static Object sf2784;
+        static Object sf2785;
+        static Object sf2786;
+        static Object sf2787;
+        static Object sf2788;
+        static Object sf2789;
+        static Object sf2790;
+        static Object sf2791;
+        static Object sf2792;
+        static Object sf2793;
+        static Object sf2794;
+        static Object sf2795;
+        static Object sf2796;
+        static Object sf2797;
+        static Object sf2798;
+        static Object sf2799;
+        static Object sf2800;
+        static Object sf2801;
+        static Object sf2802;
+        static Object sf2803;
+        static Object sf2804;
+        static Object sf2805;
+        static Object sf2806;
+        static Object sf2807;
+        static Object sf2808;
+        static Object sf2809;
+        static Object sf2810;
+        static Object sf2811;
+        static Object sf2812;
+        static Object sf2813;
+        static Object sf2814;
+        static Object sf2815;
+        static Object sf2816;
+        static Object sf2817;
+        static Object sf2818;
+        static Object sf2819;
+        static Object sf2820;
+        static Object sf2821;
+        static Object sf2822;
+        static Object sf2823;
+        static Object sf2824;
+        static Object sf2825;
+        static Object sf2826;
+        static Object sf2827;
+        static Object sf2828;
+        static Object sf2829;
+        static Object sf2830;
+        static Object sf2831;
+        static Object sf2832;
+        static Object sf2833;
+        static Object sf2834;
+        static Object sf2835;
+        static Object sf2836;
+        static Object sf2837;
+        static Object sf2838;
+        static Object sf2839;
+        static Object sf2840;
+        static Object sf2841;
+        static Object sf2842;
+        static Object sf2843;
+        static Object sf2844;
+        static Object sf2845;
+        static Object sf2846;
+        static Object sf2847;
+        static Object sf2848;
+        static Object sf2849;
+        static Object sf2850;
+        static Object sf2851;
+        static Object sf2852;
+        static Object sf2853;
+        static Object sf2854;
+        static Object sf2855;
+        static Object sf2856;
+        static Object sf2857;
+        static Object sf2858;
+        static Object sf2859;
+        static Object sf2860;
+        static Object sf2861;
+        static Object sf2862;
+        static Object sf2863;
+        static Object sf2864;
+        static Object sf2865;
+        static Object sf2866;
+        static Object sf2867;
+        static Object sf2868;
+        static Object sf2869;
+        static Object sf2870;
+        static Object sf2871;
+        static Object sf2872;
+        static Object sf2873;
+        static Object sf2874;
+        static Object sf2875;
+        static Object sf2876;
+        static Object sf2877;
+        static Object sf2878;
+        static Object sf2879;
+        static Object sf2880;
+        static Object sf2881;
+        static Object sf2882;
+        static Object sf2883;
+        static Object sf2884;
+        static Object sf2885;
+        static Object sf2886;
+        static Object sf2887;
+        static Object sf2888;
+        static Object sf2889;
+        static Object sf2890;
+        static Object sf2891;
+        static Object sf2892;
+        static Object sf2893;
+        static Object sf2894;
+        static Object sf2895;
+        static Object sf2896;
+        static Object sf2897;
+        static Object sf2898;
+        static Object sf2899;
+        static Object sf2900;
+        static Object sf2901;
+        static Object sf2902;
+        static Object sf2903;
+        static Object sf2904;
+        static Object sf2905;
+        static Object sf2906;
+        static Object sf2907;
+        static Object sf2908;
+        static Object sf2909;
+        static Object sf2910;
+        static Object sf2911;
+        static Object sf2912;
+        static Object sf2913;
+        static Object sf2914;
+        static Object sf2915;
+        static Object sf2916;
+        static Object sf2917;
+        static Object sf2918;
+        static Object sf2919;
+        static Object sf2920;
+        static Object sf2921;
+        static Object sf2922;
+        static Object sf2923;
+        static Object sf2924;
+        static Object sf2925;
+        static Object sf2926;
+        static Object sf2927;
+        static Object sf2928;
+        static Object sf2929;
+        static Object sf2930;
+        static Object sf2931;
+        static Object sf2932;
+        static Object sf2933;
+        static Object sf2934;
+        static Object sf2935;
+        static Object sf2936;
+        static Object sf2937;
+        static Object sf2938;
+        static Object sf2939;
+        static Object sf2940;
+        static Object sf2941;
+        static Object sf2942;
+        static Object sf2943;
+        static Object sf2944;
+        static Object sf2945;
+        static Object sf2946;
+        static Object sf2947;
+        static Object sf2948;
+        static Object sf2949;
+        static Object sf2950;
+        static Object sf2951;
+        static Object sf2952;
+        static Object sf2953;
+        static Object sf2954;
+        static Object sf2955;
+        static Object sf2956;
+        static Object sf2957;
+        static Object sf2958;
+        static Object sf2959;
+        static Object sf2960;
+        static Object sf2961;
+        static Object sf2962;
+        static Object sf2963;
+        static Object sf2964;
+        static Object sf2965;
+        static Object sf2966;
+        static Object sf2967;
+        static Object sf2968;
+        static Object sf2969;
+        static Object sf2970;
+        static Object sf2971;
+        static Object sf2972;
+        static Object sf2973;
+        static Object sf2974;
+        static Object sf2975;
+        static Object sf2976;
+        static Object sf2977;
+        static Object sf2978;
+        static Object sf2979;
+        static Object sf2980;
+        static Object sf2981;
+        static Object sf2982;
+        static Object sf2983;
+        static Object sf2984;
+        static Object sf2985;
+        static Object sf2986;
+        static Object sf2987;
+        static Object sf2988;
+        static Object sf2989;
+        static Object sf2990;
+        static Object sf2991;
+        static Object sf2992;
+        static Object sf2993;
+        static Object sf2994;
+        static Object sf2995;
+        static Object sf2996;
+        static Object sf2997;
+        static Object sf2998;
+        static Object sf2999;
+        static Object sf3000;
+    }
+
+}
diff --git a/test/122-secondarydex/build b/test/127-secondarydex/build
similarity index 100%
rename from test/122-secondarydex/build
rename to test/127-secondarydex/build
diff --git a/test/122-secondarydex/expected.txt b/test/127-secondarydex/expected.txt
similarity index 100%
rename from test/122-secondarydex/expected.txt
rename to test/127-secondarydex/expected.txt
diff --git a/test/122-secondarydex/info.txt b/test/127-secondarydex/info.txt
similarity index 100%
rename from test/122-secondarydex/info.txt
rename to test/127-secondarydex/info.txt
diff --git a/test/122-secondarydex/run b/test/127-secondarydex/run
similarity index 100%
rename from test/122-secondarydex/run
rename to test/127-secondarydex/run
diff --git a/test/122-secondarydex/src/Main.java b/test/127-secondarydex/src/Main.java
similarity index 100%
rename from test/122-secondarydex/src/Main.java
rename to test/127-secondarydex/src/Main.java
diff --git a/test/122-secondarydex/src/Super.java b/test/127-secondarydex/src/Super.java
similarity index 100%
rename from test/122-secondarydex/src/Super.java
rename to test/127-secondarydex/src/Super.java
diff --git a/test/122-secondarydex/src/Test.java b/test/127-secondarydex/src/Test.java
similarity index 100%
rename from test/122-secondarydex/src/Test.java
rename to test/127-secondarydex/src/Test.java
diff --git a/test/303-verification-stress/build b/test/303-verification-stress/build
index c1935d2..789d38e 100644
--- a/test/303-verification-stress/build
+++ b/test/303-verification-stress/build
@@ -18,7 +18,7 @@
 set -e
 
 # Write out a bunch of source files.
-gcc -o classes-gen classes-gen.c
+gcc -Wall -Werror -o classes-gen classes-gen.c
 ./classes-gen
 
 mkdir classes
diff --git a/test/303-verification-stress/classes-gen.c b/test/303-verification-stress/classes-gen.c
index be6cfa7..1f2fe3b 100644
--- a/test/303-verification-stress/classes-gen.c
+++ b/test/303-verification-stress/classes-gen.c
@@ -26,11 +26,11 @@
 
         fprintf(fp, "public class Test%03d {\n", i);
         fprintf(fp, "    static String[] array = new String[%d];\n", array_size);
-        fprintf(fp, "    static {\n", array_size);
+        fprintf(fp, "    static {\n");
         for (k = 0; k < array_size; k++) {
             fprintf(fp, "        array[%d] = \"string_%04d\";\n", k, k);
         }
-        fprintf(fp, "    }\n", array_size);
+        fprintf(fp, "    }\n");
         fprintf(fp, "}\n");
         fclose(fp);
     }
diff --git a/test/401-optimizing-compiler/src/Main.java b/test/401-optimizing-compiler/src/Main.java
index 2c6d1c2..7c3fd25 100644
--- a/test/401-optimizing-compiler/src/Main.java
+++ b/test/401-optimizing-compiler/src/Main.java
@@ -94,9 +94,22 @@
       exception = e;
     }
 
+    // Test that we do NPE checks on array length.
+    exception = null;
+    try {
+      $opt$ArrayLengthOfNull(null);
+    } catch (NullPointerException e) {
+      exception = e;
+    }
+
     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 +218,17 @@
     m.o = new Main();
   }
 
+  public static int $opt$InvokeVirtualMethod() {
+    return new Main().virtualMethod();
+  }
+
+  public int virtualMethod() {
+    return 42;
+  }
+
+  public static int $opt$ArrayLengthOfNull(int[] array) {
+    return array.length;
+  }
+
   Object o;
 }
diff --git a/test/406-fields/src/Main.java b/test/406-fields/src/Main.java
index 3e94e42..768a784 100644
--- a/test/406-fields/src/Main.java
+++ b/test/406-fields/src/Main.java
@@ -30,6 +30,8 @@
     assertEquals(0, fields.iI);
     assertEquals(0, fields.iJ);
     assertEquals(0, fields.iS);
+    assertEquals(0.0f, fields.iF);
+    assertEquals(0.0, fields.iD);
     assertNull(fields.iObject);
 
     long longValue = -1122198787987987987L;
@@ -40,6 +42,8 @@
     fields.iJ = longValue;
     fields.iS = 68;
     fields.iObject = fields;
+    fields.iF = 2.3f;
+    fields.iD = 5.3;
 
     assertEquals(true, fields.iZ);
     assertEquals(-2, fields.iB);
@@ -48,6 +52,8 @@
     assertEquals(longValue, fields.iJ);
     assertEquals(68, fields.iS);
     assertEquals(fields, fields.iObject);
+    assertEquals(2.3f, fields.iF);
+    assertEquals(5.3, fields.iD);
   }
 
   static class AllFields {
diff --git a/test/407-arrays/src/Main.java b/test/407-arrays/src/Main.java
index 5d27e6d..d5c5604 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;
@@ -70,6 +70,15 @@
     chars[index] = 'd';
     assertEquals('d', chars[index]);
 
+    chars[0] = 65535;
+    assertEquals(65535, chars[0]);
+    // Do an update between the two max value updates, to avoid
+    // optimizing the second away.
+    chars[index] = 0;
+    assertEquals(0, chars[index]);
+    chars[index] = 65535;
+    assertEquals(65535, chars[index]);
+
     shorts[0] = -42;
     assertEquals(-42, shorts[0]);
     shorts[index] = -84;
@@ -86,7 +95,13 @@
     Object o2 = new Object();
     objects[index] = o2;
     assertEquals(o2, objects[index]);
+    // Longs are initially not supported in the linear scan register allocator
+    // on 32bits. So we call out a long helper to ensure this method gets
+    // optimized.
+    $opt$testLongWrites(longs, index);
+  }
 
+  public static void $opt$testLongWrites(long[] longs, int index) {
     long l = -21876876876876876L;
     longs[0] = l;
     assertEquals(l, longs[0]);
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/410-floats/expected.txt b/test/410-floats/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/410-floats/expected.txt
diff --git a/test/410-floats/info.txt b/test/410-floats/info.txt
new file mode 100644
index 0000000..5332704
--- /dev/null
+++ b/test/410-floats/info.txt
@@ -0,0 +1 @@
+Small tests involving floats and doubles.
diff --git a/test/410-floats/src/Main.java b/test/410-floats/src/Main.java
new file mode 100644
index 0000000..2300457
--- /dev/null
+++ b/test/410-floats/src/Main.java
@@ -0,0 +1,140 @@
+/*
+ * 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) {
+    assertEquals(4.2f, returnFloat());
+    float[] a = new float[2];
+    a[0] = 42.2f;
+    a[1] = 3.2f;
+    assertEquals(45.4f, returnFloat(a));
+
+    assertEquals(4.4, returnDouble());
+    double[] b = new double[1];
+    b[0] = 42.4;
+    assertEquals(42.4, returnDouble(b));
+
+    assertEquals(4.2f, invokeReturnFloat());
+    assertEquals(4.4, invokeReturnDouble());
+    assertEquals(4.2f, takeAFloat(4.2f));
+    assertEquals(3.1, takeADouble(3.1));
+    assertEquals(12.7, takeThreeDouble(3.1, 4.4, 5.2));
+    assertEquals(12.7f, takeThreeFloat(3.1f, 4.4f, 5.2f));
+    assertEquals(4.2f, invokeTakeAFloat(4.2f));
+    assertEquals(3.1, invokeTakeADouble(3.1));
+    assertEquals(12.7, invokeTakeThreeDouble(3.1, 4.4, 5.2));
+    assertEquals(12.7f, invokeTakeThreeFloat(3.1f, 4.4f, 5.2f));
+
+    testArrayOperations(new float[2], 0, 1.2f, 3.4f);
+    testArrayOperations(new double[2], 0, 4.1, 7.6);
+  }
+
+  public static float invokeReturnFloat() {
+    return returnFloat();
+  }
+
+  public static double invokeReturnDouble() {
+    return returnDouble();
+  }
+
+  public static float returnFloat() {
+    return 4.2f;
+  }
+
+  public static float returnFloat(float[] a) {
+    return a[0] + a[1];
+  }
+
+  public static double returnDouble() {
+    return 4.4;
+  }
+
+  public static double returnDouble(double[] a) {
+    return a[0];
+  }
+
+  public static float takeAFloat(float a) {
+    return a;
+  }
+
+  public static double takeADouble(double a) {
+    return a;
+  }
+
+  public static double takeThreeDouble(double a, double b, double c) {
+    return a + b + c;
+  }
+
+  public static float takeThreeFloat(float a, float b, float c) {
+    return a + b + c;
+  }
+
+  public static float invokeTakeAFloat(float a) {
+    return takeAFloat(a);
+  }
+
+  public static double invokeTakeADouble(double a) {
+    return takeADouble(a);
+  }
+
+  public static double invokeTakeThreeDouble(double a, double b, double c) {
+    return takeThreeDouble(a, b, c);
+  }
+
+  public static float invokeTakeThreeFloat(float a, float b, float c) {
+    return takeThreeFloat(a, b, c);
+  }
+
+  // Test simple operations on a float array to ensure the register allocator works
+  // properly.
+  public static void testArrayOperations(float[] a, int index, float value1, float value2) {
+    a[0] = value1;
+    a[1] = value2;
+    assertEquals(value1 + value2, a[0] + a[1]);
+    a[0] = 0.0f;
+    a[1] = 0.0f;
+    assertEquals(0.0f, a[0] + a[1]);
+    a[index] = value1;
+    a[index + 1] = value2;
+    assertEquals(value1 + value2, a[0] + a[1]);
+  }
+
+  // Test simple operations on a double array to ensure the register allocator works
+  // properly.
+  public static void testArrayOperations(double[] a, int index, double value1, double value2) {
+    a[0] = value1;
+    a[1] = value2;
+    assertEquals(value1 + value2, a[0] + a[1]);
+    a[0] = 0.0;
+    a[1] = 0.0;
+    assertEquals(0.0, a[0] + a[1]);
+    a[index] = value1;
+    a[index + 1] = value2;
+    assertEquals(value1 + value2, a[0] + a[1]);
+  }
+
+  public static void assertEquals(float expected, float actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(double expected, double actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+}
diff --git a/test/411-optimizing-arith/expected.txt b/test/411-optimizing-arith/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/411-optimizing-arith/expected.txt
diff --git a/test/411-optimizing-arith/info.txt b/test/411-optimizing-arith/info.txt
new file mode 100644
index 0000000..1015551
--- /dev/null
+++ b/test/411-optimizing-arith/info.txt
@@ -0,0 +1 @@
+Tests for basic arithmethic operations.
diff --git a/test/411-optimizing-arith/src/Main.java b/test/411-optimizing-arith/src/Main.java
new file mode 100644
index 0000000..a22c516
--- /dev/null
+++ b/test/411-optimizing-arith/src/Main.java
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+// Note that $opt$ is a marker for the optimizing compiler to ensure
+// it does compile the method.
+public class Main {
+
+  public static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(float expected, float result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(double expected, double result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectApproxEquals(float a, float b, float maxDelta) {
+    boolean aproxEquals = (a > b)
+      ? ((a - b) < maxDelta)
+      : ((b - a) < maxDelta);
+    if (!aproxEquals) {
+      throw new Error("Expected: " + a + ", found: " + b + ", with delta: " + maxDelta);
+    }
+  }
+
+  public static void expectApproxEquals(double a, double b, double maxDelta) {
+    boolean aproxEquals = (a > b)
+      ? ((a - b) < maxDelta)
+      : ((b - a) < maxDelta);
+    if (!aproxEquals) {
+      throw new Error("Expected: " + a + ", found: " + b + ", with delta: " + maxDelta);
+    }
+  }
+
+  public static void expectNaN(float a) {
+    if (a == a) {
+      throw new Error("Expected NaN: " + a);
+    }
+  }
+
+  public static void expectNaN(double a) {
+    if (a == a) {
+      throw new Error("Expected NaN: " + a);
+    }
+  }
+
+  public static void main(String[] args) {
+    mul();
+  }
+
+  public static void mul() {
+    mulInt();
+    mulLong();
+    mulFloat();
+    mulDouble();
+  }
+
+  private static void mulInt() {
+    expectEquals(15, $opt$Mul(5, 3));
+    expectEquals(0, $opt$Mul(0, 0));
+    expectEquals(0, $opt$Mul(0, 3));
+    expectEquals(0, $opt$Mul(3, 0));
+    expectEquals(-3, $opt$Mul(1, -3));
+    expectEquals(36, $opt$Mul(-12, -3));
+    expectEquals(33, $opt$Mul(1, 3) * 11);
+    expectEquals(671088645, $opt$Mul(134217729, 5)); // (2^27 + 1) * 5
+  }
+
+  private static void mulLong() {
+    expectEquals(15L, $opt$Mul(5L, 3L));
+    expectEquals(0L, $opt$Mul(0L, 0L));
+    expectEquals(0L, $opt$Mul(0L, 3L));
+    expectEquals(0L, $opt$Mul(3L, 0L));
+    expectEquals(-3L, $opt$Mul(1L, -3L));
+    expectEquals(36L, $opt$Mul(-12L, -3L));
+    expectEquals(33L, $opt$Mul(1L, 3L) * 11F);
+    expectEquals(240518168583L, $opt$Mul(34359738369L, 7L)); // (2^35 + 1) * 7
+  }
+
+  private static void mulFloat() {
+    expectApproxEquals(15F, $opt$Mul(5F, 3F), 0.0001F);
+    expectApproxEquals(0F, $opt$Mul(0F, 0F), 0.0001F);
+    expectApproxEquals(0F, $opt$Mul(0F, 3F), 0.0001F);
+    expectApproxEquals(0F, $opt$Mul(3F, 0F), 0.0001F);
+    expectApproxEquals(-3F, $opt$Mul(1F, -3F), 0.0001F);
+    expectApproxEquals(36F, $opt$Mul(-12F, -3F), 0.0001F);
+    expectApproxEquals(33F, $opt$Mul(1F, 3F) * 11F, 0.0001F);
+    expectApproxEquals(0.02F, 0.1F * 0.2F, 0.0001F);
+    expectApproxEquals(-0.1F, -0.5F * 0.2F, 0.0001F);
+
+    expectNaN($opt$Mul(0F, Float.POSITIVE_INFINITY));
+    expectNaN($opt$Mul(0F, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Mul(Float.NaN, 11F));
+    expectNaN($opt$Mul(Float.NaN, -11F));
+    expectNaN($opt$Mul(Float.NaN, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Mul(Float.NaN, Float.POSITIVE_INFINITY));
+
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(2F, 3.40282346638528860e+38F));
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(2F, Float.POSITIVE_INFINITY));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(-2F, Float.POSITIVE_INFINITY));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(-2F, 3.40282346638528860e+38F));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(2F, Float.NEGATIVE_INFINITY));
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(-2F, Float.NEGATIVE_INFINITY));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+  }
+
+  private static void mulDouble() {
+    expectApproxEquals(15D, $opt$Mul(5D, 3D), 0.0001D);
+    expectApproxEquals(0D, $opt$Mul(0D, 0D), 0.0001D);
+    expectApproxEquals(0D, $opt$Mul(0D, 3D), 0.0001D);
+    expectApproxEquals(0D, $opt$Mul(3D, 0D), 0.0001D);
+    expectApproxEquals(-3D, $opt$Mul(1D, -3D), 0.0001D);
+    expectApproxEquals(36D, $opt$Mul(-12D, -3D), 0.0001D);
+    expectApproxEquals(33D, $opt$Mul(1D, 3D) * 11D, 0.0001D);
+    expectApproxEquals(0.02D, 0.1D * 0.2D, 0.0001D);
+    expectApproxEquals(-0.1D, -0.5D * 0.2D, 0.0001D);
+
+    expectNaN($opt$Mul(0D, Double.POSITIVE_INFINITY));
+    expectNaN($opt$Mul(0D, Double.NEGATIVE_INFINITY));
+    expectNaN($opt$Mul(Double.NaN, 11D));
+    expectNaN($opt$Mul(Double.NaN, -11D));
+    expectNaN($opt$Mul(Double.NaN, Double.NEGATIVE_INFINITY));
+    expectNaN($opt$Mul(Double.NaN, Double.POSITIVE_INFINITY));
+
+    expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(2D, 1.79769313486231570e+308));
+    expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(2D, Double.POSITIVE_INFINITY));
+    expectEquals(Double.NEGATIVE_INFINITY, $opt$Mul(-2D, Double.POSITIVE_INFINITY));
+    expectEquals(Double.NEGATIVE_INFINITY, $opt$Mul(-2D, 1.79769313486231570e+308));
+    expectEquals(Double.NEGATIVE_INFINITY, $opt$Mul(2D, Double.NEGATIVE_INFINITY));
+    expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(-2D, Double.NEGATIVE_INFINITY));
+    expectEquals(Double.NEGATIVE_INFINITY, $opt$Mul(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+    expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+    expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
+  }
+
+  static int $opt$Mul(int a, int b) {
+    return a * b;
+  }
+
+  static long $opt$Mul(long a, long b) {
+    return a * b;
+  }
+
+  static float $opt$Mul(float a, float b) {
+    return a * b;
+  }
+
+  static double $opt$Mul(double a, double b) {
+    return a * b;
+  }
+}
diff --git a/test/412-new-array/expected.txt b/test/412-new-array/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/412-new-array/expected.txt
diff --git a/test/412-new-array/info.txt b/test/412-new-array/info.txt
new file mode 100644
index 0000000..cb388b6
--- /dev/null
+++ b/test/412-new-array/info.txt
@@ -0,0 +1 @@
+Simple tests for new-array, filled-new-array and fill-array-data.
diff --git a/test/412-new-array/smali/fill_array_data.smali b/test/412-new-array/smali/fill_array_data.smali
new file mode 100644
index 0000000..34776db
--- /dev/null
+++ b/test/412-new-array/smali/fill_array_data.smali
@@ -0,0 +1,81 @@
+.class public LFillArrayData;
+
+.super Ljava/lang/Object;
+
+.method public static intArray([I)V
+   .registers 1
+
+   fill-array-data v0, :ArrayData
+   return-void
+
+:ArrayData
+    .array-data 4
+        1 2 3 4 5
+    .end array-data
+
+.end method
+
+.method public static shortArray([S)V
+   .registers 1
+
+   fill-array-data v0, :ArrayData
+   return-void
+
+:ArrayData
+    .array-data 2
+        1 2 3 4 5
+    .end array-data
+
+.end method
+
+.method public static charArray([C)V
+   .registers 1
+
+   fill-array-data v0, :ArrayData
+   return-void
+
+:ArrayData
+    .array-data 2
+        1 2 3 4 5
+    .end array-data
+
+.end method
+
+.method public static byteArray([B)V
+   .registers 1
+
+   fill-array-data v0, :ArrayData
+   return-void
+
+:ArrayData
+    .array-data 1
+        1 2 3 4 5
+    .end array-data
+
+.end method
+
+.method public static booleanArray([Z)V
+   .registers 1
+
+   fill-array-data v0, :ArrayData
+   return-void
+
+:ArrayData
+    .array-data 1
+        0 1 1
+    .end array-data
+
+.end method
+
+.method public static longArray([J)V
+   .registers 1
+
+   fill-array-data v0, :ArrayData
+   return-void
+
+:ArrayData
+    .array-data 8
+        1 2 3 4 5
+    .end array-data
+
+.end method
diff --git a/test/412-new-array/smali/filled_new_array.smali b/test/412-new-array/smali/filled_new_array.smali
new file mode 100644
index 0000000..ed8683a
--- /dev/null
+++ b/test/412-new-array/smali/filled_new_array.smali
@@ -0,0 +1,45 @@
+.class public LFilledNewArray;
+
+.super Ljava/lang/Object;
+
+.method public static newInt(III)[I
+   .registers 4
+   filled-new-array {v1, v2, v3}, [I
+   move-result-object v0
+   return-object v0
+.end method
+
+.method public static newRef(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;
+   .registers 3
+   filled-new-array {v1, v2}, [Ljava/lang/Object;
+   move-result-object v0
+   return-object v0
+.end method
+
+.method public static newArray([I[I)[[I
+   .registers 3
+   filled-new-array {v1, v2}, [[I
+   move-result-object v0
+   return-object v0
+.end method
+
+.method public static newIntRange(III)[I
+   .registers 4
+   filled-new-array/range {v1 .. v3}, [I
+   move-result-object v0
+   return-object v0
+.end method
+
+.method public static newRefRange(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;
+   .registers 3
+   filled-new-array/range {v1 .. v2}, [Ljava/lang/Object;
+   move-result-object v0
+   return-object v0
+.end method
+
+.method public static newArrayRange([I[I)[[I
+   .registers 3
+   filled-new-array/range {v1 .. v2}, [[I
+   move-result-object v0
+   return-object v0
+.end method
diff --git a/test/412-new-array/smali/filled_new_array_verify_error.smali b/test/412-new-array/smali/filled_new_array_verify_error.smali
new file mode 100644
index 0000000..b1470ec
--- /dev/null
+++ b/test/412-new-array/smali/filled_new_array_verify_error.smali
@@ -0,0 +1,10 @@
+.class public LFilledNewArrayVerifyError;
+
+.super Ljava/lang/Object;
+
+.method public static newRef(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;
+   .registers 3
+   filled-new-array {v1, v2}, [Ljava/lang/Integer;
+   move-result-object v0
+   return-object v0
+.end method
diff --git a/test/412-new-array/src/Main.java b/test/412-new-array/src/Main.java
new file mode 100644
index 0000000..168420c
--- /dev/null
+++ b/test/412-new-array/src/Main.java
@@ -0,0 +1,438 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+// Note that $opt$ is a marker for the optimizing compiler to ensure
+// it does compile the method.
+
+public class Main extends TestCase {
+  public static void main(String[] args) throws Exception {
+    $opt$TestAllocations();
+    $opt$TestWithInitializations();
+    $opt$TestNegativeValueNewByteArray();
+    $opt$TestNegativeValueNewCharArray();
+    testSmaliFilledNewArray();
+    testSmaliFillArrayData();
+    testSmaliVerifyError();
+  }
+
+  static void $opt$TestAllocations() {
+    float[] a = new float[1];
+    assertEquals(1, a.length);
+
+    double[] b = new double[2];
+    assertEquals(2, b.length);
+
+    long[] c = new long[3];
+    assertEquals(3, c.length);
+
+    int[] d = new int[4];
+    assertEquals(4, d.length);
+
+    short[] e = new short[5];
+    assertEquals(5, e.length);
+
+    char[] f = new char[6];
+    assertEquals(6, f.length);
+
+    byte[] g = new byte[7];
+    assertEquals(7, g.length);
+
+    boolean[] h = new boolean[8];
+    assertEquals(8, h.length);
+
+    Object[] i = new Object[9];
+    assertEquals(9, i.length);
+  }
+
+  static void $opt$TestWithInitializations() {
+    float[] a = { 1.2f };
+    assertEquals(1, a.length);
+    assertEquals(1.2f, a[0]);
+
+    double[] b = { 4.3, 1.2 };
+    assertEquals(2, b.length);
+    assertEquals(4.3, b[0]);
+    assertEquals(1.2, b[1]);
+
+    long[] c = { 4L, 5L };
+    assertEquals(2, c.length);
+    assertEquals(4L, c[0]);
+    assertEquals(5L, c[1]);
+
+    int[] d = {1, 2, 3};
+    assertEquals(3, d.length);
+    assertEquals(1, d[0]);
+    assertEquals(2, d[1]);
+    assertEquals(3, d[2]);
+
+    short[] e = {4, 5, 6};
+    assertEquals(3, e.length);
+    assertEquals(4, e[0]);
+    assertEquals(5, e[1]);
+    assertEquals(6, e[2]);
+
+    char[] f = {'a', 'b'};
+    assertEquals(2, f.length);
+    assertEquals('a', f[0]);
+    assertEquals('b', f[1]);
+
+    byte[] g = {7, 8, 9};
+    assertEquals(3, g.length);
+    assertEquals(7, g[0]);
+    assertEquals(8, g[1]);
+    assertEquals(9, g[2]);
+
+    boolean[] h = {true, false};
+    assertEquals(2, h.length);
+    assertEquals(true, h[0]);
+    assertEquals(false, h[1]);
+
+    Object obj1 = new Object();
+    Object obj2 = new Object();
+    Object[] i = {obj1, obj2};
+    assertEquals(2, i.length);
+    assertEquals(obj1, i[0]);
+    assertEquals(obj2, i[1]);
+  }
+
+  static void $opt$TestNegativeValueNewByteArray() {
+    // Use an array initializer to hint the use of filled-new-array.
+    byte[] a = { (byte)0xa0, (byte)0xa1, (byte)0xa2, (byte)0xa3,
+                 (byte)0xa4, (byte)0xa5, (byte)0xa6, (byte)0xa7 };
+    for (int i = 0; i < a.length; i++) {
+      assertEquals((byte)0xa0 + i, a[i]);
+    }
+  }
+
+  static void $opt$TestNegativeValueNewCharArray() {
+    // Use an array initializer to hint the use of filled-new-array.
+    char[] a = { (char)0xa000, (char)0xa001, (char)0xa002, (char)0xa003,
+                 (char)0xa004, (char)0xa005, (char)0xa006, (char)0xa007 };
+    for (int i = 0; i < a.length; i++) {
+      assertEquals((char)0xa000 + i, a[i]);
+    }
+  }
+
+  public static void testSmaliFilledNewArray() throws Exception {
+    Class<?> c = Class.forName("FilledNewArray");
+
+    {
+      Method m = c.getMethod("newInt", Integer.TYPE, Integer.TYPE, Integer.TYPE);
+      Object[] args = {new Integer(1), new Integer(2), new Integer(3)};
+      int[] result = (int[])m.invoke(null, args);
+      assertEquals(3, result.length);
+      assertEquals(1, result[0]);
+      assertEquals(2, result[1]);
+      assertEquals(3, result[2]);
+    }
+
+    {
+      Method m = c.getMethod("newRef", Object.class, Object.class);
+      Object[] args = {new Integer(1), new Integer(2)};
+      Object[] result = (Object[])m.invoke(null, args);
+      assertEquals(2, result.length);
+      assertEquals(args[0], result[0]);
+      assertEquals(args[1], result[1]);
+    }
+
+    {
+      Method m = c.getMethod("newArray", int[].class, int[].class);
+      Object[] args = {new int[0], new int[1]};
+      Object[] result = (Object[])m.invoke(null, args);
+      assertEquals(2, result.length);
+      assertEquals(args[0], result[0]);
+      assertEquals(args[1], result[1]);
+    }
+
+    {
+      Method m = c.getMethod("newIntRange", Integer.TYPE, Integer.TYPE, Integer.TYPE);
+      Object[] args = {new Integer(1), new Integer(2), new Integer(3)};
+      int[] result = (int[])m.invoke(null, args);
+      assertEquals(3, result.length);
+      assertEquals(1, result[0]);
+      assertEquals(2, result[1]);
+      assertEquals(3, result[2]);
+    }
+
+    {
+      Method m = c.getMethod("newRefRange", Object.class, Object.class);
+      Object[] args = {new Integer(1), new Integer(2)};
+      Object[] result = (Object[])m.invoke(null, args);
+      assertEquals(2, result.length);
+      assertEquals(args[0], result[0]);
+      assertEquals(args[1], result[1]);
+    }
+
+    {
+      Method m = c.getMethod("newArrayRange", int[].class, int[].class);
+      Object[] args = {new int[0], new int[1]};
+      Object[] result = (Object[])m.invoke(null, args);
+      assertEquals(2, result.length);
+      assertEquals(args[0], result[0]);
+      assertEquals(args[1], result[1]);
+    }
+  }
+
+  public static void testSmaliVerifyError() throws Exception {
+    Error error = null;
+    // Ensure the elements in filled-new-array must be assignable
+    // to the array component type.
+    try {
+      Class.forName("FilledNewArrayVerifyError");
+    } catch (VerifyError e) {
+      error = e;
+    }
+    assertNotNull(error);
+  }
+
+  public static void testSmaliFillArrayData() throws Exception {
+    Class<?> c = Class.forName("FillArrayData");
+    {
+      Method m = c.getMethod("intArray", int[].class);
+      int[] array = new int[7];
+      Object[] args = { array };
+      m.invoke(null, args);
+      assertEquals(7, array.length);
+      assertEquals(1, array[0]);
+      assertEquals(2, array[1]);
+      assertEquals(3, array[2]);
+      assertEquals(4, array[3]);
+      assertEquals(5, array[4]);
+      assertEquals(0, array[5]);
+      assertEquals(0, array[6]);
+
+      array = new int[2];
+      args[0] = array;
+      Throwable exception  = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof IndexOutOfBoundsException);
+      }
+      assertNotNull(exception);
+      exception = null;
+      // Test that nothing has been written to the array.
+      assertEquals(0, array[0]);
+      assertEquals(0, array[1]);
+
+      args[0] = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof NullPointerException);
+      }
+      assertNotNull(exception);
+    }
+
+    {
+      Method m = c.getMethod("shortArray", short[].class);
+      short[] array = new short[7];
+      Object[] args = { array };
+      m.invoke(null, args);
+      assertEquals(7, array.length);
+      assertEquals(1, array[0]);
+      assertEquals(2, array[1]);
+      assertEquals(3, array[2]);
+      assertEquals(4, array[3]);
+      assertEquals(5, array[4]);
+      assertEquals(0, array[5]);
+      assertEquals(0, array[6]);
+
+      array = new short[2];
+      args[0] = array;
+      Throwable exception  = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof IndexOutOfBoundsException);
+      }
+      assertNotNull(exception);
+      exception = null;
+      // Test that nothing has been written to the array.
+      assertEquals(0, array[0]);
+      assertEquals(0, array[1]);
+
+      args[0] = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof NullPointerException);
+      }
+      assertNotNull(exception);
+    }
+
+    {
+      Method m = c.getMethod("longArray", long[].class);
+      long[] array = new long[7];
+      Object[] args = { array };
+      m.invoke(null, args);
+      assertEquals(7, array.length);
+      assertEquals(1L, array[0]);
+      assertEquals(2L, array[1]);
+      assertEquals(3L, array[2]);
+      assertEquals(4L, array[3]);
+      assertEquals(5L, array[4]);
+      assertEquals(0L, array[5]);
+      assertEquals(0L, array[6]);
+
+      array = new long[2];
+      args[0] = array;
+      Throwable exception  = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof IndexOutOfBoundsException);
+      }
+      assertNotNull(exception);
+      exception = null;
+      // Test that nothing has been written to the array.
+      assertEquals(0, array[0]);
+      assertEquals(0, array[1]);
+
+      args[0] = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof NullPointerException);
+      }
+      assertNotNull(exception);
+    }
+
+    {
+      Method m = c.getMethod("charArray", char[].class);
+      char[] array = new char[7];
+      Object[] args = { array };
+      m.invoke(null, args);
+      assertEquals(7, array.length);
+      assertEquals(1, array[0]);
+      assertEquals(2, array[1]);
+      assertEquals(3, array[2]);
+      assertEquals(4, array[3]);
+      assertEquals(5, array[4]);
+      assertEquals(0, array[5]);
+      assertEquals(0, array[6]);
+
+      array = new char[2];
+      args[0] = array;
+      Throwable exception  = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof IndexOutOfBoundsException);
+      }
+      assertNotNull(exception);
+      exception = null;
+      // Test that nothing has been written to the array.
+      assertEquals(0, array[0]);
+      assertEquals(0, array[1]);
+
+      args[0] = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof NullPointerException);
+      }
+      assertNotNull(exception);
+    }
+
+    {
+      Method m = c.getMethod("byteArray", byte[].class);
+      byte[] array = new byte[7];
+      Object[] args = { array };
+      m.invoke(null, args);
+      assertEquals(7, array.length);
+      assertEquals(1, array[0]);
+      assertEquals(2, array[1]);
+      assertEquals(3, array[2]);
+      assertEquals(4, array[3]);
+      assertEquals(5, array[4]);
+      assertEquals(0, array[5]);
+      assertEquals(0, array[6]);
+
+      array = new byte[2];
+      args[0] = array;
+      Throwable exception  = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof IndexOutOfBoundsException);
+      }
+      assertNotNull(exception);
+      exception = null;
+      // Test that nothing has been written to the array.
+      assertEquals(0, array[0]);
+      assertEquals(0, array[1]);
+
+      args[0] = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof NullPointerException);
+      }
+      assertNotNull(exception);
+    }
+
+    {
+      Method m = c.getMethod("booleanArray", boolean[].class);
+      boolean[] array = new boolean[5];
+      Object[] args = { array };
+      m.invoke(null, args);
+      assertEquals(5, array.length);
+      assertEquals(false, array[0]);
+      assertEquals(true, array[1]);
+      assertEquals(true, array[2]);
+      assertEquals(false, array[3]);
+      assertEquals(false, array[4]);
+
+      array = new boolean[2];
+      args[0] = array;
+      Throwable exception  = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof IndexOutOfBoundsException);
+      }
+      assertNotNull(exception);
+      exception = null;
+      // Test that nothing has been written to the array.
+      assertEquals(false, array[0]);
+      assertEquals(false, array[1]);
+
+      args[0] = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof NullPointerException);
+      }
+      assertNotNull(exception);
+    }
+  }
+}
diff --git a/test/412-new-array/src/TestCase.java b/test/412-new-array/src/TestCase.java
new file mode 100644
index 0000000..ef77f71
--- /dev/null
+++ b/test/412-new-array/src/TestCase.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+/**
+ * Common superclass for test cases.
+ */
+
+import java.util.Arrays;
+
+public abstract class TestCase {
+  public static void assertSame(Object expected, Object value) {
+    if (expected != value) {
+      throw new AssertionError("Objects are not the same: expected " +
+          String.valueOf(expected) + ", got " + String.valueOf(value));
+    }
+  }
+
+  public static void assertNotSame(Object expected, Object value) {
+    if (expected == value) {
+      throw new AssertionError(
+          "Objects are the same: " + String.valueOf(expected));
+    }
+  }
+
+  public static void assertEquals(String message, int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertTrue(String message, boolean condition) {
+    if (!condition) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertTrue(boolean condition) {
+    assertTrue("Expected true", condition);
+  }
+
+  public static void assertFalse(String message, boolean condition) {
+    if (condition) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertFalse(boolean condition) {
+    assertFalse("Expected false", condition);
+  }
+
+  public static void assertEquals(Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+      throw new AssertionError(msg);
+    }
+  }
+
+  public static void assertNotEquals(int expected, int actual) {
+    if (expected == actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertNotEquals(Object expected, Object actual) {
+    if (expected.equals(actual)) {
+      String msg = "Objects are the same: " + String.valueOf(expected);
+      throw new AssertionError(msg);
+    }
+  }
+
+  public static <T> void assertArrayEquals(T[] actual, T... expected) {
+      assertTrue(Arrays.equals(expected, actual));
+  }
+
+  public static void assertEquals(
+      String message, Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(
+      String message, long expected, long actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(long expected, long actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(
+      String message, boolean expected, boolean actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(boolean expected, boolean actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(
+      String message, float expected, float actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(float expected, float actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(float expected, float actual,
+                                  float tolerance) {
+    if ((actual < expected - tolerance) || (expected + tolerance < actual)) {
+      throw new AssertionError("Expected " + expected + " got " + actual +
+          " tolerance " + tolerance);
+    }
+  }
+
+  public static void assertEquals(
+      String message, double expected, double actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(double expected, double actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(double expected, double actual,
+                                  double tolerance) {
+    if ((actual < expected - tolerance) || (expected + tolerance < actual)) {
+      throw new AssertionError("Expected " + expected + " got " + actual +
+          " tolerance " + tolerance);
+    }
+  }
+
+  public static void assertSame(
+      String message, Object expected, Object actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertNull(String message, Object object) {
+    if (object != null) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertNull(Object object) {
+    assertNull("Expected null", object);
+  }
+
+  public static void assertNotNull(String message, Object object) {
+    if (object == null) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertNotNull(Object object) {
+    assertNotNull("Expected non-null", object);
+  }
+
+  public static void fail(String msg) {
+    throw new AssertionError(msg);
+  }
+}
diff --git a/test/413-regalloc-regression/expected.txt b/test/413-regalloc-regression/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/413-regalloc-regression/expected.txt
diff --git a/test/413-regalloc-regression/info.txt b/test/413-regalloc-regression/info.txt
new file mode 100644
index 0000000..c706c1d
--- /dev/null
+++ b/test/413-regalloc-regression/info.txt
@@ -0,0 +1,2 @@
+Regression test for the linear scan register allocator, that use to
+fail compiling removeElementAt in x86.
diff --git a/test/413-regalloc-regression/src/Main.java b/test/413-regalloc-regression/src/Main.java
new file mode 100644
index 0000000..3e649f8
--- /dev/null
+++ b/test/413-regalloc-regression/src/Main.java
@@ -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.
+ */
+
+public class Main {
+  private Object[] data;
+  private int size;
+
+  public Main() {
+    data = new Object[4];
+    size = 0;
+  }
+
+  public void removeElementAt(int index) {
+    for (int i = index; i < size - 1; i++) {
+      data[i] = data[i + 1];
+    }
+    data[--size] = null;
+  }
+
+  public static void main(String[] args) {
+    Main main = new Main();
+    main.size++;
+    main.removeElementAt(0);
+    if (main.size != 0) {
+      throw new Error("Unexpected size");
+    }
+  }
+}
diff --git a/test/414-optimizing-arith-sub/expected.txt b/test/414-optimizing-arith-sub/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/414-optimizing-arith-sub/expected.txt
diff --git a/test/414-optimizing-arith-sub/info.txt b/test/414-optimizing-arith-sub/info.txt
new file mode 100644
index 0000000..1eaa148
--- /dev/null
+++ b/test/414-optimizing-arith-sub/info.txt
@@ -0,0 +1 @@
+Subtraction tests.
diff --git a/test/414-optimizing-arith-sub/src/Main.java b/test/414-optimizing-arith-sub/src/Main.java
new file mode 100644
index 0000000..30e8436
--- /dev/null
+++ b/test/414-optimizing-arith-sub/src/Main.java
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+// Note that $opt$ is a marker for the optimizing compiler to ensure
+// it does compile the method.
+public class Main {
+
+  public static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(float expected, float result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(double expected, double result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectApproxEquals(float a, float b) {
+    float maxDelta = 0.0001F;
+    boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta);
+    if (!aproxEquals) {
+      throw new Error("Expected: " + a + ", found: " + b + ", with delta: " + maxDelta + " " + (a - b));
+    }
+  }
+
+  public static void expectApproxEquals(double a, double b) {
+    double maxDelta = 0.00001D;
+    boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta);
+    if (!aproxEquals) {
+      throw new Error("Expected: " + a + ", found: " + b + ", with delta: " + maxDelta + " " + (a - b));
+    }
+  }
+
+  public static void expectNaN(float a) {
+    if (a == a) {
+      throw new Error("Expected NaN: " + a);
+    }
+  }
+
+  public static void expectNaN(double a) {
+    if (a == a) {
+      throw new Error("Expected NaN: " + a);
+    }
+  }
+
+  public static void main(String[] args) {
+    subInt();
+    subLong();
+    subFloat();
+    subDouble();
+  }
+
+  private static void subInt() {
+    expectEquals(2, $opt$Sub(5, 3));
+    expectEquals(0, $opt$Sub(0, 0));
+    expectEquals(-3, $opt$Sub(0, 3));
+    expectEquals(3, $opt$Sub(3, 0));
+    expectEquals(4, $opt$Sub(1, -3));
+    expectEquals(-9, $opt$Sub(-12, -3));
+    expectEquals(134217724, $opt$Sub(134217729, 5)); // (2^27 + 1) - 5
+  }
+
+  private static void subLong() {
+    expectEquals(2L, $opt$Sub(5L, 3L));
+    expectEquals(0L, $opt$Sub(0L, 0L));
+    expectEquals(-3L, $opt$Sub(0L, 3L));
+    expectEquals(3L, $opt$Sub(3L, 0L));
+    expectEquals(4L, $opt$Sub(1L, -3L));
+    expectEquals(-9L, $opt$Sub(-12L, -3L));
+    expectEquals(134217724L, $opt$Sub(134217729L, 5L)); // (2^27 + 1) - 5
+    expectEquals(34359738362L, $opt$Sub(34359738369L, 7L)); // (2^35 + 1) - 7
+  }
+
+  private static void subFloat() {
+    expectApproxEquals(2F, $opt$Sub(5F, 3F));
+    expectApproxEquals(0F, $opt$Sub(0F, 0F));
+    expectApproxEquals(-3F, $opt$Sub(0F, 3F));
+    expectApproxEquals(3F, $opt$Sub(3F, 0F));
+    expectApproxEquals(4F, $opt$Sub(1F, -3F));
+    expectApproxEquals(-9F, $opt$Sub(-12F, -3F));
+    expectApproxEquals(34359738362F, $opt$Sub(34359738369F, 7F)); // (2^35 + 1) - 7
+    expectApproxEquals(-0.1F, $opt$Sub(0.1F, 0.2F));
+    expectApproxEquals(0.2F, $opt$Sub(-0.5F, -0.7F));
+
+    expectNaN($opt$Sub(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Sub(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+    expectNaN($opt$Sub(Float.NaN, 11F));
+    expectNaN($opt$Sub(Float.NaN, -11F));
+    expectNaN($opt$Sub(Float.NaN, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Sub(Float.NaN, Float.POSITIVE_INFINITY));
+
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Sub(-Float.MAX_VALUE, Float.MAX_VALUE));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Sub(2F, Float.POSITIVE_INFINITY));
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Sub(Float.MAX_VALUE, -Float.MAX_VALUE));
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Sub(2F, Float.NEGATIVE_INFINITY));
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Sub(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Sub(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY));
+  }
+
+  private static void subDouble() {
+    expectApproxEquals(2D, $opt$Sub(5D, 3D));
+    expectApproxEquals(0D, $opt$Sub(0D, 0D));
+    expectApproxEquals(-3D, $opt$Sub(0D, 3D));
+    expectApproxEquals(3D, $opt$Sub(3D, 0D));
+    expectApproxEquals(4D, $opt$Sub(1D, -3D));
+    expectApproxEquals(-9D, $opt$Sub(-12D, -3D));
+    expectApproxEquals(134217724D, $opt$Sub(134217729D, 5D)); // (2^27 + 1) - 5
+    expectApproxEquals(34359738362D, $opt$Sub(34359738369D, 7D)); // (2^35 + 1) - 7
+    expectApproxEquals(-0.1D, $opt$Sub(0.1D, 0.2D));
+    expectApproxEquals(0.2D, $opt$Sub(-0.5D, -0.7D));
+
+    expectNaN($opt$Sub(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
+    expectNaN($opt$Sub(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+    expectNaN($opt$Sub(Double.NaN, 11D));
+    expectNaN($opt$Sub(Double.NaN, -11D));
+    expectNaN($opt$Sub(Double.NaN, Double.NEGATIVE_INFINITY));
+    expectNaN($opt$Sub(Double.NaN, Double.POSITIVE_INFINITY));
+
+    expectEquals(Double.NEGATIVE_INFINITY, $opt$Sub(-Double.MAX_VALUE, Double.MAX_VALUE));
+    expectEquals(Double.NEGATIVE_INFINITY, $opt$Sub(2D, Double.POSITIVE_INFINITY));
+    expectEquals(Double.POSITIVE_INFINITY, $opt$Sub(Double.MAX_VALUE, -Double.MAX_VALUE));
+    expectEquals(Double.POSITIVE_INFINITY, $opt$Sub(2D, Double.NEGATIVE_INFINITY));
+    expectEquals(Double.POSITIVE_INFINITY, $opt$Sub(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+    expectEquals(Double.NEGATIVE_INFINITY, $opt$Sub(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
+  }
+
+  static int $opt$Sub(int a, int b) {
+    return a - b;
+  }
+
+  static long $opt$Sub(long a, long b) {
+    return a - b;
+  }
+
+  static float $opt$Sub(float a, float b) {
+    return a - b;
+  }
+
+  static double $opt$Sub(double a, double b) {
+    return a - b;
+  }
+
+}
diff --git a/test/414-static-fields/expected.txt b/test/414-static-fields/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/414-static-fields/expected.txt
diff --git a/test/414-static-fields/info.txt b/test/414-static-fields/info.txt
new file mode 100644
index 0000000..1cdd3c7
--- /dev/null
+++ b/test/414-static-fields/info.txt
@@ -0,0 +1 @@
+Simple test for static field access.
diff --git a/test/414-static-fields/src/Main.java b/test/414-static-fields/src/Main.java
new file mode 100644
index 0000000..3403772
--- /dev/null
+++ b/test/414-static-fields/src/Main.java
@@ -0,0 +1,98 @@
+/*
+ * 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 extends TestCase {
+  public static void main(String[] args) {
+    $opt$TestThisClassStaticField();
+    $opt$TestOtherClassStaticField();
+    $opt$TestAddThisClassStaticField();
+    $opt$TestAddOtherClassStaticField();
+    $opt$TestOtherClassWithClinitStaticField();
+    $opt$TestAccess();
+  }
+
+  static int staticField = 42;
+
+  static int getInt() {
+    return 33;
+  }
+
+  static void $opt$TestThisClassStaticField() {
+    assertEquals(42, staticField);
+  }
+
+  static void $opt$TestOtherClassStaticField() {
+    assertEquals(41, Other.staticField);
+  }
+
+  static void $opt$TestAddThisClassStaticField() {
+    int a = getInt();
+    assertEquals(a + 42, a + staticField);
+  }
+
+  static void $opt$TestAddOtherClassStaticField() {
+    int a = getInt();
+    assertEquals(a + 41, a + Other.staticField);
+  }
+
+  static void $opt$TestOtherClassWithClinitStaticField() {
+    assertEquals(40, OtherWithClinit.staticField);
+  }
+
+  static void $opt$TestAccess() {
+    assertEquals(false, sZ);
+    assertEquals(0, sB);
+    assertEquals(0, sC);
+    assertEquals(0, sI);
+    assertEquals(0, sJ);
+    assertEquals(0, sS);
+    assertEquals(0.0f, sF);
+    assertEquals(0.0, sD);
+    assertNull(sObject);
+
+    long longValue = -1122198787987987987L;
+    Object o = new Object();
+    sZ = true;
+    sB = -2;
+    sC = 'c';
+    sI = 42;
+    sJ = longValue;
+    sS = 68;
+    sObject = o;
+    sF = 2.3f;
+    sD = 5.3;
+
+    assertEquals(true, sZ);
+    assertEquals(-2, sB);
+    assertEquals('c', sC);
+    assertEquals(42, sI);
+    assertEquals(longValue, sJ);
+    assertEquals(68, sS);
+    assertEquals(o, sObject);
+    assertEquals(2.3f, sF);
+    assertEquals(5.3, sD);
+  }
+
+  static boolean sZ;
+  static byte sB;
+  static char sC;
+  static double sD;
+  static float sF;
+  static int sI;
+  static long sJ;
+  static short sS;
+  static Object sObject;
+}
diff --git a/runtime/log_severity.h b/test/414-static-fields/src/Other.java
similarity index 62%
copy from runtime/log_severity.h
copy to test/414-static-fields/src/Other.java
index 31682df..aede930 100644
--- a/runtime/log_severity.h
+++ b/test/414-static-fields/src/Other.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
-
-typedef int LogSeverity;
-
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
-
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+public class Other {
+  static int staticField = 41;
+}
diff --git a/runtime/log_severity.h b/test/414-static-fields/src/OtherWithClinit.java
similarity index 62%
copy from runtime/log_severity.h
copy to test/414-static-fields/src/OtherWithClinit.java
index 31682df..eafbd2b 100644
--- a/runtime/log_severity.h
+++ b/test/414-static-fields/src/OtherWithClinit.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_LOG_SEVERITY_H_
-#define ART_RUNTIME_LOG_SEVERITY_H_
+public class OtherWithClinit {
+  static int staticField = 39;
 
-typedef int LogSeverity;
-
-const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5;
-const int INTERNAL_FATAL = 6;  // For Runtime::Abort.
-
-#endif  // ART_RUNTIME_LOG_SEVERITY_H_
+  static {
+    staticField = 40;
+  }
+}
diff --git a/test/414-static-fields/src/TestCase.java b/test/414-static-fields/src/TestCase.java
new file mode 100644
index 0000000..ef77f71
--- /dev/null
+++ b/test/414-static-fields/src/TestCase.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+/**
+ * Common superclass for test cases.
+ */
+
+import java.util.Arrays;
+
+public abstract class TestCase {
+  public static void assertSame(Object expected, Object value) {
+    if (expected != value) {
+      throw new AssertionError("Objects are not the same: expected " +
+          String.valueOf(expected) + ", got " + String.valueOf(value));
+    }
+  }
+
+  public static void assertNotSame(Object expected, Object value) {
+    if (expected == value) {
+      throw new AssertionError(
+          "Objects are the same: " + String.valueOf(expected));
+    }
+  }
+
+  public static void assertEquals(String message, int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertTrue(String message, boolean condition) {
+    if (!condition) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertTrue(boolean condition) {
+    assertTrue("Expected true", condition);
+  }
+
+  public static void assertFalse(String message, boolean condition) {
+    if (condition) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertFalse(boolean condition) {
+    assertFalse("Expected false", condition);
+  }
+
+  public static void assertEquals(Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+      throw new AssertionError(msg);
+    }
+  }
+
+  public static void assertNotEquals(int expected, int actual) {
+    if (expected == actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertNotEquals(Object expected, Object actual) {
+    if (expected.equals(actual)) {
+      String msg = "Objects are the same: " + String.valueOf(expected);
+      throw new AssertionError(msg);
+    }
+  }
+
+  public static <T> void assertArrayEquals(T[] actual, T... expected) {
+      assertTrue(Arrays.equals(expected, actual));
+  }
+
+  public static void assertEquals(
+      String message, Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(
+      String message, long expected, long actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(long expected, long actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(
+      String message, boolean expected, boolean actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(boolean expected, boolean actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(
+      String message, float expected, float actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(float expected, float actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(float expected, float actual,
+                                  float tolerance) {
+    if ((actual < expected - tolerance) || (expected + tolerance < actual)) {
+      throw new AssertionError("Expected " + expected + " got " + actual +
+          " tolerance " + tolerance);
+    }
+  }
+
+  public static void assertEquals(
+      String message, double expected, double actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(double expected, double actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(double expected, double actual,
+                                  double tolerance) {
+    if ((actual < expected - tolerance) || (expected + tolerance < actual)) {
+      throw new AssertionError("Expected " + expected + " got " + actual +
+          " tolerance " + tolerance);
+    }
+  }
+
+  public static void assertSame(
+      String message, Object expected, Object actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertNull(String message, Object object) {
+    if (object != null) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertNull(Object object) {
+    assertNull("Expected null", object);
+  }
+
+  public static void assertNotNull(String message, Object object) {
+    if (object == null) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertNotNull(Object object) {
+    assertNotNull("Expected non-null", object);
+  }
+
+  public static void fail(String msg) {
+    throw new AssertionError(msg);
+  }
+}
diff --git a/test/415-optimizing-arith-neg/expected.txt b/test/415-optimizing-arith-neg/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/415-optimizing-arith-neg/expected.txt
diff --git a/test/415-optimizing-arith-neg/info.txt b/test/415-optimizing-arith-neg/info.txt
new file mode 100644
index 0000000..8494aad
--- /dev/null
+++ b/test/415-optimizing-arith-neg/info.txt
@@ -0,0 +1 @@
+Tests for arithmetic negation operations.
diff --git a/test/415-optimizing-arith-neg/src/Main.java b/test/415-optimizing-arith-neg/src/Main.java
new file mode 100644
index 0000000..e2850ca
--- /dev/null
+++ b/test/415-optimizing-arith-neg/src/Main.java
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+// Note that $opt$ is a marker for the optimizing compiler to ensure
+// it does compile the method.
+public class Main {
+
+  public static void assertEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void assertEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void assertEquals(float expected, float result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void assertEquals(double expected, double result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void assertIsNaN(float result) {
+    if (!Float.isNaN(result)) {
+      throw new Error("Expected NaN: " + result);
+    }
+  }
+
+  public static void assertIsNaN(double result) {
+    if (!Double.isNaN(result)) {
+      throw new Error("Expected NaN: " + result);
+    }
+  }
+
+  public static void main(String[] args) {
+    negInt();
+    $opt$InplaceNegOneInt(1);
+
+    negLong();
+    $opt$InplaceNegOneLong(1L);
+
+    negFloat();
+    negDouble();
+  }
+
+  private static void negInt() {
+    assertEquals(-1, $opt$NegInt(1));
+    assertEquals(1, $opt$NegInt(-1));
+    assertEquals(0, $opt$NegInt(0));
+    assertEquals(51, $opt$NegInt(-51));
+    assertEquals(-51, $opt$NegInt(51));
+    assertEquals(2147483647, $opt$NegInt(-2147483647));  // (2^31 - 1)
+    assertEquals(-2147483647, $opt$NegInt(2147483647));  // -(2^31 - 1)
+    // From the Java 7 SE Edition specification:
+    // http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.15.4
+    //
+    //   For integer values, negation is the same as subtraction from
+    //   zero.  The Java programming language uses two's-complement
+    //   representation for integers, and the range of two's-complement
+    //   values is not symmetric, so negation of the maximum negative
+    //   int or long results in that same maximum negative number.
+    //   Overflow occurs in this case, but no exception is thrown.
+    //   For all integer values x, -x equals (~x)+1.''
+    assertEquals(-2147483648, $opt$NegInt(-2147483648)); // -(2^31)
+  }
+
+  private static void $opt$InplaceNegOneInt(int a) {
+    a = -a;
+    assertEquals(-1, a);
+  }
+
+  private static void negLong() {
+    assertEquals(-1L, $opt$NegLong(1L));
+    assertEquals(1L, $opt$NegLong(-1L));
+    assertEquals(0L, $opt$NegLong(0L));
+    assertEquals(51L, $opt$NegLong(-51L));
+    assertEquals(-51L, $opt$NegLong(51L));
+
+    assertEquals(2147483647L, $opt$NegLong(-2147483647L));  // (2^31 - 1)
+    assertEquals(-2147483647L, $opt$NegLong(2147483647L));  // -(2^31 - 1)
+    assertEquals(2147483648L, $opt$NegLong(-2147483648L));  // 2^31
+    assertEquals(-2147483648L, $opt$NegLong(2147483648L));  // -(2^31)
+
+    assertEquals(9223372036854775807L, $opt$NegLong(-9223372036854775807L));  // (2^63 - 1)
+    assertEquals(-9223372036854775807L, $opt$NegLong(9223372036854775807L));  // -(2^63 - 1)
+    // See remark regarding the negation of the maximum negative
+    // (long) value in negInt().
+    assertEquals(-9223372036854775808L, $opt$NegLong(-9223372036854775808L)); // -(2^63)
+  }
+
+  private static void $opt$InplaceNegOneLong(long a) {
+    a = -a;
+    assertEquals(-1L, a);
+  }
+
+  private static void negFloat() {
+     assertEquals(-1F, $opt$NegFloat(1F));
+     assertEquals(1F, $opt$NegFloat(-1F));
+     assertEquals(0F, $opt$NegFloat(0F));
+     assertEquals(51F, $opt$NegFloat(-51F));
+     assertEquals(-51F, $opt$NegFloat(51F));
+
+     assertEquals(-0.1F, $opt$NegFloat(0.1F));
+     assertEquals(0.1F, $opt$NegFloat(-0.1F));
+     assertEquals(343597.38362F, $opt$NegFloat(-343597.38362F));
+     assertEquals(-343597.38362F, $opt$NegFloat(343597.38362F));
+
+     assertEquals(-Float.MIN_NORMAL, $opt$NegFloat(Float.MIN_NORMAL));
+     assertEquals(Float.MIN_NORMAL, $opt$NegFloat(-Float.MIN_NORMAL));
+     assertEquals(-Float.MIN_VALUE, $opt$NegFloat(Float.MIN_VALUE));
+     assertEquals(Float.MIN_VALUE, $opt$NegFloat(-Float.MIN_VALUE));
+     assertEquals(-Float.MAX_VALUE, $opt$NegFloat(Float.MAX_VALUE));
+     assertEquals(Float.MAX_VALUE, $opt$NegFloat(-Float.MAX_VALUE));
+
+     assertEquals(Float.NEGATIVE_INFINITY, $opt$NegFloat(Float.POSITIVE_INFINITY));
+     assertEquals(Float.POSITIVE_INFINITY, $opt$NegFloat(Float.NEGATIVE_INFINITY));
+     assertIsNaN($opt$NegFloat(Float.NaN));
+  }
+
+  private static void negDouble() {
+     assertEquals(-1D, $opt$NegDouble(1D));
+     assertEquals(1D, $opt$NegDouble(-1D));
+     assertEquals(0D, $opt$NegDouble(0D));
+     assertEquals(51D, $opt$NegDouble(-51D));
+     assertEquals(-51D, $opt$NegDouble(51D));
+
+     assertEquals(-0.1D, $opt$NegDouble(0.1D));
+     assertEquals(0.1D, $opt$NegDouble(-0.1D));
+     assertEquals(343597.38362D, $opt$NegDouble(-343597.38362D));
+     assertEquals(-343597.38362D, $opt$NegDouble(343597.38362D));
+
+     assertEquals(-Double.MIN_NORMAL, $opt$NegDouble(Double.MIN_NORMAL));
+     assertEquals(Double.MIN_NORMAL, $opt$NegDouble(-Double.MIN_NORMAL));
+     assertEquals(-Double.MIN_VALUE, $opt$NegDouble(Double.MIN_VALUE));
+     assertEquals(Double.MIN_VALUE, $opt$NegDouble(-Double.MIN_VALUE));
+     assertEquals(-Double.MAX_VALUE, $opt$NegDouble(Double.MAX_VALUE));
+     assertEquals(Double.MAX_VALUE, $opt$NegDouble(-Double.MAX_VALUE));
+
+     assertEquals(Double.NEGATIVE_INFINITY, $opt$NegDouble(Double.POSITIVE_INFINITY));
+     assertEquals(Double.POSITIVE_INFINITY, $opt$NegDouble(Double.NEGATIVE_INFINITY));
+     assertIsNaN($opt$NegDouble(Double.NaN));
+  }
+
+  static int $opt$NegInt(int a){
+    return -a;
+  }
+
+  static long $opt$NegLong(long a){
+    return -a;
+  }
+
+  static float $opt$NegFloat(float a){
+    return -a;
+  }
+
+  static double $opt$NegDouble(double a){
+    return -a;
+  }
+}
diff --git a/test/416-optimizing-arith-not/expected.txt b/test/416-optimizing-arith-not/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/416-optimizing-arith-not/expected.txt
diff --git a/test/416-optimizing-arith-not/info.txt b/test/416-optimizing-arith-not/info.txt
new file mode 100644
index 0000000..e9418fd
--- /dev/null
+++ b/test/416-optimizing-arith-not/info.txt
@@ -0,0 +1 @@
+Tests for bitwise not operations.
diff --git a/test/416-optimizing-arith-not/smali/not.smali b/test/416-optimizing-arith-not/smali/not.smali
new file mode 100644
index 0000000..6dea3b6
--- /dev/null
+++ b/test/416-optimizing-arith-not/smali/not.smali
@@ -0,0 +1,15 @@
+.class public LTestNot;
+
+.super Ljava/lang/Object;
+
+.method public static $opt$NotInt(I)I
+   .registers 2
+   not-int v0, v1
+   return v0
+.end method
+
+.method public static $opt$NotLong(J)J
+   .registers 4
+   not-long v0, v2
+   return-wide v0
+.end method
diff --git a/test/416-optimizing-arith-not/src/Main.java b/test/416-optimizing-arith-not/src/Main.java
new file mode 100644
index 0000000..26e206c
--- /dev/null
+++ b/test/416-optimizing-arith-not/src/Main.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+
+  public static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    notInt();
+    notLong();
+  }
+
+  private static void notInt() throws Exception {
+    expectEquals(1, smaliNotInt(-2));
+    expectEquals(0, smaliNotInt(-1));
+    expectEquals(-1, smaliNotInt(0));
+    expectEquals(-2, smaliNotInt(1));
+    expectEquals(2147483647, smaliNotInt(-2147483648));  // (2^31) - 1
+    expectEquals(2147483646, smaliNotInt(-2147483647));  // (2^31) - 2
+    expectEquals(-2147483647, smaliNotInt(2147483646));  // -(2^31) - 1
+    expectEquals(-2147483648, smaliNotInt(2147483647));  // -(2^31)
+  }
+
+  private static void notLong() throws Exception {
+    expectEquals(1L, smaliNotLong(-2L));
+    expectEquals(0L, smaliNotLong(-1L));
+    expectEquals(-1L, smaliNotLong(0L));
+    expectEquals(-2L, smaliNotLong(1L));
+    expectEquals(2147483647L, smaliNotLong(-2147483648L));  // (2^31) - 1
+    expectEquals(2147483646L, smaliNotLong(-2147483647L));  // (2^31) - 2
+    expectEquals(-2147483647L, smaliNotLong(2147483646L));  // -(2^31) - 1
+    expectEquals(-2147483648L, smaliNotLong(2147483647L));  // -(2^31)
+    expectEquals(9223372036854775807L, smaliNotLong(-9223372036854775808L));  // (2^63) - 1
+    expectEquals(9223372036854775806L, smaliNotLong(-9223372036854775807L));  // (2^63) - 2
+    expectEquals(-9223372036854775807L, smaliNotLong(9223372036854775806L));  // -(2^63) - 1
+    expectEquals(-9223372036854775808L, smaliNotLong(9223372036854775807L));  // -(2^63)
+  }
+
+  // Wrappers around methods located in file not.smali.
+
+  private static int smaliNotInt(int a) throws Exception {
+    Class<?> c = Class.forName("TestNot");
+    Method m = c.getMethod("$opt$NotInt", int.class);
+    int result = (Integer)m.invoke(null, a);
+    return result;
+  }
+
+  private static long smaliNotLong(long a) throws Exception {
+    Class<?> c = Class.forName("TestNot");
+    Method m = c.getMethod("$opt$NotLong", long.class);
+    long result = (Long)m.invoke(null, a);
+    return result;
+  }
+}
diff --git a/test/417-optimizing-arith-div/expected.txt b/test/417-optimizing-arith-div/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/417-optimizing-arith-div/expected.txt
diff --git a/test/417-optimizing-arith-div/info.txt b/test/417-optimizing-arith-div/info.txt
new file mode 100644
index 0000000..1374b0f
--- /dev/null
+++ b/test/417-optimizing-arith-div/info.txt
@@ -0,0 +1 @@
+Tests for division operation.
diff --git a/test/417-optimizing-arith-div/src/Main.java b/test/417-optimizing-arith-div/src/Main.java
new file mode 100644
index 0000000..5825d24
--- /dev/null
+++ b/test/417-optimizing-arith-div/src/Main.java
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+// Note that $opt$ is a marker for the optimizing compiler to ensure
+// it does compile the method.
+public class Main {
+
+  public static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(float expected, float result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(double expected, double result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectApproxEquals(float a, float b) {
+    float maxDelta = 0.00001F;
+    boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta);
+    if (!aproxEquals) {
+      throw new Error("Expected: " + a + ", found: " + b
+          + ", with delta: " + maxDelta + " " + (a - b));
+    }
+  }
+
+  public static void expectApproxEquals(double a, double b) {
+    double maxDelta = 0.00001D;
+    boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta);
+    if (!aproxEquals) {
+      throw new Error("Expected: " + a + ", found: "
+          + b + ", with delta: " + maxDelta + " " + (a - b));
+    }
+  }
+
+  public static void expectNaN(float a) {
+    if (a == a) {
+      throw new Error("Expected NaN: " + a);
+    }
+  }
+
+  public static void expectNaN(double a) {
+    if (a == a) {
+      throw new Error("Expected NaN: " + a);
+    }
+  }
+
+  public static void expectDivisionByZero(int value) {
+    try {
+      $opt$Div(value, 0);
+      throw new Error("Expected RuntimeException when dividing by 0");
+    } catch (java.lang.RuntimeException e) {
+    }
+    try {
+      $opt$DivZero(value);
+      throw new Error("Expected RuntimeException when dividing by 0");
+    } catch (java.lang.RuntimeException e) {
+    }
+  }
+  public static void main(String[] args) {
+    div();
+  }
+
+  public static void div() {
+    divInt();
+    divFloat();
+    divDouble();
+  }
+
+  private static void divInt() {
+    expectEquals(2, $opt$DivLit(6));
+    expectEquals(2, $opt$Div(6, 3));
+    expectEquals(6, $opt$Div(6, 1));
+    expectEquals(-2, $opt$Div(6, -3));
+    expectEquals(1, $opt$Div(4, 3));
+    expectEquals(-1, $opt$Div(4, -3));
+    expectEquals(5, $opt$Div(23, 4));
+    expectEquals(-5, $opt$Div(-23, 4));
+
+    expectEquals(-Integer.MAX_VALUE, $opt$Div(Integer.MAX_VALUE, -1));
+    expectEquals(Integer.MIN_VALUE, $opt$Div(Integer.MIN_VALUE, -1)); // overflow
+    expectEquals(-1073741824, $opt$Div(Integer.MIN_VALUE, 2));
+
+    expectEquals(0, $opt$Div(0, Integer.MAX_VALUE));
+    expectEquals(0, $opt$Div(0, Integer.MIN_VALUE));
+
+    expectDivisionByZero(0);
+    expectDivisionByZero(1);
+    expectDivisionByZero(Integer.MAX_VALUE);
+    expectDivisionByZero(Integer.MIN_VALUE);
+  }
+
+  private static void divFloat() {
+    expectApproxEquals(1.6666666F, $opt$Div(5F, 3F));
+    expectApproxEquals(0F, $opt$Div(0F, 3F));
+    expectApproxEquals(-0.3333333F, $opt$Div(1F, -3F));
+    expectApproxEquals(4F, $opt$Div(-12F, -3F));
+    expectApproxEquals(0.5, $opt$Div(0.1F, 0.2F));
+    expectApproxEquals(-2.5F, $opt$Div(-0.5F, 0.2F));
+
+    expectEquals(0F, $opt$Div(0F, Float.POSITIVE_INFINITY));
+    expectEquals(0F, $opt$Div(11F, Float.POSITIVE_INFINITY));
+    expectEquals(0F, $opt$Div(0F, Float.NEGATIVE_INFINITY));
+    expectEquals(0F, $opt$Div(11F, Float.NEGATIVE_INFINITY));
+
+    expectNaN($opt$Div(0F, 0F));
+    expectNaN($opt$Div(Float.NaN, 11F));
+    expectNaN($opt$Div(-11F, Float.NaN));
+    expectNaN($opt$Div(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Div(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY));
+    expectNaN($opt$Div(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Div(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+    expectNaN($opt$Div(Float.NaN, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Div(Float.POSITIVE_INFINITY, Float.NaN));
+
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Div(3F, 0F));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Div(-3F, 0F));
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Div(Float.MAX_VALUE, Float.MIN_VALUE));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Div(-Float.MAX_VALUE, Float.MIN_VALUE));
+  }
+
+  private static void divDouble() {
+    expectApproxEquals(1.6666666D, $opt$Div(5D, 3D));
+    expectApproxEquals(0D, $opt$Div(0D, 3D));
+    expectApproxEquals(-0.3333333D, $opt$Div(1D, -3D));
+    expectApproxEquals(4D, $opt$Div(-12D, -3D));
+    expectApproxEquals(0.5, $opt$Div(0.1D, 0.2D));
+    expectApproxEquals(-2.5D, $opt$Div(-0.5D, 0.2D));
+
+    expectEquals(0D, $opt$Div(0D, Float.POSITIVE_INFINITY));
+    expectEquals(0D, $opt$Div(11D, Float.POSITIVE_INFINITY));
+    expectEquals(0D, $opt$Div(0D, Float.NEGATIVE_INFINITY));
+    expectEquals(0D, $opt$Div(11D, Float.NEGATIVE_INFINITY));
+
+    expectNaN($opt$Div(0D, 0D));
+    expectNaN($opt$Div(Float.NaN, 11D));
+    expectNaN($opt$Div(-11D, Float.NaN));
+    expectNaN($opt$Div(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Div(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY));
+    expectNaN($opt$Div(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Div(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+    expectNaN($opt$Div(Float.NaN, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Div(Float.POSITIVE_INFINITY, Float.NaN));
+
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Div(3D, 0D));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Div(-3D, 0D));
+    expectEquals(Float.POSITIVE_INFINITY, $opt$Div(Float.MAX_VALUE, Float.MIN_VALUE));
+    expectEquals(Float.NEGATIVE_INFINITY, $opt$Div(-Float.MAX_VALUE, Float.MIN_VALUE));
+  }
+
+  static int $opt$Div(int a, int b) {
+    return a / b;
+  }
+
+  static int $opt$DivZero(int a) {
+    return a / 0;
+  }
+
+  // Division by literals != 0 should not generate checks.
+  static int $opt$DivLit(int a) {
+    return a / 3;
+  }
+
+  static float $opt$Div(float a, float b) {
+    return a / b;
+  }
+
+  static double $opt$Div(double a, double b) {
+    return a / b;
+  }
+}
diff --git a/test/418-const-string/expected.txt b/test/418-const-string/expected.txt
new file mode 100644
index 0000000..8254f87
--- /dev/null
+++ b/test/418-const-string/expected.txt
@@ -0,0 +1,2 @@
+Hello World
+Hello World
diff --git a/test/418-const-string/info.txt b/test/418-const-string/info.txt
new file mode 100644
index 0000000..b7a468f
--- /dev/null
+++ b/test/418-const-string/info.txt
@@ -0,0 +1 @@
+Small test case for testing CONST_STRING.
diff --git a/test/418-const-string/src/Main.java b/test/418-const-string/src/Main.java
new file mode 100644
index 0000000..7c1ffec
--- /dev/null
+++ b/test/418-const-string/src/Main.java
@@ -0,0 +1,28 @@
+/*
+ * 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) {
+    // First call: may go in slow path.
+    System.out.println($opt$ReturnHelloWorld());
+    // Second call: no slow path.
+    System.out.println($opt$ReturnHelloWorld());
+  }
+
+  public static String $opt$ReturnHelloWorld() {
+    return "Hello World";
+  }
+}
diff --git a/test/419-long-parameter/expected.txt b/test/419-long-parameter/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/419-long-parameter/expected.txt
diff --git a/test/419-long-parameter/info.txt b/test/419-long-parameter/info.txt
new file mode 100644
index 0000000..5eac977
--- /dev/null
+++ b/test/419-long-parameter/info.txt
@@ -0,0 +1,3 @@
+Regression test for the long parameter passed both in stack and register
+on 32bits architectures. The move to hard float ABI makes it so that the
+register index does not necessarily match the stack index anymore.
diff --git a/test/419-long-parameter/src/Main.java b/test/419-long-parameter/src/Main.java
new file mode 100644
index 0000000..808b7f6
--- /dev/null
+++ b/test/419-long-parameter/src/Main.java
@@ -0,0 +1,34 @@
+/*
+ * 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) {
+    if ($opt$TestCallee(1.0, 2.0, 1L, 2L) != 1L) {
+      throw new Error("Unexpected result");
+    }
+    if ($opt$TestCaller() != 1L) {
+      throw new Error("Unexpected result");
+    }
+  }
+
+  public static long $opt$TestCallee(double a, double b, long c, long d) {
+    return d - c;
+  }
+
+  public static long $opt$TestCaller() {
+    return $opt$TestCallee(1.0, 2.0, 1L, 2L);
+  }
+}
diff --git a/test/420-const-class/expected.txt b/test/420-const-class/expected.txt
new file mode 100644
index 0000000..3213026
--- /dev/null
+++ b/test/420-const-class/expected.txt
@@ -0,0 +1,16 @@
+class Main
+class Main
+class Main$Other
+class Main$Other
+class java.lang.System
+class java.lang.System
+Hello from OtherWithClinit
+42
+class Main$OtherWithClinit
+42
+class Main$OtherWithClinit
+class Main$OtherWithClinit2
+Hello from OtherWithClinit2
+43
+class Main$OtherWithClinit2
+43
diff --git a/test/420-const-class/info.txt b/test/420-const-class/info.txt
new file mode 100644
index 0000000..81cbac7
--- /dev/null
+++ b/test/420-const-class/info.txt
@@ -0,0 +1 @@
+Test for the CONST_CLASS opcode.
diff --git a/test/420-const-class/src/Main.java b/test/420-const-class/src/Main.java
new file mode 100644
index 0000000..44a7436
--- /dev/null
+++ b/test/420-const-class/src/Main.java
@@ -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.
+ */
+
+public class Main {
+  static class Other {
+  }
+
+  static class OtherWithClinit {
+    static int a;
+    static {
+      System.out.println("Hello from OtherWithClinit");
+      a = 42;
+    }
+  }
+
+  static class OtherWithClinit2 {
+    static int a;
+    static {
+      System.out.println("Hello from OtherWithClinit2");
+      a = 43;
+    }
+  }
+
+  public static void main(String[] args) {
+    // Call methods twice in case they have a slow path.
+
+    System.out.println($opt$LoadThisClass());
+    System.out.println($opt$LoadThisClass());
+
+    System.out.println($opt$LoadOtherClass());
+    System.out.println($opt$LoadOtherClass());
+
+    System.out.println($opt$LoadSystemClass());
+    System.out.println($opt$LoadSystemClass());
+
+    $opt$ClinitCheckAndLoad();
+    $opt$ClinitCheckAndLoad();
+
+    $opt$LoadAndClinitCheck();
+    $opt$LoadAndClinitCheck();
+  }
+
+  public static Class $opt$LoadThisClass() {
+    return Main.class;
+  }
+
+  public static Class $opt$LoadOtherClass() {
+    return Other.class;
+  }
+
+  public static Class $opt$LoadSystemClass() {
+    return System.class;
+  }
+
+  public static void $opt$ClinitCheckAndLoad() {
+    System.out.println(OtherWithClinit.a);
+    System.out.println(OtherWithClinit.class);
+  }
+
+  public static void $opt$LoadAndClinitCheck() {
+    System.out.println(OtherWithClinit2.class);
+    System.out.println(OtherWithClinit2.a);
+  }
+}
diff --git a/test/421-exceptions/expected.txt b/test/421-exceptions/expected.txt
new file mode 100644
index 0000000..94db350
--- /dev/null
+++ b/test/421-exceptions/expected.txt
@@ -0,0 +1,20 @@
+1
+3
+4
+1
+4
+1
+4
+1
+4
+Caught class java.lang.RuntimeException
+1
+2
+4
+1
+4
+1
+4
+1
+4
+Caught class java.lang.NullPointerException
diff --git a/test/421-exceptions/info.txt b/test/421-exceptions/info.txt
new file mode 100644
index 0000000..bdec67e
--- /dev/null
+++ b/test/421-exceptions/info.txt
@@ -0,0 +1 @@
+Simple test for try/catch/throw.
diff --git a/test/421-exceptions/src/Main.java b/test/421-exceptions/src/Main.java
new file mode 100644
index 0000000..6bf2377
--- /dev/null
+++ b/test/421-exceptions/src/Main.java
@@ -0,0 +1,65 @@
+/*
+ * 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 $opt$bar() {
+    try {
+      $opt$foo(1);
+    } catch (NullPointerException e) {
+      $opt$foo(2);
+    } catch (RuntimeException e) {
+      $opt$foo(3);
+    } finally {
+      $opt$foo(4);
+    }
+  }
+
+  static int barState;
+  static int fooState;
+
+  public static void main(String[] args) {
+    fooState = 0;
+    $opt$runTest();
+    fooState = 1;
+    $opt$runTest();
+  }
+
+  public static void $opt$runTest() {
+    barState = 1;
+    $opt$bar();
+    barState = 2;
+    $opt$bar();
+    barState = 3;
+    $opt$bar();
+    barState = 4;
+    try {
+      $opt$bar();
+    } catch (RuntimeException e) {
+      System.out.println("Caught " + e.getClass());
+    }
+  }
+
+  public static void $opt$foo(int value) {
+    System.out.println(value);
+    if (value == barState) {
+      if (fooState == 0) {
+        throw new RuntimeException();
+      } else {
+        throw new NullPointerException();
+      }
+    }
+  }
+}
diff --git a/test/421-large-frame/expected.txt b/test/421-large-frame/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/421-large-frame/expected.txt
diff --git a/test/421-large-frame/info.txt b/test/421-large-frame/info.txt
new file mode 100644
index 0000000..d71e7ee
--- /dev/null
+++ b/test/421-large-frame/info.txt
@@ -0,0 +1 @@
+Tests for large stack frames.
diff --git a/test/421-large-frame/src/Main.java b/test/421-large-frame/src/Main.java
new file mode 100644
index 0000000..01b89ba
--- /dev/null
+++ b/test/421-large-frame/src/Main.java
@@ -0,0 +1,2034 @@
+/*
+ * 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.
+ */
+
+// Note that $opt$ is a marker for the optimizing compiler to ensure
+// it does compile the method.
+public class Main {
+
+  public static void assertEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void main(String[] args) {
+    // Sum[i = 0..999](i) = 999 * 1000 / 2 = 499500L.
+    assertEquals(499500L, $opt$LargeFrame());
+  }
+
+  static long $opt$LargeFrame() {
+    long l0 = 0L;
+    long l1 = 1L;
+    long l2 = 2L;
+    long l3 = 3L;
+    long l4 = 4L;
+    long l5 = 5L;
+    long l6 = 6L;
+    long l7 = 7L;
+    long l8 = 8L;
+    long l9 = 9L;
+    long l10 = 10L;
+    long l11 = 11L;
+    long l12 = 12L;
+    long l13 = 13L;
+    long l14 = 14L;
+    long l15 = 15L;
+    long l16 = 16L;
+    long l17 = 17L;
+    long l18 = 18L;
+    long l19 = 19L;
+    long l20 = 20L;
+    long l21 = 21L;
+    long l22 = 22L;
+    long l23 = 23L;
+    long l24 = 24L;
+    long l25 = 25L;
+    long l26 = 26L;
+    long l27 = 27L;
+    long l28 = 28L;
+    long l29 = 29L;
+    long l30 = 30L;
+    long l31 = 31L;
+    long l32 = 32L;
+    long l33 = 33L;
+    long l34 = 34L;
+    long l35 = 35L;
+    long l36 = 36L;
+    long l37 = 37L;
+    long l38 = 38L;
+    long l39 = 39L;
+    long l40 = 40L;
+    long l41 = 41L;
+    long l42 = 42L;
+    long l43 = 43L;
+    long l44 = 44L;
+    long l45 = 45L;
+    long l46 = 46L;
+    long l47 = 47L;
+    long l48 = 48L;
+    long l49 = 49L;
+    long l50 = 50L;
+    long l51 = 51L;
+    long l52 = 52L;
+    long l53 = 53L;
+    long l54 = 54L;
+    long l55 = 55L;
+    long l56 = 56L;
+    long l57 = 57L;
+    long l58 = 58L;
+    long l59 = 59L;
+    long l60 = 60L;
+    long l61 = 61L;
+    long l62 = 62L;
+    long l63 = 63L;
+    long l64 = 64L;
+    long l65 = 65L;
+    long l66 = 66L;
+    long l67 = 67L;
+    long l68 = 68L;
+    long l69 = 69L;
+    long l70 = 70L;
+    long l71 = 71L;
+    long l72 = 72L;
+    long l73 = 73L;
+    long l74 = 74L;
+    long l75 = 75L;
+    long l76 = 76L;
+    long l77 = 77L;
+    long l78 = 78L;
+    long l79 = 79L;
+    long l80 = 80L;
+    long l81 = 81L;
+    long l82 = 82L;
+    long l83 = 83L;
+    long l84 = 84L;
+    long l85 = 85L;
+    long l86 = 86L;
+    long l87 = 87L;
+    long l88 = 88L;
+    long l89 = 89L;
+    long l90 = 90L;
+    long l91 = 91L;
+    long l92 = 92L;
+    long l93 = 93L;
+    long l94 = 94L;
+    long l95 = 95L;
+    long l96 = 96L;
+    long l97 = 97L;
+    long l98 = 98L;
+    long l99 = 99L;
+    long l100 = 100L;
+    long l101 = 101L;
+    long l102 = 102L;
+    long l103 = 103L;
+    long l104 = 104L;
+    long l105 = 105L;
+    long l106 = 106L;
+    long l107 = 107L;
+    long l108 = 108L;
+    long l109 = 109L;
+    long l110 = 110L;
+    long l111 = 111L;
+    long l112 = 112L;
+    long l113 = 113L;
+    long l114 = 114L;
+    long l115 = 115L;
+    long l116 = 116L;
+    long l117 = 117L;
+    long l118 = 118L;
+    long l119 = 119L;
+    long l120 = 120L;
+    long l121 = 121L;
+    long l122 = 122L;
+    long l123 = 123L;
+    long l124 = 124L;
+    long l125 = 125L;
+    long l126 = 126L;
+    long l127 = 127L;
+    long l128 = 128L;
+    long l129 = 129L;
+    long l130 = 130L;
+    long l131 = 131L;
+    long l132 = 132L;
+    long l133 = 133L;
+    long l134 = 134L;
+    long l135 = 135L;
+    long l136 = 136L;
+    long l137 = 137L;
+    long l138 = 138L;
+    long l139 = 139L;
+    long l140 = 140L;
+    long l141 = 141L;
+    long l142 = 142L;
+    long l143 = 143L;
+    long l144 = 144L;
+    long l145 = 145L;
+    long l146 = 146L;
+    long l147 = 147L;
+    long l148 = 148L;
+    long l149 = 149L;
+    long l150 = 150L;
+    long l151 = 151L;
+    long l152 = 152L;
+    long l153 = 153L;
+    long l154 = 154L;
+    long l155 = 155L;
+    long l156 = 156L;
+    long l157 = 157L;
+    long l158 = 158L;
+    long l159 = 159L;
+    long l160 = 160L;
+    long l161 = 161L;
+    long l162 = 162L;
+    long l163 = 163L;
+    long l164 = 164L;
+    long l165 = 165L;
+    long l166 = 166L;
+    long l167 = 167L;
+    long l168 = 168L;
+    long l169 = 169L;
+    long l170 = 170L;
+    long l171 = 171L;
+    long l172 = 172L;
+    long l173 = 173L;
+    long l174 = 174L;
+    long l175 = 175L;
+    long l176 = 176L;
+    long l177 = 177L;
+    long l178 = 178L;
+    long l179 = 179L;
+    long l180 = 180L;
+    long l181 = 181L;
+    long l182 = 182L;
+    long l183 = 183L;
+    long l184 = 184L;
+    long l185 = 185L;
+    long l186 = 186L;
+    long l187 = 187L;
+    long l188 = 188L;
+    long l189 = 189L;
+    long l190 = 190L;
+    long l191 = 191L;
+    long l192 = 192L;
+    long l193 = 193L;
+    long l194 = 194L;
+    long l195 = 195L;
+    long l196 = 196L;
+    long l197 = 197L;
+    long l198 = 198L;
+    long l199 = 199L;
+    long l200 = 200L;
+    long l201 = 201L;
+    long l202 = 202L;
+    long l203 = 203L;
+    long l204 = 204L;
+    long l205 = 205L;
+    long l206 = 206L;
+    long l207 = 207L;
+    long l208 = 208L;
+    long l209 = 209L;
+    long l210 = 210L;
+    long l211 = 211L;
+    long l212 = 212L;
+    long l213 = 213L;
+    long l214 = 214L;
+    long l215 = 215L;
+    long l216 = 216L;
+    long l217 = 217L;
+    long l218 = 218L;
+    long l219 = 219L;
+    long l220 = 220L;
+    long l221 = 221L;
+    long l222 = 222L;
+    long l223 = 223L;
+    long l224 = 224L;
+    long l225 = 225L;
+    long l226 = 226L;
+    long l227 = 227L;
+    long l228 = 228L;
+    long l229 = 229L;
+    long l230 = 230L;
+    long l231 = 231L;
+    long l232 = 232L;
+    long l233 = 233L;
+    long l234 = 234L;
+    long l235 = 235L;
+    long l236 = 236L;
+    long l237 = 237L;
+    long l238 = 238L;
+    long l239 = 239L;
+    long l240 = 240L;
+    long l241 = 241L;
+    long l242 = 242L;
+    long l243 = 243L;
+    long l244 = 244L;
+    long l245 = 245L;
+    long l246 = 246L;
+    long l247 = 247L;
+    long l248 = 248L;
+    long l249 = 249L;
+    long l250 = 250L;
+    long l251 = 251L;
+    long l252 = 252L;
+    long l253 = 253L;
+    long l254 = 254L;
+    long l255 = 255L;
+    long l256 = 256L;
+    long l257 = 257L;
+    long l258 = 258L;
+    long l259 = 259L;
+    long l260 = 260L;
+    long l261 = 261L;
+    long l262 = 262L;
+    long l263 = 263L;
+    long l264 = 264L;
+    long l265 = 265L;
+    long l266 = 266L;
+    long l267 = 267L;
+    long l268 = 268L;
+    long l269 = 269L;
+    long l270 = 270L;
+    long l271 = 271L;
+    long l272 = 272L;
+    long l273 = 273L;
+    long l274 = 274L;
+    long l275 = 275L;
+    long l276 = 276L;
+    long l277 = 277L;
+    long l278 = 278L;
+    long l279 = 279L;
+    long l280 = 280L;
+    long l281 = 281L;
+    long l282 = 282L;
+    long l283 = 283L;
+    long l284 = 284L;
+    long l285 = 285L;
+    long l286 = 286L;
+    long l287 = 287L;
+    long l288 = 288L;
+    long l289 = 289L;
+    long l290 = 290L;
+    long l291 = 291L;
+    long l292 = 292L;
+    long l293 = 293L;
+    long l294 = 294L;
+    long l295 = 295L;
+    long l296 = 296L;
+    long l297 = 297L;
+    long l298 = 298L;
+    long l299 = 299L;
+    long l300 = 300L;
+    long l301 = 301L;
+    long l302 = 302L;
+    long l303 = 303L;
+    long l304 = 304L;
+    long l305 = 305L;
+    long l306 = 306L;
+    long l307 = 307L;
+    long l308 = 308L;
+    long l309 = 309L;
+    long l310 = 310L;
+    long l311 = 311L;
+    long l312 = 312L;
+    long l313 = 313L;
+    long l314 = 314L;
+    long l315 = 315L;
+    long l316 = 316L;
+    long l317 = 317L;
+    long l318 = 318L;
+    long l319 = 319L;
+    long l320 = 320L;
+    long l321 = 321L;
+    long l322 = 322L;
+    long l323 = 323L;
+    long l324 = 324L;
+    long l325 = 325L;
+    long l326 = 326L;
+    long l327 = 327L;
+    long l328 = 328L;
+    long l329 = 329L;
+    long l330 = 330L;
+    long l331 = 331L;
+    long l332 = 332L;
+    long l333 = 333L;
+    long l334 = 334L;
+    long l335 = 335L;
+    long l336 = 336L;
+    long l337 = 337L;
+    long l338 = 338L;
+    long l339 = 339L;
+    long l340 = 340L;
+    long l341 = 341L;
+    long l342 = 342L;
+    long l343 = 343L;
+    long l344 = 344L;
+    long l345 = 345L;
+    long l346 = 346L;
+    long l347 = 347L;
+    long l348 = 348L;
+    long l349 = 349L;
+    long l350 = 350L;
+    long l351 = 351L;
+    long l352 = 352L;
+    long l353 = 353L;
+    long l354 = 354L;
+    long l355 = 355L;
+    long l356 = 356L;
+    long l357 = 357L;
+    long l358 = 358L;
+    long l359 = 359L;
+    long l360 = 360L;
+    long l361 = 361L;
+    long l362 = 362L;
+    long l363 = 363L;
+    long l364 = 364L;
+    long l365 = 365L;
+    long l366 = 366L;
+    long l367 = 367L;
+    long l368 = 368L;
+    long l369 = 369L;
+    long l370 = 370L;
+    long l371 = 371L;
+    long l372 = 372L;
+    long l373 = 373L;
+    long l374 = 374L;
+    long l375 = 375L;
+    long l376 = 376L;
+    long l377 = 377L;
+    long l378 = 378L;
+    long l379 = 379L;
+    long l380 = 380L;
+    long l381 = 381L;
+    long l382 = 382L;
+    long l383 = 383L;
+    long l384 = 384L;
+    long l385 = 385L;
+    long l386 = 386L;
+    long l387 = 387L;
+    long l388 = 388L;
+    long l389 = 389L;
+    long l390 = 390L;
+    long l391 = 391L;
+    long l392 = 392L;
+    long l393 = 393L;
+    long l394 = 394L;
+    long l395 = 395L;
+    long l396 = 396L;
+    long l397 = 397L;
+    long l398 = 398L;
+    long l399 = 399L;
+    long l400 = 400L;
+    long l401 = 401L;
+    long l402 = 402L;
+    long l403 = 403L;
+    long l404 = 404L;
+    long l405 = 405L;
+    long l406 = 406L;
+    long l407 = 407L;
+    long l408 = 408L;
+    long l409 = 409L;
+    long l410 = 410L;
+    long l411 = 411L;
+    long l412 = 412L;
+    long l413 = 413L;
+    long l414 = 414L;
+    long l415 = 415L;
+    long l416 = 416L;
+    long l417 = 417L;
+    long l418 = 418L;
+    long l419 = 419L;
+    long l420 = 420L;
+    long l421 = 421L;
+    long l422 = 422L;
+    long l423 = 423L;
+    long l424 = 424L;
+    long l425 = 425L;
+    long l426 = 426L;
+    long l427 = 427L;
+    long l428 = 428L;
+    long l429 = 429L;
+    long l430 = 430L;
+    long l431 = 431L;
+    long l432 = 432L;
+    long l433 = 433L;
+    long l434 = 434L;
+    long l435 = 435L;
+    long l436 = 436L;
+    long l437 = 437L;
+    long l438 = 438L;
+    long l439 = 439L;
+    long l440 = 440L;
+    long l441 = 441L;
+    long l442 = 442L;
+    long l443 = 443L;
+    long l444 = 444L;
+    long l445 = 445L;
+    long l446 = 446L;
+    long l447 = 447L;
+    long l448 = 448L;
+    long l449 = 449L;
+    long l450 = 450L;
+    long l451 = 451L;
+    long l452 = 452L;
+    long l453 = 453L;
+    long l454 = 454L;
+    long l455 = 455L;
+    long l456 = 456L;
+    long l457 = 457L;
+    long l458 = 458L;
+    long l459 = 459L;
+    long l460 = 460L;
+    long l461 = 461L;
+    long l462 = 462L;
+    long l463 = 463L;
+    long l464 = 464L;
+    long l465 = 465L;
+    long l466 = 466L;
+    long l467 = 467L;
+    long l468 = 468L;
+    long l469 = 469L;
+    long l470 = 470L;
+    long l471 = 471L;
+    long l472 = 472L;
+    long l473 = 473L;
+    long l474 = 474L;
+    long l475 = 475L;
+    long l476 = 476L;
+    long l477 = 477L;
+    long l478 = 478L;
+    long l479 = 479L;
+    long l480 = 480L;
+    long l481 = 481L;
+    long l482 = 482L;
+    long l483 = 483L;
+    long l484 = 484L;
+    long l485 = 485L;
+    long l486 = 486L;
+    long l487 = 487L;
+    long l488 = 488L;
+    long l489 = 489L;
+    long l490 = 490L;
+    long l491 = 491L;
+    long l492 = 492L;
+    long l493 = 493L;
+    long l494 = 494L;
+    long l495 = 495L;
+    long l496 = 496L;
+    long l497 = 497L;
+    long l498 = 498L;
+    long l499 = 499L;
+    long l500 = 500L;
+    long l501 = 501L;
+    long l502 = 502L;
+    long l503 = 503L;
+    long l504 = 504L;
+    long l505 = 505L;
+    long l506 = 506L;
+    long l507 = 507L;
+    long l508 = 508L;
+    long l509 = 509L;
+    long l510 = 510L;
+    long l511 = 511L;
+    long l512 = 512L;
+    long l513 = 513L;
+    long l514 = 514L;
+    long l515 = 515L;
+    long l516 = 516L;
+    long l517 = 517L;
+    long l518 = 518L;
+    long l519 = 519L;
+    long l520 = 520L;
+    long l521 = 521L;
+    long l522 = 522L;
+    long l523 = 523L;
+    long l524 = 524L;
+    long l525 = 525L;
+    long l526 = 526L;
+    long l527 = 527L;
+    long l528 = 528L;
+    long l529 = 529L;
+    long l530 = 530L;
+    long l531 = 531L;
+    long l532 = 532L;
+    long l533 = 533L;
+    long l534 = 534L;
+    long l535 = 535L;
+    long l536 = 536L;
+    long l537 = 537L;
+    long l538 = 538L;
+    long l539 = 539L;
+    long l540 = 540L;
+    long l541 = 541L;
+    long l542 = 542L;
+    long l543 = 543L;
+    long l544 = 544L;
+    long l545 = 545L;
+    long l546 = 546L;
+    long l547 = 547L;
+    long l548 = 548L;
+    long l549 = 549L;
+    long l550 = 550L;
+    long l551 = 551L;
+    long l552 = 552L;
+    long l553 = 553L;
+    long l554 = 554L;
+    long l555 = 555L;
+    long l556 = 556L;
+    long l557 = 557L;
+    long l558 = 558L;
+    long l559 = 559L;
+    long l560 = 560L;
+    long l561 = 561L;
+    long l562 = 562L;
+    long l563 = 563L;
+    long l564 = 564L;
+    long l565 = 565L;
+    long l566 = 566L;
+    long l567 = 567L;
+    long l568 = 568L;
+    long l569 = 569L;
+    long l570 = 570L;
+    long l571 = 571L;
+    long l572 = 572L;
+    long l573 = 573L;
+    long l574 = 574L;
+    long l575 = 575L;
+    long l576 = 576L;
+    long l577 = 577L;
+    long l578 = 578L;
+    long l579 = 579L;
+    long l580 = 580L;
+    long l581 = 581L;
+    long l582 = 582L;
+    long l583 = 583L;
+    long l584 = 584L;
+    long l585 = 585L;
+    long l586 = 586L;
+    long l587 = 587L;
+    long l588 = 588L;
+    long l589 = 589L;
+    long l590 = 590L;
+    long l591 = 591L;
+    long l592 = 592L;
+    long l593 = 593L;
+    long l594 = 594L;
+    long l595 = 595L;
+    long l596 = 596L;
+    long l597 = 597L;
+    long l598 = 598L;
+    long l599 = 599L;
+    long l600 = 600L;
+    long l601 = 601L;
+    long l602 = 602L;
+    long l603 = 603L;
+    long l604 = 604L;
+    long l605 = 605L;
+    long l606 = 606L;
+    long l607 = 607L;
+    long l608 = 608L;
+    long l609 = 609L;
+    long l610 = 610L;
+    long l611 = 611L;
+    long l612 = 612L;
+    long l613 = 613L;
+    long l614 = 614L;
+    long l615 = 615L;
+    long l616 = 616L;
+    long l617 = 617L;
+    long l618 = 618L;
+    long l619 = 619L;
+    long l620 = 620L;
+    long l621 = 621L;
+    long l622 = 622L;
+    long l623 = 623L;
+    long l624 = 624L;
+    long l625 = 625L;
+    long l626 = 626L;
+    long l627 = 627L;
+    long l628 = 628L;
+    long l629 = 629L;
+    long l630 = 630L;
+    long l631 = 631L;
+    long l632 = 632L;
+    long l633 = 633L;
+    long l634 = 634L;
+    long l635 = 635L;
+    long l636 = 636L;
+    long l637 = 637L;
+    long l638 = 638L;
+    long l639 = 639L;
+    long l640 = 640L;
+    long l641 = 641L;
+    long l642 = 642L;
+    long l643 = 643L;
+    long l644 = 644L;
+    long l645 = 645L;
+    long l646 = 646L;
+    long l647 = 647L;
+    long l648 = 648L;
+    long l649 = 649L;
+    long l650 = 650L;
+    long l651 = 651L;
+    long l652 = 652L;
+    long l653 = 653L;
+    long l654 = 654L;
+    long l655 = 655L;
+    long l656 = 656L;
+    long l657 = 657L;
+    long l658 = 658L;
+    long l659 = 659L;
+    long l660 = 660L;
+    long l661 = 661L;
+    long l662 = 662L;
+    long l663 = 663L;
+    long l664 = 664L;
+    long l665 = 665L;
+    long l666 = 666L;
+    long l667 = 667L;
+    long l668 = 668L;
+    long l669 = 669L;
+    long l670 = 670L;
+    long l671 = 671L;
+    long l672 = 672L;
+    long l673 = 673L;
+    long l674 = 674L;
+    long l675 = 675L;
+    long l676 = 676L;
+    long l677 = 677L;
+    long l678 = 678L;
+    long l679 = 679L;
+    long l680 = 680L;
+    long l681 = 681L;
+    long l682 = 682L;
+    long l683 = 683L;
+    long l684 = 684L;
+    long l685 = 685L;
+    long l686 = 686L;
+    long l687 = 687L;
+    long l688 = 688L;
+    long l689 = 689L;
+    long l690 = 690L;
+    long l691 = 691L;
+    long l692 = 692L;
+    long l693 = 693L;
+    long l694 = 694L;
+    long l695 = 695L;
+    long l696 = 696L;
+    long l697 = 697L;
+    long l698 = 698L;
+    long l699 = 699L;
+    long l700 = 700L;
+    long l701 = 701L;
+    long l702 = 702L;
+    long l703 = 703L;
+    long l704 = 704L;
+    long l705 = 705L;
+    long l706 = 706L;
+    long l707 = 707L;
+    long l708 = 708L;
+    long l709 = 709L;
+    long l710 = 710L;
+    long l711 = 711L;
+    long l712 = 712L;
+    long l713 = 713L;
+    long l714 = 714L;
+    long l715 = 715L;
+    long l716 = 716L;
+    long l717 = 717L;
+    long l718 = 718L;
+    long l719 = 719L;
+    long l720 = 720L;
+    long l721 = 721L;
+    long l722 = 722L;
+    long l723 = 723L;
+    long l724 = 724L;
+    long l725 = 725L;
+    long l726 = 726L;
+    long l727 = 727L;
+    long l728 = 728L;
+    long l729 = 729L;
+    long l730 = 730L;
+    long l731 = 731L;
+    long l732 = 732L;
+    long l733 = 733L;
+    long l734 = 734L;
+    long l735 = 735L;
+    long l736 = 736L;
+    long l737 = 737L;
+    long l738 = 738L;
+    long l739 = 739L;
+    long l740 = 740L;
+    long l741 = 741L;
+    long l742 = 742L;
+    long l743 = 743L;
+    long l744 = 744L;
+    long l745 = 745L;
+    long l746 = 746L;
+    long l747 = 747L;
+    long l748 = 748L;
+    long l749 = 749L;
+    long l750 = 750L;
+    long l751 = 751L;
+    long l752 = 752L;
+    long l753 = 753L;
+    long l754 = 754L;
+    long l755 = 755L;
+    long l756 = 756L;
+    long l757 = 757L;
+    long l758 = 758L;
+    long l759 = 759L;
+    long l760 = 760L;
+    long l761 = 761L;
+    long l762 = 762L;
+    long l763 = 763L;
+    long l764 = 764L;
+    long l765 = 765L;
+    long l766 = 766L;
+    long l767 = 767L;
+    long l768 = 768L;
+    long l769 = 769L;
+    long l770 = 770L;
+    long l771 = 771L;
+    long l772 = 772L;
+    long l773 = 773L;
+    long l774 = 774L;
+    long l775 = 775L;
+    long l776 = 776L;
+    long l777 = 777L;
+    long l778 = 778L;
+    long l779 = 779L;
+    long l780 = 780L;
+    long l781 = 781L;
+    long l782 = 782L;
+    long l783 = 783L;
+    long l784 = 784L;
+    long l785 = 785L;
+    long l786 = 786L;
+    long l787 = 787L;
+    long l788 = 788L;
+    long l789 = 789L;
+    long l790 = 790L;
+    long l791 = 791L;
+    long l792 = 792L;
+    long l793 = 793L;
+    long l794 = 794L;
+    long l795 = 795L;
+    long l796 = 796L;
+    long l797 = 797L;
+    long l798 = 798L;
+    long l799 = 799L;
+    long l800 = 800L;
+    long l801 = 801L;
+    long l802 = 802L;
+    long l803 = 803L;
+    long l804 = 804L;
+    long l805 = 805L;
+    long l806 = 806L;
+    long l807 = 807L;
+    long l808 = 808L;
+    long l809 = 809L;
+    long l810 = 810L;
+    long l811 = 811L;
+    long l812 = 812L;
+    long l813 = 813L;
+    long l814 = 814L;
+    long l815 = 815L;
+    long l816 = 816L;
+    long l817 = 817L;
+    long l818 = 818L;
+    long l819 = 819L;
+    long l820 = 820L;
+    long l821 = 821L;
+    long l822 = 822L;
+    long l823 = 823L;
+    long l824 = 824L;
+    long l825 = 825L;
+    long l826 = 826L;
+    long l827 = 827L;
+    long l828 = 828L;
+    long l829 = 829L;
+    long l830 = 830L;
+    long l831 = 831L;
+    long l832 = 832L;
+    long l833 = 833L;
+    long l834 = 834L;
+    long l835 = 835L;
+    long l836 = 836L;
+    long l837 = 837L;
+    long l838 = 838L;
+    long l839 = 839L;
+    long l840 = 840L;
+    long l841 = 841L;
+    long l842 = 842L;
+    long l843 = 843L;
+    long l844 = 844L;
+    long l845 = 845L;
+    long l846 = 846L;
+    long l847 = 847L;
+    long l848 = 848L;
+    long l849 = 849L;
+    long l850 = 850L;
+    long l851 = 851L;
+    long l852 = 852L;
+    long l853 = 853L;
+    long l854 = 854L;
+    long l855 = 855L;
+    long l856 = 856L;
+    long l857 = 857L;
+    long l858 = 858L;
+    long l859 = 859L;
+    long l860 = 860L;
+    long l861 = 861L;
+    long l862 = 862L;
+    long l863 = 863L;
+    long l864 = 864L;
+    long l865 = 865L;
+    long l866 = 866L;
+    long l867 = 867L;
+    long l868 = 868L;
+    long l869 = 869L;
+    long l870 = 870L;
+    long l871 = 871L;
+    long l872 = 872L;
+    long l873 = 873L;
+    long l874 = 874L;
+    long l875 = 875L;
+    long l876 = 876L;
+    long l877 = 877L;
+    long l878 = 878L;
+    long l879 = 879L;
+    long l880 = 880L;
+    long l881 = 881L;
+    long l882 = 882L;
+    long l883 = 883L;
+    long l884 = 884L;
+    long l885 = 885L;
+    long l886 = 886L;
+    long l887 = 887L;
+    long l888 = 888L;
+    long l889 = 889L;
+    long l890 = 890L;
+    long l891 = 891L;
+    long l892 = 892L;
+    long l893 = 893L;
+    long l894 = 894L;
+    long l895 = 895L;
+    long l896 = 896L;
+    long l897 = 897L;
+    long l898 = 898L;
+    long l899 = 899L;
+    long l900 = 900L;
+    long l901 = 901L;
+    long l902 = 902L;
+    long l903 = 903L;
+    long l904 = 904L;
+    long l905 = 905L;
+    long l906 = 906L;
+    long l907 = 907L;
+    long l908 = 908L;
+    long l909 = 909L;
+    long l910 = 910L;
+    long l911 = 911L;
+    long l912 = 912L;
+    long l913 = 913L;
+    long l914 = 914L;
+    long l915 = 915L;
+    long l916 = 916L;
+    long l917 = 917L;
+    long l918 = 918L;
+    long l919 = 919L;
+    long l920 = 920L;
+    long l921 = 921L;
+    long l922 = 922L;
+    long l923 = 923L;
+    long l924 = 924L;
+    long l925 = 925L;
+    long l926 = 926L;
+    long l927 = 927L;
+    long l928 = 928L;
+    long l929 = 929L;
+    long l930 = 930L;
+    long l931 = 931L;
+    long l932 = 932L;
+    long l933 = 933L;
+    long l934 = 934L;
+    long l935 = 935L;
+    long l936 = 936L;
+    long l937 = 937L;
+    long l938 = 938L;
+    long l939 = 939L;
+    long l940 = 940L;
+    long l941 = 941L;
+    long l942 = 942L;
+    long l943 = 943L;
+    long l944 = 944L;
+    long l945 = 945L;
+    long l946 = 946L;
+    long l947 = 947L;
+    long l948 = 948L;
+    long l949 = 949L;
+    long l950 = 950L;
+    long l951 = 951L;
+    long l952 = 952L;
+    long l953 = 953L;
+    long l954 = 954L;
+    long l955 = 955L;
+    long l956 = 956L;
+    long l957 = 957L;
+    long l958 = 958L;
+    long l959 = 959L;
+    long l960 = 960L;
+    long l961 = 961L;
+    long l962 = 962L;
+    long l963 = 963L;
+    long l964 = 964L;
+    long l965 = 965L;
+    long l966 = 966L;
+    long l967 = 967L;
+    long l968 = 968L;
+    long l969 = 969L;
+    long l970 = 970L;
+    long l971 = 971L;
+    long l972 = 972L;
+    long l973 = 973L;
+    long l974 = 974L;
+    long l975 = 975L;
+    long l976 = 976L;
+    long l977 = 977L;
+    long l978 = 978L;
+    long l979 = 979L;
+    long l980 = 980L;
+    long l981 = 981L;
+    long l982 = 982L;
+    long l983 = 983L;
+    long l984 = 984L;
+    long l985 = 985L;
+    long l986 = 986L;
+    long l987 = 987L;
+    long l988 = 988L;
+    long l989 = 989L;
+    long l990 = 990L;
+    long l991 = 991L;
+    long l992 = 992L;
+    long l993 = 993L;
+    long l994 = 994L;
+    long l995 = 995L;
+    long l996 = 996L;
+    long l997 = 997L;
+    long l998 = 998L;
+    long l999 = 999L;
+    l1 += l0;
+    l2 += l1;
+    l3 += l2;
+    l4 += l3;
+    l5 += l4;
+    l6 += l5;
+    l7 += l6;
+    l8 += l7;
+    l9 += l8;
+    l10 += l9;
+    l11 += l10;
+    l12 += l11;
+    l13 += l12;
+    l14 += l13;
+    l15 += l14;
+    l16 += l15;
+    l17 += l16;
+    l18 += l17;
+    l19 += l18;
+    l20 += l19;
+    l21 += l20;
+    l22 += l21;
+    l23 += l22;
+    l24 += l23;
+    l25 += l24;
+    l26 += l25;
+    l27 += l26;
+    l28 += l27;
+    l29 += l28;
+    l30 += l29;
+    l31 += l30;
+    l32 += l31;
+    l33 += l32;
+    l34 += l33;
+    l35 += l34;
+    l36 += l35;
+    l37 += l36;
+    l38 += l37;
+    l39 += l38;
+    l40 += l39;
+    l41 += l40;
+    l42 += l41;
+    l43 += l42;
+    l44 += l43;
+    l45 += l44;
+    l46 += l45;
+    l47 += l46;
+    l48 += l47;
+    l49 += l48;
+    l50 += l49;
+    l51 += l50;
+    l52 += l51;
+    l53 += l52;
+    l54 += l53;
+    l55 += l54;
+    l56 += l55;
+    l57 += l56;
+    l58 += l57;
+    l59 += l58;
+    l60 += l59;
+    l61 += l60;
+    l62 += l61;
+    l63 += l62;
+    l64 += l63;
+    l65 += l64;
+    l66 += l65;
+    l67 += l66;
+    l68 += l67;
+    l69 += l68;
+    l70 += l69;
+    l71 += l70;
+    l72 += l71;
+    l73 += l72;
+    l74 += l73;
+    l75 += l74;
+    l76 += l75;
+    l77 += l76;
+    l78 += l77;
+    l79 += l78;
+    l80 += l79;
+    l81 += l80;
+    l82 += l81;
+    l83 += l82;
+    l84 += l83;
+    l85 += l84;
+    l86 += l85;
+    l87 += l86;
+    l88 += l87;
+    l89 += l88;
+    l90 += l89;
+    l91 += l90;
+    l92 += l91;
+    l93 += l92;
+    l94 += l93;
+    l95 += l94;
+    l96 += l95;
+    l97 += l96;
+    l98 += l97;
+    l99 += l98;
+    l100 += l99;
+    l101 += l100;
+    l102 += l101;
+    l103 += l102;
+    l104 += l103;
+    l105 += l104;
+    l106 += l105;
+    l107 += l106;
+    l108 += l107;
+    l109 += l108;
+    l110 += l109;
+    l111 += l110;
+    l112 += l111;
+    l113 += l112;
+    l114 += l113;
+    l115 += l114;
+    l116 += l115;
+    l117 += l116;
+    l118 += l117;
+    l119 += l118;
+    l120 += l119;
+    l121 += l120;
+    l122 += l121;
+    l123 += l122;
+    l124 += l123;
+    l125 += l124;
+    l126 += l125;
+    l127 += l126;
+    l128 += l127;
+    l129 += l128;
+    l130 += l129;
+    l131 += l130;
+    l132 += l131;
+    l133 += l132;
+    l134 += l133;
+    l135 += l134;
+    l136 += l135;
+    l137 += l136;
+    l138 += l137;
+    l139 += l138;
+    l140 += l139;
+    l141 += l140;
+    l142 += l141;
+    l143 += l142;
+    l144 += l143;
+    l145 += l144;
+    l146 += l145;
+    l147 += l146;
+    l148 += l147;
+    l149 += l148;
+    l150 += l149;
+    l151 += l150;
+    l152 += l151;
+    l153 += l152;
+    l154 += l153;
+    l155 += l154;
+    l156 += l155;
+    l157 += l156;
+    l158 += l157;
+    l159 += l158;
+    l160 += l159;
+    l161 += l160;
+    l162 += l161;
+    l163 += l162;
+    l164 += l163;
+    l165 += l164;
+    l166 += l165;
+    l167 += l166;
+    l168 += l167;
+    l169 += l168;
+    l170 += l169;
+    l171 += l170;
+    l172 += l171;
+    l173 += l172;
+    l174 += l173;
+    l175 += l174;
+    l176 += l175;
+    l177 += l176;
+    l178 += l177;
+    l179 += l178;
+    l180 += l179;
+    l181 += l180;
+    l182 += l181;
+    l183 += l182;
+    l184 += l183;
+    l185 += l184;
+    l186 += l185;
+    l187 += l186;
+    l188 += l187;
+    l189 += l188;
+    l190 += l189;
+    l191 += l190;
+    l192 += l191;
+    l193 += l192;
+    l194 += l193;
+    l195 += l194;
+    l196 += l195;
+    l197 += l196;
+    l198 += l197;
+    l199 += l198;
+    l200 += l199;
+    l201 += l200;
+    l202 += l201;
+    l203 += l202;
+    l204 += l203;
+    l205 += l204;
+    l206 += l205;
+    l207 += l206;
+    l208 += l207;
+    l209 += l208;
+    l210 += l209;
+    l211 += l210;
+    l212 += l211;
+    l213 += l212;
+    l214 += l213;
+    l215 += l214;
+    l216 += l215;
+    l217 += l216;
+    l218 += l217;
+    l219 += l218;
+    l220 += l219;
+    l221 += l220;
+    l222 += l221;
+    l223 += l222;
+    l224 += l223;
+    l225 += l224;
+    l226 += l225;
+    l227 += l226;
+    l228 += l227;
+    l229 += l228;
+    l230 += l229;
+    l231 += l230;
+    l232 += l231;
+    l233 += l232;
+    l234 += l233;
+    l235 += l234;
+    l236 += l235;
+    l237 += l236;
+    l238 += l237;
+    l239 += l238;
+    l240 += l239;
+    l241 += l240;
+    l242 += l241;
+    l243 += l242;
+    l244 += l243;
+    l245 += l244;
+    l246 += l245;
+    l247 += l246;
+    l248 += l247;
+    l249 += l248;
+    l250 += l249;
+    l251 += l250;
+    l252 += l251;
+    l253 += l252;
+    l254 += l253;
+    l255 += l254;
+    l256 += l255;
+    l257 += l256;
+    l258 += l257;
+    l259 += l258;
+    l260 += l259;
+    l261 += l260;
+    l262 += l261;
+    l263 += l262;
+    l264 += l263;
+    l265 += l264;
+    l266 += l265;
+    l267 += l266;
+    l268 += l267;
+    l269 += l268;
+    l270 += l269;
+    l271 += l270;
+    l272 += l271;
+    l273 += l272;
+    l274 += l273;
+    l275 += l274;
+    l276 += l275;
+    l277 += l276;
+    l278 += l277;
+    l279 += l278;
+    l280 += l279;
+    l281 += l280;
+    l282 += l281;
+    l283 += l282;
+    l284 += l283;
+    l285 += l284;
+    l286 += l285;
+    l287 += l286;
+    l288 += l287;
+    l289 += l288;
+    l290 += l289;
+    l291 += l290;
+    l292 += l291;
+    l293 += l292;
+    l294 += l293;
+    l295 += l294;
+    l296 += l295;
+    l297 += l296;
+    l298 += l297;
+    l299 += l298;
+    l300 += l299;
+    l301 += l300;
+    l302 += l301;
+    l303 += l302;
+    l304 += l303;
+    l305 += l304;
+    l306 += l305;
+    l307 += l306;
+    l308 += l307;
+    l309 += l308;
+    l310 += l309;
+    l311 += l310;
+    l312 += l311;
+    l313 += l312;
+    l314 += l313;
+    l315 += l314;
+    l316 += l315;
+    l317 += l316;
+    l318 += l317;
+    l319 += l318;
+    l320 += l319;
+    l321 += l320;
+    l322 += l321;
+    l323 += l322;
+    l324 += l323;
+    l325 += l324;
+    l326 += l325;
+    l327 += l326;
+    l328 += l327;
+    l329 += l328;
+    l330 += l329;
+    l331 += l330;
+    l332 += l331;
+    l333 += l332;
+    l334 += l333;
+    l335 += l334;
+    l336 += l335;
+    l337 += l336;
+    l338 += l337;
+    l339 += l338;
+    l340 += l339;
+    l341 += l340;
+    l342 += l341;
+    l343 += l342;
+    l344 += l343;
+    l345 += l344;
+    l346 += l345;
+    l347 += l346;
+    l348 += l347;
+    l349 += l348;
+    l350 += l349;
+    l351 += l350;
+    l352 += l351;
+    l353 += l352;
+    l354 += l353;
+    l355 += l354;
+    l356 += l355;
+    l357 += l356;
+    l358 += l357;
+    l359 += l358;
+    l360 += l359;
+    l361 += l360;
+    l362 += l361;
+    l363 += l362;
+    l364 += l363;
+    l365 += l364;
+    l366 += l365;
+    l367 += l366;
+    l368 += l367;
+    l369 += l368;
+    l370 += l369;
+    l371 += l370;
+    l372 += l371;
+    l373 += l372;
+    l374 += l373;
+    l375 += l374;
+    l376 += l375;
+    l377 += l376;
+    l378 += l377;
+    l379 += l378;
+    l380 += l379;
+    l381 += l380;
+    l382 += l381;
+    l383 += l382;
+    l384 += l383;
+    l385 += l384;
+    l386 += l385;
+    l387 += l386;
+    l388 += l387;
+    l389 += l388;
+    l390 += l389;
+    l391 += l390;
+    l392 += l391;
+    l393 += l392;
+    l394 += l393;
+    l395 += l394;
+    l396 += l395;
+    l397 += l396;
+    l398 += l397;
+    l399 += l398;
+    l400 += l399;
+    l401 += l400;
+    l402 += l401;
+    l403 += l402;
+    l404 += l403;
+    l405 += l404;
+    l406 += l405;
+    l407 += l406;
+    l408 += l407;
+    l409 += l408;
+    l410 += l409;
+    l411 += l410;
+    l412 += l411;
+    l413 += l412;
+    l414 += l413;
+    l415 += l414;
+    l416 += l415;
+    l417 += l416;
+    l418 += l417;
+    l419 += l418;
+    l420 += l419;
+    l421 += l420;
+    l422 += l421;
+    l423 += l422;
+    l424 += l423;
+    l425 += l424;
+    l426 += l425;
+    l427 += l426;
+    l428 += l427;
+    l429 += l428;
+    l430 += l429;
+    l431 += l430;
+    l432 += l431;
+    l433 += l432;
+    l434 += l433;
+    l435 += l434;
+    l436 += l435;
+    l437 += l436;
+    l438 += l437;
+    l439 += l438;
+    l440 += l439;
+    l441 += l440;
+    l442 += l441;
+    l443 += l442;
+    l444 += l443;
+    l445 += l444;
+    l446 += l445;
+    l447 += l446;
+    l448 += l447;
+    l449 += l448;
+    l450 += l449;
+    l451 += l450;
+    l452 += l451;
+    l453 += l452;
+    l454 += l453;
+    l455 += l454;
+    l456 += l455;
+    l457 += l456;
+    l458 += l457;
+    l459 += l458;
+    l460 += l459;
+    l461 += l460;
+    l462 += l461;
+    l463 += l462;
+    l464 += l463;
+    l465 += l464;
+    l466 += l465;
+    l467 += l466;
+    l468 += l467;
+    l469 += l468;
+    l470 += l469;
+    l471 += l470;
+    l472 += l471;
+    l473 += l472;
+    l474 += l473;
+    l475 += l474;
+    l476 += l475;
+    l477 += l476;
+    l478 += l477;
+    l479 += l478;
+    l480 += l479;
+    l481 += l480;
+    l482 += l481;
+    l483 += l482;
+    l484 += l483;
+    l485 += l484;
+    l486 += l485;
+    l487 += l486;
+    l488 += l487;
+    l489 += l488;
+    l490 += l489;
+    l491 += l490;
+    l492 += l491;
+    l493 += l492;
+    l494 += l493;
+    l495 += l494;
+    l496 += l495;
+    l497 += l496;
+    l498 += l497;
+    l499 += l498;
+    l500 += l499;
+    l501 += l500;
+    l502 += l501;
+    l503 += l502;
+    l504 += l503;
+    l505 += l504;
+    l506 += l505;
+    l507 += l506;
+    l508 += l507;
+    l509 += l508;
+    l510 += l509;
+    l511 += l510;
+    l512 += l511;
+    l513 += l512;
+    l514 += l513;
+    l515 += l514;
+    l516 += l515;
+    l517 += l516;
+    l518 += l517;
+    l519 += l518;
+    l520 += l519;
+    l521 += l520;
+    l522 += l521;
+    l523 += l522;
+    l524 += l523;
+    l525 += l524;
+    l526 += l525;
+    l527 += l526;
+    l528 += l527;
+    l529 += l528;
+    l530 += l529;
+    l531 += l530;
+    l532 += l531;
+    l533 += l532;
+    l534 += l533;
+    l535 += l534;
+    l536 += l535;
+    l537 += l536;
+    l538 += l537;
+    l539 += l538;
+    l540 += l539;
+    l541 += l540;
+    l542 += l541;
+    l543 += l542;
+    l544 += l543;
+    l545 += l544;
+    l546 += l545;
+    l547 += l546;
+    l548 += l547;
+    l549 += l548;
+    l550 += l549;
+    l551 += l550;
+    l552 += l551;
+    l553 += l552;
+    l554 += l553;
+    l555 += l554;
+    l556 += l555;
+    l557 += l556;
+    l558 += l557;
+    l559 += l558;
+    l560 += l559;
+    l561 += l560;
+    l562 += l561;
+    l563 += l562;
+    l564 += l563;
+    l565 += l564;
+    l566 += l565;
+    l567 += l566;
+    l568 += l567;
+    l569 += l568;
+    l570 += l569;
+    l571 += l570;
+    l572 += l571;
+    l573 += l572;
+    l574 += l573;
+    l575 += l574;
+    l576 += l575;
+    l577 += l576;
+    l578 += l577;
+    l579 += l578;
+    l580 += l579;
+    l581 += l580;
+    l582 += l581;
+    l583 += l582;
+    l584 += l583;
+    l585 += l584;
+    l586 += l585;
+    l587 += l586;
+    l588 += l587;
+    l589 += l588;
+    l590 += l589;
+    l591 += l590;
+    l592 += l591;
+    l593 += l592;
+    l594 += l593;
+    l595 += l594;
+    l596 += l595;
+    l597 += l596;
+    l598 += l597;
+    l599 += l598;
+    l600 += l599;
+    l601 += l600;
+    l602 += l601;
+    l603 += l602;
+    l604 += l603;
+    l605 += l604;
+    l606 += l605;
+    l607 += l606;
+    l608 += l607;
+    l609 += l608;
+    l610 += l609;
+    l611 += l610;
+    l612 += l611;
+    l613 += l612;
+    l614 += l613;
+    l615 += l614;
+    l616 += l615;
+    l617 += l616;
+    l618 += l617;
+    l619 += l618;
+    l620 += l619;
+    l621 += l620;
+    l622 += l621;
+    l623 += l622;
+    l624 += l623;
+    l625 += l624;
+    l626 += l625;
+    l627 += l626;
+    l628 += l627;
+    l629 += l628;
+    l630 += l629;
+    l631 += l630;
+    l632 += l631;
+    l633 += l632;
+    l634 += l633;
+    l635 += l634;
+    l636 += l635;
+    l637 += l636;
+    l638 += l637;
+    l639 += l638;
+    l640 += l639;
+    l641 += l640;
+    l642 += l641;
+    l643 += l642;
+    l644 += l643;
+    l645 += l644;
+    l646 += l645;
+    l647 += l646;
+    l648 += l647;
+    l649 += l648;
+    l650 += l649;
+    l651 += l650;
+    l652 += l651;
+    l653 += l652;
+    l654 += l653;
+    l655 += l654;
+    l656 += l655;
+    l657 += l656;
+    l658 += l657;
+    l659 += l658;
+    l660 += l659;
+    l661 += l660;
+    l662 += l661;
+    l663 += l662;
+    l664 += l663;
+    l665 += l664;
+    l666 += l665;
+    l667 += l666;
+    l668 += l667;
+    l669 += l668;
+    l670 += l669;
+    l671 += l670;
+    l672 += l671;
+    l673 += l672;
+    l674 += l673;
+    l675 += l674;
+    l676 += l675;
+    l677 += l676;
+    l678 += l677;
+    l679 += l678;
+    l680 += l679;
+    l681 += l680;
+    l682 += l681;
+    l683 += l682;
+    l684 += l683;
+    l685 += l684;
+    l686 += l685;
+    l687 += l686;
+    l688 += l687;
+    l689 += l688;
+    l690 += l689;
+    l691 += l690;
+    l692 += l691;
+    l693 += l692;
+    l694 += l693;
+    l695 += l694;
+    l696 += l695;
+    l697 += l696;
+    l698 += l697;
+    l699 += l698;
+    l700 += l699;
+    l701 += l700;
+    l702 += l701;
+    l703 += l702;
+    l704 += l703;
+    l705 += l704;
+    l706 += l705;
+    l707 += l706;
+    l708 += l707;
+    l709 += l708;
+    l710 += l709;
+    l711 += l710;
+    l712 += l711;
+    l713 += l712;
+    l714 += l713;
+    l715 += l714;
+    l716 += l715;
+    l717 += l716;
+    l718 += l717;
+    l719 += l718;
+    l720 += l719;
+    l721 += l720;
+    l722 += l721;
+    l723 += l722;
+    l724 += l723;
+    l725 += l724;
+    l726 += l725;
+    l727 += l726;
+    l728 += l727;
+    l729 += l728;
+    l730 += l729;
+    l731 += l730;
+    l732 += l731;
+    l733 += l732;
+    l734 += l733;
+    l735 += l734;
+    l736 += l735;
+    l737 += l736;
+    l738 += l737;
+    l739 += l738;
+    l740 += l739;
+    l741 += l740;
+    l742 += l741;
+    l743 += l742;
+    l744 += l743;
+    l745 += l744;
+    l746 += l745;
+    l747 += l746;
+    l748 += l747;
+    l749 += l748;
+    l750 += l749;
+    l751 += l750;
+    l752 += l751;
+    l753 += l752;
+    l754 += l753;
+    l755 += l754;
+    l756 += l755;
+    l757 += l756;
+    l758 += l757;
+    l759 += l758;
+    l760 += l759;
+    l761 += l760;
+    l762 += l761;
+    l763 += l762;
+    l764 += l763;
+    l765 += l764;
+    l766 += l765;
+    l767 += l766;
+    l768 += l767;
+    l769 += l768;
+    l770 += l769;
+    l771 += l770;
+    l772 += l771;
+    l773 += l772;
+    l774 += l773;
+    l775 += l774;
+    l776 += l775;
+    l777 += l776;
+    l778 += l777;
+    l779 += l778;
+    l780 += l779;
+    l781 += l780;
+    l782 += l781;
+    l783 += l782;
+    l784 += l783;
+    l785 += l784;
+    l786 += l785;
+    l787 += l786;
+    l788 += l787;
+    l789 += l788;
+    l790 += l789;
+    l791 += l790;
+    l792 += l791;
+    l793 += l792;
+    l794 += l793;
+    l795 += l794;
+    l796 += l795;
+    l797 += l796;
+    l798 += l797;
+    l799 += l798;
+    l800 += l799;
+    l801 += l800;
+    l802 += l801;
+    l803 += l802;
+    l804 += l803;
+    l805 += l804;
+    l806 += l805;
+    l807 += l806;
+    l808 += l807;
+    l809 += l808;
+    l810 += l809;
+    l811 += l810;
+    l812 += l811;
+    l813 += l812;
+    l814 += l813;
+    l815 += l814;
+    l816 += l815;
+    l817 += l816;
+    l818 += l817;
+    l819 += l818;
+    l820 += l819;
+    l821 += l820;
+    l822 += l821;
+    l823 += l822;
+    l824 += l823;
+    l825 += l824;
+    l826 += l825;
+    l827 += l826;
+    l828 += l827;
+    l829 += l828;
+    l830 += l829;
+    l831 += l830;
+    l832 += l831;
+    l833 += l832;
+    l834 += l833;
+    l835 += l834;
+    l836 += l835;
+    l837 += l836;
+    l838 += l837;
+    l839 += l838;
+    l840 += l839;
+    l841 += l840;
+    l842 += l841;
+    l843 += l842;
+    l844 += l843;
+    l845 += l844;
+    l846 += l845;
+    l847 += l846;
+    l848 += l847;
+    l849 += l848;
+    l850 += l849;
+    l851 += l850;
+    l852 += l851;
+    l853 += l852;
+    l854 += l853;
+    l855 += l854;
+    l856 += l855;
+    l857 += l856;
+    l858 += l857;
+    l859 += l858;
+    l860 += l859;
+    l861 += l860;
+    l862 += l861;
+    l863 += l862;
+    l864 += l863;
+    l865 += l864;
+    l866 += l865;
+    l867 += l866;
+    l868 += l867;
+    l869 += l868;
+    l870 += l869;
+    l871 += l870;
+    l872 += l871;
+    l873 += l872;
+    l874 += l873;
+    l875 += l874;
+    l876 += l875;
+    l877 += l876;
+    l878 += l877;
+    l879 += l878;
+    l880 += l879;
+    l881 += l880;
+    l882 += l881;
+    l883 += l882;
+    l884 += l883;
+    l885 += l884;
+    l886 += l885;
+    l887 += l886;
+    l888 += l887;
+    l889 += l888;
+    l890 += l889;
+    l891 += l890;
+    l892 += l891;
+    l893 += l892;
+    l894 += l893;
+    l895 += l894;
+    l896 += l895;
+    l897 += l896;
+    l898 += l897;
+    l899 += l898;
+    l900 += l899;
+    l901 += l900;
+    l902 += l901;
+    l903 += l902;
+    l904 += l903;
+    l905 += l904;
+    l906 += l905;
+    l907 += l906;
+    l908 += l907;
+    l909 += l908;
+    l910 += l909;
+    l911 += l910;
+    l912 += l911;
+    l913 += l912;
+    l914 += l913;
+    l915 += l914;
+    l916 += l915;
+    l917 += l916;
+    l918 += l917;
+    l919 += l918;
+    l920 += l919;
+    l921 += l920;
+    l922 += l921;
+    l923 += l922;
+    l924 += l923;
+    l925 += l924;
+    l926 += l925;
+    l927 += l926;
+    l928 += l927;
+    l929 += l928;
+    l930 += l929;
+    l931 += l930;
+    l932 += l931;
+    l933 += l932;
+    l934 += l933;
+    l935 += l934;
+    l936 += l935;
+    l937 += l936;
+    l938 += l937;
+    l939 += l938;
+    l940 += l939;
+    l941 += l940;
+    l942 += l941;
+    l943 += l942;
+    l944 += l943;
+    l945 += l944;
+    l946 += l945;
+    l947 += l946;
+    l948 += l947;
+    l949 += l948;
+    l950 += l949;
+    l951 += l950;
+    l952 += l951;
+    l953 += l952;
+    l954 += l953;
+    l955 += l954;
+    l956 += l955;
+    l957 += l956;
+    l958 += l957;
+    l959 += l958;
+    l960 += l959;
+    l961 += l960;
+    l962 += l961;
+    l963 += l962;
+    l964 += l963;
+    l965 += l964;
+    l966 += l965;
+    l967 += l966;
+    l968 += l967;
+    l969 += l968;
+    l970 += l969;
+    l971 += l970;
+    l972 += l971;
+    l973 += l972;
+    l974 += l973;
+    l975 += l974;
+    l976 += l975;
+    l977 += l976;
+    l978 += l977;
+    l979 += l978;
+    l980 += l979;
+    l981 += l980;
+    l982 += l981;
+    l983 += l982;
+    l984 += l983;
+    l985 += l984;
+    l986 += l985;
+    l987 += l986;
+    l988 += l987;
+    l989 += l988;
+    l990 += l989;
+    l991 += l990;
+    l992 += l991;
+    l993 += l992;
+    l994 += l993;
+    l995 += l994;
+    l996 += l995;
+    l997 += l996;
+    l998 += l997;
+    l999 += l998;
+    return l999;
+  }
+}
diff --git a/test/422-instanceof/expected.txt b/test/422-instanceof/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/422-instanceof/expected.txt
diff --git a/test/422-instanceof/info.txt b/test/422-instanceof/info.txt
new file mode 100644
index 0000000..b2f7ff1
--- /dev/null
+++ b/test/422-instanceof/info.txt
@@ -0,0 +1 @@
+Tests for instanceof bytecode.
diff --git a/test/422-instanceof/src/Main.java b/test/422-instanceof/src/Main.java
new file mode 100644
index 0000000..307c987
--- /dev/null
+++ b/test/422-instanceof/src/Main.java
@@ -0,0 +1,70 @@
+/*
+ * 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 Object a;
+
+  public static void assertTrue(boolean value) {
+    if (!value) {
+      throw new Error("Wrong result");
+    }
+  }
+
+  public static void assertFalse(boolean value) {
+    if (value) {
+      throw new Error("Wrong result");
+    }
+  }
+
+  public static boolean $opt$InstanceOfMain() {
+    return a instanceof Main;
+  }
+
+  public static boolean $opt$InstanceOfFinalClass() {
+    return a instanceof FinalClass;
+  }
+
+  public static void main(String[] args) {
+    $opt$TestMain();
+    $opt$TestFinalClass();
+  }
+
+  public static void $opt$TestMain() {
+    a = new Main();
+    assertTrue($opt$InstanceOfMain());
+    a = null;
+    assertFalse($opt$InstanceOfMain());
+    a = new MainChild();
+    assertTrue($opt$InstanceOfMain());
+    a = new Object();
+    assertFalse($opt$InstanceOfMain());
+  }
+
+  public static void $opt$TestFinalClass() {
+    a = new FinalClass();
+    assertTrue($opt$InstanceOfFinalClass());
+    a = null;
+    assertFalse($opt$InstanceOfFinalClass());
+    a = new Main();
+    assertFalse($opt$InstanceOfFinalClass());
+    a = new Object();
+    assertFalse($opt$InstanceOfFinalClass());
+  }
+
+  static class MainChild extends Main {}
+
+  static final class FinalClass {}
+}
diff --git a/test/422-type-conversion/expected.txt b/test/422-type-conversion/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/422-type-conversion/expected.txt
diff --git a/test/422-type-conversion/info.txt b/test/422-type-conversion/info.txt
new file mode 100644
index 0000000..e734f32
--- /dev/null
+++ b/test/422-type-conversion/info.txt
@@ -0,0 +1 @@
+Tests for type conversions.
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
new file mode 100644
index 0000000..d61f255
--- /dev/null
+++ b/test/422-type-conversion/src/Main.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+// Note that $opt$ is a marker for the optimizing compiler to ensure
+// it does compile the method.
+public class Main {
+
+  public static void assertEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void main(String[] args) {
+    byteToLong();
+    charToLong();
+    shortToLong();
+    intToLong();
+  }
+
+  private static void byteToLong() {
+    assertEquals(1L, $opt$ByteToLong((byte)1));
+    assertEquals(0L, $opt$ByteToLong((byte)0));
+    assertEquals(-1L, $opt$ByteToLong((byte)-1));
+    assertEquals(51L, $opt$ByteToLong((byte)51));
+    assertEquals(-51L, $opt$ByteToLong((byte)-51));
+    assertEquals(127L, $opt$ByteToLong((byte)127));  // (2^7) - 1
+    assertEquals(-127L, $opt$ByteToLong((byte)-127));  // -(2^7) - 1
+    assertEquals(-128L, $opt$ByteToLong((byte)-128));  // -(2^7)
+  }
+
+  private static void shortToLong() {
+    assertEquals(1L, $opt$ShortToLong((short)1));
+    assertEquals(0L, $opt$ShortToLong((short)0));
+    assertEquals(-1L, $opt$ShortToLong((short)-1));
+    assertEquals(51L, $opt$ShortToLong((short)51));
+    assertEquals(-51L, $opt$ShortToLong((short)-51));
+    assertEquals(32767L, $opt$ShortToLong((short)32767));  // (2^15) - 1
+    assertEquals(-32767L, $opt$ShortToLong((short)-32767));  // -(2^15) - 1
+    assertEquals(-32768L, $opt$ShortToLong((short)-32768));  // -(2^15)
+  }
+
+  private static void intToLong() {
+    assertEquals(1L, $opt$IntToLong(1));
+    assertEquals(0L, $opt$IntToLong(0));
+    assertEquals(-1L, $opt$IntToLong(-1));
+    assertEquals(51L, $opt$IntToLong(51));
+    assertEquals(-51L, $opt$IntToLong(-51));
+    assertEquals(2147483647L, $opt$IntToLong(2147483647));  // (2^31) - 1
+    assertEquals(-2147483647L, $opt$IntToLong(-2147483647));  // -(2^31) - 1
+    assertEquals(-2147483648L, $opt$IntToLong(-2147483648));  // -(2^31)
+  }
+
+  private static void charToLong() {
+    assertEquals(1L, $opt$CharToLong((char)1));
+    assertEquals(0L, $opt$CharToLong((char)0));
+    assertEquals(51L, $opt$CharToLong((char)51));
+    assertEquals(32767L, $opt$CharToLong((char)32767));  // (2^15) - 1
+    assertEquals(65535L, $opt$CharToLong((char)65535));  // (2^16) - 1
+
+    assertEquals(0L, $opt$CharToLong('\u0000'));
+    assertEquals(65535L, $opt$CharToLong('\uFFFF'));  // (2^16) - 1
+
+    assertEquals(65535L, $opt$CharToLong((char)-1));
+    assertEquals(65485L, $opt$CharToLong((char)-51));
+    assertEquals(32769L, $opt$CharToLong((char)-32767));  // -(2^15) - 1
+    assertEquals(32768L, $opt$CharToLong((char)-32768));  // -(2^15)
+  }
+
+  // All these methods produce an int-to-long Dex instruction.
+  static long $opt$ByteToLong(byte a) { return a; }
+  static long $opt$ShortToLong(short a) { return a; }
+  static long $opt$IntToLong(int a) { return a; }
+  static long $opt$CharToLong(int a) { return a; }
+}
diff --git a/test/423-invoke-interface/expected.txt b/test/423-invoke-interface/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/423-invoke-interface/expected.txt
diff --git a/test/423-invoke-interface/info.txt b/test/423-invoke-interface/info.txt
new file mode 100644
index 0000000..a496ffe
--- /dev/null
+++ b/test/423-invoke-interface/info.txt
@@ -0,0 +1,2 @@
+invoke-interface test with hopefully enough interface methods to trigger
+a conflict in our imt table.
diff --git a/test/423-invoke-interface/src/Main.java b/test/423-invoke-interface/src/Main.java
new file mode 100644
index 0000000..fae857a
--- /dev/null
+++ b/test/423-invoke-interface/src/Main.java
@@ -0,0 +1,120 @@
+/*
+ * 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 {
+  static interface Itf {
+    public int return1();
+    public int return2();
+    public int return3();
+    public int return4();
+    public int return5();
+    public int return6();
+    public int return7();
+    public int return8();
+    public int return9();
+    public int return10();
+    public int return11();
+    public int return12();
+    public int return13();
+    public int return14();
+    public int return15();
+    public int return16();
+    public int return17();
+    public int return18();
+    public int return19();
+    public int return20();
+  }
+
+  static class ItfImpl1 implements Itf {
+    public int return1() { return 1; }
+    public int return2() { return 2; }
+    public int return3() { return 3; }
+    public int return4() { return 4; }
+    public int return5() { return 5; }
+    public int return6() { return 6; }
+    public int return7() { return 7; }
+    public int return8() { return 8; }
+    public int return9() { return 9; }
+    public int return10() { return 10; }
+    public int return11() { return 11; }
+    public int return12() { return 12; }
+    public int return13() { return 13; }
+    public int return14() { return 14; }
+    public int return15() { return 15; }
+    public int return16() { return 16; }
+    public int return17() { return 17; }
+    public int return18() { return 18; }
+    public int return19() { return 19; }
+    public int return20() { return 20; }
+  }
+
+  static class ItfImpl2 implements Itf {
+    public int return1() { return -1; }
+    public int return2() { return -2; }
+    public int return3() { return -3; }
+    public int return4() { return -4; }
+    public int return5() { return -5; }
+    public int return6() { return -6; }
+    public int return7() { return -7; }
+    public int return8() { return -8; }
+    public int return9() { return -9; }
+    public int return10() { return -10; }
+    public int return11() { return -11; }
+    public int return12() { return -12; }
+    public int return13() { return -13; }
+    public int return14() { return -14; }
+    public int return15() { return -15; }
+    public int return16() { return -16; }
+    public int return17() { return -17; }
+    public int return18() { return -18; }
+    public int return19() { return -19; }
+    public int return20() { return -20; }
+  }
+
+  public static void main(String[] args) {
+    $opt$InvokeInterface(new ItfImpl1(), 1);
+    $opt$InvokeInterface(new ItfImpl2(), -1);
+  }
+
+  public static void assertEquals(int expected, int value) {
+    if (expected != value) {
+      throw new Error("Expected " + expected +  ", got " + value);
+    }
+  }
+
+  public static void $opt$InvokeInterface(Itf object, int factor) {
+    assertEquals(factor * 1, object.return1());
+    assertEquals(factor * 2, object.return2());
+    assertEquals(factor * 3, object.return3());
+    assertEquals(factor * 4, object.return4());
+    assertEquals(factor * 5, object.return5());
+    assertEquals(factor * 6, object.return6());
+    assertEquals(factor * 7, object.return7());
+    assertEquals(factor * 8, object.return8());
+    assertEquals(factor * 9, object.return9());
+    assertEquals(factor * 10, object.return10());
+    assertEquals(factor * 11, object.return11());
+    assertEquals(factor * 12, object.return12());
+    assertEquals(factor * 13, object.return13());
+    assertEquals(factor * 14, object.return14());
+    assertEquals(factor * 15, object.return15());
+    assertEquals(factor * 16, object.return16());
+    assertEquals(factor * 17, object.return17());
+    assertEquals(factor * 18, object.return18());
+    assertEquals(factor * 19, object.return19());
+    assertEquals(factor * 20, object.return20());
+  }
+}
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/703-floating-point-div/expected.txt b/test/703-floating-point-div/expected.txt
new file mode 100644
index 0000000..76f5a5a
--- /dev/null
+++ b/test/703-floating-point-div/expected.txt
@@ -0,0 +1 @@
+Done!
diff --git a/test/703-floating-point-div/info.txt b/test/703-floating-point-div/info.txt
new file mode 100644
index 0000000..418b831
--- /dev/null
+++ b/test/703-floating-point-div/info.txt
@@ -0,0 +1 @@
+Simple tests to check floating point division.
diff --git a/test/703-floating-point-div/src/Main.java b/test/703-floating-point-div/src/Main.java
new file mode 100644
index 0000000..9990a54
--- /dev/null
+++ b/test/703-floating-point-div/src/Main.java
@@ -0,0 +1,90 @@
+/*
+ * 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 {
+
+    static double dPi = Math.PI;
+    static float  fPi = (float)Math.PI;
+
+    public static void expectEquals(long expected, long result) {
+        if (expected != result) {
+            throw new Error("Expected: " + expected + ", found: " + result);
+        }
+    }
+
+    public static void expectEquals(int expected, int result) {
+        if (expected != result) {
+            throw new Error("Expected: " + expected + ", found: " + result);
+        }
+    }
+
+    public static void divDoubleTest() {
+        double d1 = 0x1.0p1023;
+        double d2 = -2.0;
+        double d3 = 0.0;
+        double d4 = Double.MIN_NORMAL;
+        double d5 = Double.POSITIVE_INFINITY;
+        double d6 = Double.NEGATIVE_INFINITY;
+        double d7 = -0.0;
+        double d8 = Double.MAX_VALUE;
+        double d9 = Double.MIN_VALUE;
+        double d0 = Double.NaN;
+
+        expectEquals(Double.doubleToRawLongBits(dPi/d1), 0x1921fb54442d18L);
+        expectEquals(Double.doubleToRawLongBits(dPi/d2), 0xbff921fb54442d18L);
+        expectEquals(Double.doubleToRawLongBits(dPi/d3), 0x7ff0000000000000L);
+        expectEquals(Double.doubleToRawLongBits(dPi/d4), 0x7fe921fb54442d18L);
+        expectEquals(Double.doubleToRawLongBits(dPi/d5), 0x0L);
+        expectEquals(Double.doubleToRawLongBits(dPi/d6), 0x8000000000000000L);
+        expectEquals(Double.doubleToRawLongBits(dPi/d7), 0xfff0000000000000L);
+
+        expectEquals(Double.doubleToRawLongBits(dPi/d8), 0xc90fdaa22168cL);
+        expectEquals(Double.doubleToRawLongBits(dPi/d9), 0x7ff0000000000000L);
+        expectEquals(Double.doubleToRawLongBits(dPi/d0), 0x7ff8000000000000L);
+    }
+
+    public static void divFloatTest() {
+        float f1 = 0x1.0p127f;
+        float f2 = -2.0f;
+        float f3 = 0.0f;
+        float f4 = Float.MIN_NORMAL;
+        float f5 = Float.POSITIVE_INFINITY;
+        float f6 = Float.NEGATIVE_INFINITY;
+        float f7 = -0.0f;
+        float f8 = Float.MAX_VALUE;
+        float f9 = Float.MIN_VALUE;
+        float f0 = Float.NaN;
+
+        expectEquals(Float.floatToRawIntBits(fPi/f1), 0xc90fdb);
+        expectEquals(Float.floatToRawIntBits(fPi/f2), 0xbfc90fdb);
+        expectEquals(Float.floatToRawIntBits(fPi/f3), 0x7f800000);
+        expectEquals(Float.floatToRawIntBits(fPi/f4), 0x7f490fdb);
+        expectEquals(Float.floatToRawIntBits(fPi/f5), 0x0);
+        expectEquals(Float.floatToRawIntBits(fPi/f6), 0x80000000);
+        expectEquals(Float.floatToRawIntBits(fPi/f7), 0xff800000);
+
+        expectEquals(Float.floatToRawIntBits(fPi/f8), 0x6487ee);
+        expectEquals(Float.floatToRawIntBits(fPi/f9), 0x7f800000);
+        expectEquals(Float.floatToRawIntBits(fPi/f0), 0x7fc00000);
+    }
+
+    public static void main(String[] args) {
+        divDoubleTest();
+        divFloatTest();
+        System.out.println("Done!");
+    }
+
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
new file mode 100644
index 0000000..4002fbf
--- /dev/null
+++ b/test/800-smali/expected.txt
@@ -0,0 +1,3 @@
+b/17790197
+FloatBadArgReg
+Done!
diff --git a/test/800-smali/info.txt b/test/800-smali/info.txt
new file mode 100644
index 0000000..3022962
--- /dev/null
+++ b/test/800-smali/info.txt
@@ -0,0 +1,4 @@
+Smali-based tests.
+Will compile and run all the smali files in smali/ and run the test cases mentioned in src/Main.java.
+
+Obviously needs to run under Dalvik or ART.
diff --git a/test/800-smali/smali/FloatBadArgReg.smali b/test/800-smali/smali/FloatBadArgReg.smali
new file mode 100644
index 0000000..719ba09
--- /dev/null
+++ b/test/800-smali/smali/FloatBadArgReg.smali
@@ -0,0 +1,16 @@
+.class public LFloatBadArgReg;
+
+.super Ljava/lang/Object;
+
+.method public static getInt(I)I
+    .registers 2
+    const/4 v0, 0x0
+    if-ne v0, v0, :after
+    float-to-int v0, v0
+    :exit
+    add-int/2addr v0, v1
+    return v0
+    :after
+    move v1, v0
+    goto :exit
+.end method
diff --git a/test/800-smali/smali/b_17790197.smali b/test/800-smali/smali/b_17790197.smali
new file mode 100644
index 0000000..7560fcf
--- /dev/null
+++ b/test/800-smali/smali/b_17790197.smali
@@ -0,0 +1,17 @@
+.class public LB17790197;
+
+.super Ljava/lang/Object;
+
+.method public static getInt()I
+    .registers 4
+    const/16 v0, 100
+    const/4 v1, 1
+    const/4 v2, 7
+    :loop
+    if-eq v2, v0, :done
+    add-int v2, v2, v1
+    goto :loop
+    :done
+    add-float v3, v0, v1
+    return v2
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
new file mode 100644
index 0000000..c86470c
--- /dev/null
+++ b/test/800-smali/src/Main.java
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Smali excercise.
+ */
+public class Main {
+
+    private static class TestCase {
+        public TestCase(String testName, String testClass, String testMethodName, Object[] values,
+                        Throwable expectedException, Object expectedReturn) {
+            this.testName = testName;
+            this.testClass = testClass;
+            this.testMethodName = testMethodName;
+            this.values = values;
+            this.expectedException = expectedException;
+            this.expectedReturn = expectedReturn;
+        }
+
+        String testName;
+        String testClass;
+        String testMethodName;
+        Object[] values;
+        Throwable expectedException;
+        Object expectedReturn;
+    }
+
+    private List<TestCase> testCases;
+
+    public Main() {
+        // Create the test cases.
+        testCases = new LinkedList<TestCase>();
+
+        testCases.add(new TestCase("b/17790197", "B17790197", "getInt", null, null, 100));
+        testCases.add(new TestCase("FloatBadArgReg", "FloatBadArgReg", "getInt",
+            new Object[]{100}, null, 100));
+    }
+
+    public void runTests() {
+        for (TestCase tc : testCases) {
+            System.out.println(tc.testName);
+            try {
+                runTest(tc);
+            } catch (Exception exc) {
+                exc.printStackTrace(System.out);
+            }
+        }
+    }
+
+    private void runTest(TestCase tc) throws Exception {
+        Class<?> c = Class.forName(tc.testClass);
+
+        Method[] methods = c.getDeclaredMethods();
+
+        // For simplicity we assume that test methods are not overloaded. So searching by name
+        // will give us the method we need to run.
+        Method method = null;
+        for (Method m : methods) {
+            if (m.getName().equals(tc.testMethodName)) {
+                method = m;
+                break;
+            }
+        }
+
+        if (method == null) {
+            throw new IllegalArgumentException("Could not find test method " + tc.testMethodName +
+                    " in class " + tc.testClass + " for test " + tc.testName);
+        }
+
+        Exception errorReturn = null;
+        try {
+            Object retValue = method.invoke(null, tc.values);
+            if (tc.expectedException != null) {
+                errorReturn = new IllegalStateException("Expected an exception in test " +
+                                                        tc.testName);
+            }
+            if (tc.expectedReturn == null && retValue != null) {
+                errorReturn = new IllegalStateException("Expected a null result in test " +
+                                                        tc.testName);
+            } else if (tc.expectedReturn != null &&
+                       (retValue == null || !tc.expectedReturn.equals(retValue))) {
+                errorReturn = new IllegalStateException("Expected return " + tc.expectedReturn +
+                                                        ", but got " + retValue);
+            }
+        } catch (Exception exc) {
+            if (tc.expectedException == null) {
+                errorReturn = new IllegalStateException("Did not expect exception", exc);
+            } else if (!tc.expectedException.getClass().equals(exc.getClass())) {
+                errorReturn = new IllegalStateException("Expected " +
+                                                tc.expectedException.getClass().getName() +
+                                                ", but got " + exc.getClass(), exc);
+            }
+        } finally {
+            if (errorReturn != null) {
+                throw errorReturn;
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        Main main = new Main();
+
+        main.runTests();
+
+        System.out.println("Done!");
+    }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 2e12335..c9c0475 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -59,8 +59,7 @@
   ifeq ($$(art_target_or_host),target)
     $(call set-target-local-clang-vars)
     $(call set-target-local-cflags-vars,debug)
-    LOCAL_SHARED_LIBRARIES += libdl libcutils
-    LOCAL_STATIC_LIBRARIES := libgtest
+    LOCAL_SHARED_LIBRARIES += libdl
     LOCAL_MULTILIB := both
     LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32)
     LOCAL_MODULE_PATH_64 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_64)
@@ -69,11 +68,7 @@
   else # host
     LOCAL_CLANG := $(ART_HOST_CLANG)
     LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
-    LOCAL_STATIC_LIBRARIES := libcutils
-    LOCAL_LDLIBS += -ldl -lpthread
-    ifeq ($(HOST_OS),linux)
-      LOCAL_LDLIBS += -lrt
-    endif
+    LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
     LOCAL_IS_HOST_MODULE := true
     LOCAL_MULTILIB := both
     include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/test/Android.libnativebridgetest.mk b/test/Android.libnativebridgetest.mk
index dd7255a..1b20e69 100644
--- a/test/Android.libnativebridgetest.mk
+++ b/test/Android.libnativebridgetest.mk
@@ -51,7 +51,7 @@
   ifeq ($$(art_target_or_host),target)
     $(call set-target-local-clang-vars)
     $(call set-target-local-cflags-vars,debug)
-    LOCAL_SHARED_LIBRARIES += libdl libcutils
+    LOCAL_SHARED_LIBRARIES += libdl
     LOCAL_STATIC_LIBRARIES := libgtest
     LOCAL_MULTILIB := both
     LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32)
@@ -62,7 +62,7 @@
     LOCAL_CLANG := $(ART_HOST_CLANG)
     LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
     LOCAL_STATIC_LIBRARIES := libcutils
-    LOCAL_LDLIBS += -ldl -lpthread
+    LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
     ifeq ($(HOST_OS),linux)
       LOCAL_LDLIBS += -lrt
     endif
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 4ff6c65..e7a0439 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
@@ -168,9 +36,11 @@
 # $(1): the test number
 define define-build-art-run-test
   dmart_target := $(art_run_tests_dir)/art-run-tests/$(1)/touch
-$$(dmart_target): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin
+$$(dmart_target): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger
 	$(hide) rm -rf $$(dir $$@) && mkdir -p $$(dir $$@)
 	$(hide) DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
+	  SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \
+	  DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \
 	  $(LOCAL_PATH)/run-test --build-only --output-path $$(abspath $$(dir $$@)) $(1)
 	$(hide) touch $$@
 
@@ -189,135 +59,454 @@
 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 :=
+TARGET_TYPES := host target
+PREBUILD_TYPES :=
+ifeq ($(ART_TEST_RUN_TEST_PREBUILD),true)
+  PREBUILD_TYPES += prebuild
+endif
+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
+ifeq ($(ART_TEST_PIC_IMAGE),true)
+  IMAGE_TYPES += picimage
+endif
+PICTEST_TYPES := nopictest
+ifeq ($(ART_TEST_PIC_TEST),true)
+  PICTEST_TYPES += pictest
+endif
+RUN_TYPES :=
+ifeq ($(ART_TEST_RUN_TEST_DEBUG),true)
+  RUN_TYPES += debug
+endif
+ifeq ($(ART_TEST_RUN_TEST_NDEBUG),true)
+  RUN_TYPES += ndebug
+endif
+ADDRESS_SIZES_TARGET := $(ART_PHONY_TEST_TARGET_SUFFIX)
+ADDRESS_SIZES_HOST := $(ART_PHONY_TEST_HOST_SUFFIX)
+ifeq ($(ART_TEST_RUN_TEST_2ND_ARCH),true)
+  ADDRESS_SIZES_TARGET += $(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
+  ADDRESS_SIZES_HOST += $(2ND_ART_PHONY_TEST_HOST_SUFFIX)
+endif
+ALL_ADDRESS_SIZES := 64 32
 
-# 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)
+# List all run test names with number arguments agreeing with the comment above.
+define all-run-test-names
+  $(foreach target, $(1), \
+    $(foreach run-type, $(2), \
+      $(foreach prebuild, $(3), \
+        $(foreach compiler, $(4), \
+          $(foreach relocate, $(5), \
+            $(foreach trace, $(6), \
+              $(foreach gc, $(7), \
+                $(foreach jni, $(8), \
+                  $(foreach image, $(9), \
+                    $(foreach pictest, $(10), \
+                      $(foreach test, $(11), \
+                        $(foreach address_size, $(12), \
+                          test-art-$(target)-run-test-$(run-type)-$(prebuild)-$(compiler)-$(relocate)-$(trace)-$(gc)-$(jni)-$(image)-$(pictest)-$(test)$(address_size) \
+                    ))))))))))))
+endef  # all-run-test-names
+
+# To generate a full list or tests:
+# $(call all-run-test-names,$(TARGET_TYPES),$(RUN_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),$(RUN_TYPES),$(PREBUILD_TYPES), \
+        $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+        $(IMAGE_TYPES), $(PICTEST_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),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(PICTEST_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),$(RUN_TYPES),prebuild, \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(PICTEST_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),$(RUN_TYPES),no-prebuild, \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(PICTEST_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),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES), no-relocate,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(PICTEST_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 \
+  114-ParallelGC
+
+ifneq (,$(filter gcstress,$(GC_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),gcstress,$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(PICTEST_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,$(RUN_TYPES),$(PREBUILD_TYPES),$(COMPILER_TYPES), \
+    $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_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),$(RUN_TYPES),no-dex2oat, \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+      $(PICTEST_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),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),no-image, \
+      $(PICTEST_TYPES), $(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),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES), relocate-no-patchoat,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_FALLBACK_RUN_TESTS :=
+
+# The following tests use libarttest.so, which is linked against libartd.so, so will
+# not work when libart.so is the one loaded.
+# TODO: Find a way to run these tests in ndebug mode.
+TEST_ART_BROKEN_NDEBUG_TESTS := \
+  004-JniTest \
+  004-ReferenceMap \
+  004-SignalTest \
+  004-StackWalk \
+  004-UnsafeTest \
+  051-thread \
+  115-native-bridge \
+  116-nodex2oat \
+  117-nopatchoat \
+  118-noimage-dex2oat \
+  119-noimage-patchoat \
+
+ifneq (,$(filter ndebug,$(RUN_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+      $(PICTEST_TYPES),$(TEST_ART_BROKEN_NDEBUG_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_NDEBUG_TESTS :=
+
+# Known broken tests for the default compiler (Quick).
+TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \
+  412-new-array
+
+ifneq (,$(filter default,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_DEFAULT_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_DEFAULT_RUN_TESTS :=
+
+# Known broken tests for the arm64 optimizing compiler backend.
+TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := \
+  001-HelloWorld \
+  002-sleep \
+  003-omnibus-opcodes \
+  004-InterfaceTest \
+  004-JniTest \
+  004-ReferenceMap \
+  004-SignalTest \
+  004-StackWalk \
+  004-UnsafeTest \
+  005-annotations \
+  006-args \
+  007-count10 \
+  008-exceptions \
+  011-array-copy \
+  013-math2 \
+  016-intern \
+  017-float \
+  018-stack-overflow \
+  019-wrong-array-type \
+  020-string \
+  021-string2 \
+  022-interface \
+  023-many-interfaces \
+  024-illegal-access \
+  026-access \
+  028-array-write \
+  029-assert \
+  030-bad-finalizer \
+  031-class-attributes \
+  032-concrete-sub \
+  033-class-init-deadlock \
+  035-enum \
+  036-finalizer \
+  037-inherit \
+  038-inner-null \
+  039-join-main \
+  040-miranda \
+  042-new-instance \
+  043-privates \
+  044-proxy \
+  045-reflect-array \
+  046-reflect \
+  047-returns \
+  049-show-object \
+  050-sync-test \
+  051-thread \
+  052-verifier-fun \
+  054-uncaught \
+  055-enum-performance \
+  056-const-string-jumbo \
+  061-out-of-memory \
+  063-process-manager \
+  064-field-access \
+  065-mismatched-implements \
+  066-mismatched-super \
+  067-preemptive-unpark \
+  068-classloader \
+  069-field-type \
+  070-nio-buffer \
+  071-dexfile \
+  072-precise-gc \
+  074-gc-thrash \
+  075-verification-error \
+  076-boolean-put \
+  077-method-override \
+  078-polymorphic-virtual \
+  079-phantom \
+  080-oom-throw \
+  081-hot-exceptions \
+  082-inline-execute \
+  083-compiler-regressions \
+  084-class-init \
+  085-old-style-inner-class \
+  086-null-super \
+  087-gc-after-link \
+  088-monitor-verification \
+  090-loop-formation \
+  092-locale \
+  093-serialization \
+  094-pattern \
+  096-array-copy-concurrent-gc \
+  097-duplicate-method \
+  098-ddmc \
+  100-reflect2 \
+  101-fibonacci \
+  102-concurrent-gc \
+  103-string-append \
+  104-growth-limit \
+  105-invoke \
+  106-exceptions2 \
+  107-int-math2 \
+  109-suspend-check \
+  110-field-access \
+  111-unresolvable-exception \
+  112-double-math \
+  113-multidex \
+  117-nopatchoat \
+  118-noimage-dex2oat \
+  119-noimage-patchoat \
+  121-modifiers \
+  121-simple-suspend-check \
+  122-npe \
+  123-compiler-regressions-mt \
+  124-missing-classes \
+  125-gc-and-classloading \
+  126-miranda-multidex \
+  201-built-in-exception-detail-messages \
+  202-thread-oome \
+  300-package-override \
+  301-abstract-protected \
+  303-verification-stress \
+  401-optimizing-compiler \
+  402-optimizing-control-flow \
+  403-optimizing-long \
+  404-optimizing-allocator \
+  405-optimizing-long-allocator \
+  406-fields \
+  407-arrays \
+  409-materialized-condition \
+  410-floats \
+  411-optimizing-arith \
+  412-new-array \
+  413-regalloc-regression \
+  414-optimizing-arith-sub \
+  414-static-fields \
+  415-optimizing-arith-neg \
+  416-optimizing-arith-not \
+  417-optimizing-arith-div \
+  418-const-string \
+  419-long-parameter \
+  420-const-class \
+  421-exceptions \
+  421-large-frame \
+  422-instanceof \
+  422-type-conversion \
+  423-invoke-interface \
+  700-LoadArgRegs \
+  701-easy-div-rem \
+  702-LargeBranchOffset \
+  703-floating-point-div \
+  800-smali
+
+ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
+      optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS),64)
+endif
+
+TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS :=
+
+# Known broken tests for the optimizing compiler.
+TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS := \
+  099-vmdebug \ # b/18098594
+
+ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_OPTIMIZING_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 :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach run_type, $(RUN_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(run_type))_RULES :=)))
+
+# We need dex2oat and dalvikvm on the target as well as the core images (all images as we sync
+# only once).
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS)
 
 # Also need libarttest.
 TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
@@ -331,675 +520,336 @@
 TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libnativebridgetest.so
 endif
 
-# All tests require the host executables and the core images.
+# All tests require the host executables. The tests also depend on the core images, but on
+# specific version depending on the compiler.
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
   $(ART_HOST_EXECUTABLES) \
   $(ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
   $(ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
-  $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
-  $(HOST_CORE_IMG_OUT)
+  $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION)
 
 ifneq ($(HOST_PREFER_32_BIT),true)
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \
   $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
   $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
-  $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
-  $(2ND_HOST_CORE_IMG_OUT)
+  $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION)
 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: debug ndebug}-{3: prebuild no-prebuild no-dex2oat}-
+#    {4: interpreter default optimizing}-{5: relocate no-relocate relocate-no-patchoat}-
+#    {6: trace or no-trace}-{7: gcstress gcverify cms}-{8: forcecopy checkjni jni}-
+#    {9: no-image image picimage}-{10: pictest nopictest}-{11: test name}{12: 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 :=
+  run_test_options :=
   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),debug)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_DEBUG_RULES
   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),ndebug)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELEASE_RULES
+      run_test_options += -O
     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
+      $$(error found $(2) expected $(RUN_TYPES))
+    endif
+  endif
+  ifeq ($(3),prebuild)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PREBUILD_RULES
+    run_test_options += --prebuild
+  else
+    ifeq ($(3),no-prebuild)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_PREBUILD_RULES
+      run_test_options += --no-prebuild
+    else
+      ifeq ($(3),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 $(3) expected $(PREBUILD_TYPES))
       endif
     endif
   endif
-  uc_compiler :=
-  ifeq ($(3),optimizing)
-    uc_compiler := OPTIMIZING
-    run_test_options += -Xcompiler-option --compiler-backend=Optimizing
-    ifneq ($$(ART_TEST_OPTIMIZING),true)
-      skip_test := true
-    endif
+  ifeq ($(4),optimizing)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_OPTIMIZING_RULES
+    run_test_options += --optimizing
   else
-    ifeq ($(3),interpreter)
-      uc_compiler := INTERPRETER
+    ifeq ($(4),interpreter)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_INTERPRETER_RULES
       run_test_options += --interpreter
     else
-      ifeq ($(3),default)
-        uc_compiler := DEFAULT
+      ifeq ($(4),default)
+        test_groups += ART_RUN_TEST_$$(uc_host_or_target)_DEFAULT_RULES
       else
-        $$(error found $(3) expected optimizing, interpreter or default)
+        $$(error found $(4) expected $(COMPILER_TYPES))
       endif
     endif
   endif
-  ifeq ($(4),64)
-    run_test_options += --64
+
+  ifeq ($(5),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 ($(5),no-relocate)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_RELOCATE_RULES
+      run_test_options += --no-relocate
+    else
+      ifeq ($(5),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 $(5) expected $(RELOCATE_TYPES))
+      endif
     endif
   endif
-  ifeq ($(5),trace)
+  ifeq ($(6),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)
+  else
+    ifeq ($(6),no-trace)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_TRACE_RULES
+    else
+      $$(error found $(6) expected $(TRACE_TYPES))
+    endif
+  endif
+  ifeq ($(7),gcverify)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCVERIFY_RULES
+    run_test_options += --gcverify
+  else
+    ifeq ($(7),gcstress)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCSTRESS_RULES
+      run_test_options += --gcstress
+    else
+      ifeq ($(7),cms)
+        test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CMS_RULES
+      else
+        $$(error found $(7) expected $(GC_TYPES))
+      endif
+    endif
+  endif
+  ifeq ($(8),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 ($(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 ($(8),checkjni)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CHECKJNI_RULES
+      run_test_options += --runtime-option -Xcheck:jni
     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
+      ifeq ($(8),jni)
+        test_groups += ART_RUN_TEST_$$(uc_host_or_target)_JNI_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 $(8) expected $(JNI_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 ($(9),no-image)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_IMAGE_RULES
+    run_test_options += --no-image
+    # Add the core dependency. This is required for pre-building.
+    ifeq ($(1),host)
+      prereq_rule += $(HOST_CORE_IMAGE_$(4)_no-pic_$(12))
+    else
+      prereq_rule += $(TARGET_CORE_IMAGE_$(4)_no-pic_$(12))
+    endif
+  else
+    ifeq ($(9),image)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES
+      # Add the core dependency.
+      ifeq ($(1),host)
+        prereq_rule += $(HOST_CORE_IMAGE_$(4)_no-pic_$(12))
+      else
+        prereq_rule += $(TARGET_CORE_IMAGE_$(4)_no-pic_$(12))
+      endif
+    else
+      ifeq ($(9),picimage)
+        test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES
+        run_test_options += --pic-image
+        ifeq ($(1),host)
+          prereq_rule += $(HOST_CORE_IMAGE_$(4)_pic_$(12))
+        else
+          prereq_rule += $(TARGET_CORE_IMAGE_$(4)_pic_$(12))
+        endif
+      else
+        $$(error found $(9) expected $(IMAGE_TYPES))
+      endif
+    endif
+  endif
+  ifeq ($(10),pictest)
+    run_test_options += --pic-test
+  else
+    ifeq ($(10),nopictest)
+      # Nothing to be done.
+    else
+      $$(error found $(10) expected $(PICTEST_TYPES))
+    endif
+  endif
+  # $(11) is the test name
+  test_groups += ART_RUN_TEST_$$(uc_host_or_target)_$(call name-to-var,$(11))_RULES
+  ifeq ($(12),64)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_64_RULES
+    run_test_options += --64
+  else
+    ifeq ($(12),32)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_32_RULES
+    else
+      $$(error found $(12) expected $(ALL_ADDRESS_SIZES))
+    endif
+  endif
+  run_test_rule_name := test-art-$(1)-run-test-$(2)-$(3)-$(4)-$(5)-$(6)-$(7)-$(8)-$(9)-$(10)-$(11)$(12)
+  run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \
       $$(run_test_options)
+  ifneq ($(ART_TEST_ANDROID_ROOT),)
+    run_test_options := --android-root $(ART_TEST_ANDROID_ROOT) $$(run_test_options)
+  endif
 $$(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)
+$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger $$(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) \
+	    SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \
+	    DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \
+	    art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(11) \
 	      && $$(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 run_type, $(RUN_TYPES), \
+      $(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), \
+                      $(foreach pictest, $(PICTEST_TYPES), \
+                        $(eval $(call define-test-art-run-test,$(target),$(run_type),$(prebuild),$(compiler),$(relocate),$(trace),$(gc),$(jni),$(image),$(pictest),$(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 run-type, $(RUN_TYPES), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(run-type),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(run-type))_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_$(call name-to-var,$(target))_$(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 :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach run_type, $(RUN_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(run_type))_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 :=
+RUN_TYPES :=
+
+include $(LOCAL_PATH)/Android.libarttest.mk
+include art/test/Android.libnativebridgetest.mk
diff --git a/test/etc/default-build b/test/etc/default-build
index faafc1f..ab859ec 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -20,16 +20,21 @@
 mkdir classes
 ${JAVAC} -d classes `find src -name '*.java'`
 
-if [ -r src2 ]; then
+if [ -d src2 ]; then
   ${JAVAC} -d classes `find src2 -name '*.java'`
 fi
 
 if [ ${NEED_DEX} = "true" ]; then
   ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
-  zip $TEST_NAME.jar classes.dex
 fi
 
-if [ -r src-ex ]; then
+if [ -d smali ]; then
+  # Compile Smali classes
+  ${SMALI} -JXmx256m --output smali_classes.dex `find smali -name '*.smali'`
+  ${DXMERGER} classes.dex classes.dex smali_classes.dex
+fi
+
+if [ -d src-ex ]; then
   mkdir classes-ex
   ${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
   if [ ${NEED_DEX} = "true" ]; then
@@ -43,3 +48,7 @@
     mv classes-1.dex classes.dex
   fi
 fi
+
+if [ ${NEED_DEX} = "true" ]; then
+  zip $TEST_NAME.jar classes.dex
+fi
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
deleted file mode 100755
index 37c579d..0000000
--- a/test/etc/host-run-test-jar
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/bin/sh
-#
-# Run the code in test.jar using the host-mode virtual machine. The jar should
-# contain a top-level class named Main to run.
-
-msg() {
-    if [ "$QUIET" = "n" ]; then
-        echo "$@"
-    fi
-}
-
-DEBUGGER="n"
-PREBUILD="n"
-GDB="n"
-ISA="x86"
-INTERPRETER="n"
-VERIFY="y"
-RELOCATE="y"
-OPTIMIZE="y"
-INVOKE_WITH=""
-DEV_MODE="n"
-QUIET="n"
-FLAGS=""
-COMPILER_FLAGS=""
-BUILD_BOOT_OPT=""
-SECONDARY_DEX=""
-exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
-main="Main"
-DEX_VERIFY=""
-
-while true; do
-    if [ "x$1" = "x--quiet" ]; then
-        QUIET="y"
-        shift
-    elif [ "x$1" = "x--prebuild" ]; then
-        PREBUILD="y"
-        shift
-    elif [ "x$1" = "x--lib" ]; then
-        shift
-        if [ "x$1" = "x" ]; then
-            echo "$0 missing argument to --lib" 1>&2
-            exit 1
-        fi
-        LIB="$1"
-        if [ `uname` = "Darwin" ]; then
-            LIB=${LIB/%so/dylib}
-        fi
-        shift
-    elif [ "x$1" = "x--boot" ]; then
-        shift
-        option="$1"
-        BOOT_OPT="$option"
-        BUILD_BOOT_OPT="--boot-image=${option#-Ximage:}"
-        shift
-    elif [ "x$1" = "x--debug" ]; then
-        DEBUGGER="y"
-        shift
-    elif [ "x$1" = "x--gdb" ]; then
-        GDB="y"
-        DEV_MODE="y"
-        shift
-    elif [ "x$1" = "x--invoke-with" ]; then
-        shift
-        if [ "x$1" = "x" ]; then
-            echo "$0 missing argument to --invoke-with" 1>&2
-            exit 1
-        fi
-        if [ "x$INVOKE_WITH" = "x" ]; then
-            INVOKE_WITH="$1"
-        else
-            INVOKE_WITH="$INVOKE_WITH $1"
-        fi
-        shift
-    elif [ "x$1" = "x--dev" ]; then
-        DEV_MODE="y"
-        shift
-    elif [ "x$1" = "x--interpreter" ]; then
-        INTERPRETER="y"
-        shift
-    elif [ "x$1" = "x--64" ]; then
-        ISA="x86_64"
-        exe="${ANDROID_HOST_OUT}/bin/dalvikvm64"
-        shift
-    elif [ "x$1" = "x--no-verify" ]; then
-        VERIFY="n"
-        shift
-    elif [ "x$1" = "x--no-optimize" ]; then
-        OPTIMIZE="n"
-        shift
-    elif [ "x$1" = "x--no-relocate" ]; then
-        RELOCATE="n"
-        shift
-    elif [ "x$1" = "x--relocate" ]; then
-        RELOCATE="y"
-        shift
-    elif [ "x$1" = "x--secondary" ]; then
-        SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
-        shift
-    elif [ "x$1" = "x-Xcompiler-option" ]; then
-        shift
-        option="$1"
-        FLAGS="${FLAGS} -Xcompiler-option $option"
-        COMPILER_FLAGS="${COMPILER_FLAGS} $option"
-        shift
-    elif [ "x$1" = "x--runtime-option" ]; then
-        shift
-        option="$1"
-        FLAGS="${FLAGS} $option"
-        shift
-    elif [ "x$1" = "x--" ]; then
-        shift
-        break
-    elif expr "x$1" : "x--" >/dev/null 2>&1; then
-        echo "unknown $0 option: $1" 1>&2
-        exit 1
-    else
-        break
-    fi
-done
-
-if [ "x$1" = "x" ] ; then
-  main="Main"
-else
-  main="$1"
-fi
-
-msg "------------------------------"
-
-export ANDROID_PRINTF_LOG=brief
-if [ "$DEV_MODE" = "y" ]; then
-    export ANDROID_LOG_TAGS='*:d'
-else
-    export ANDROID_LOG_TAGS='*:s'
-fi
-export ANDROID_DATA="$DEX_LOCATION"
-export ANDROID_ROOT="${ANDROID_HOST_OUT}"
-export LD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
-export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
-
-if [ "$DEBUGGER" = "y" ]; then
-    PORT=8000
-    msg "Waiting for jdb to connect:"
-    msg "    jdb -attach localhost:$PORT"
-    DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
-fi
-
-if [ "$GDB" = "y" ]; then
-    if [ `uname` = "Darwin" ]; then
-        gdb=lldb
-        gdbargs="-- $exe"
-        exe=
-    else
-        gdb=gdb
-        gdbargs="--args $exe"
-        # Enable for Emacs "M-x gdb" support. TODO: allow extra gdb arguments on command line.
-        # gdbargs="--annotate=3 $gdbargs"
-    fi
-fi
-
-if [ "$INTERPRETER" = "y" ]; then
-    INT_OPTS="-Xint"
-    if [ "$VERIFY" = "y" ] ; then
-      COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
-    else
-      COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=verify-none"
-      DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
-    fi
-fi
-
-if [ "$RELOCATE" = "y" ]; then
-  FLAGS="${FLAGS} -Xrelocate"
-  COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --include-patch-information"
-  # Run test sets a fairly draconian ulimit that we will likely blow right over
-  # since we are relocating. Get the total size of the /system/framework directory
-  # in 512 byte blocks and set it as the ulimit. This should be more than enough
-  # room.
-  if [ ! `uname` = "Darwin" ]; then  # TODO: Darwin doesn't support "du -B..."
-    ulimit -S $(du -c -B512 ${ANDROID_ROOT}/framework | tail -1 | cut -f1) || exit 1
-  fi
-else
-  FLAGS="${FLAGS} -Xnorelocate"
-  COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --no-include-patch-information"
-fi
-
-mkdir_cmd="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA"
-if [ "$PREBUILD" = "y" ]; then
-  prebuild_cmd="${ANDROID_HOST_OUT}/bin/dex2oatd $COMPILER_FLAGS --instruction-set=$ISA $BUILD_BOOT_OPT --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g")"
-else
-  prebuild_cmd="true"
-fi
-
-JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $DEX_VERIFY $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $main"
-if [ "$DEV_MODE" = "y" ]; then
-  if [ "$PREBUILD" = "y" ]; then
-    echo "$mkdir_cmd && $prebuild_cmd && $cmdline"
-  elif [ "$RELOCATE" = "y" ]; then
-    echo "$mkdir_cmd && $cmdline"
-  else
-    echo $cmdline
-  fi
-fi
-
-cd $ANDROID_BUILD_TOP
-$mkdir_cmd && $prebuild_cmd && $cmdline "$@"
diff --git a/test/etc/push-and-run-prebuilt-test-jar b/test/etc/push-and-run-prebuilt-test-jar
deleted file mode 100755
index 7353544..0000000
--- a/test/etc/push-and-run-prebuilt-test-jar
+++ /dev/null
@@ -1,222 +0,0 @@
-#!/bin/sh
-#
-# Run the code in test.jar on the device. The jar should contain a top-level
-# class named Main to run.
-
-msg() {
-    if [ "$QUIET" = "n" ]; then
-        echo "$@"
-    fi
-}
-
-ARCHITECTURES_32="(arm|x86|mips|none)"
-ARCHITECTURES_64="(arm64|x86_64|none)"
-ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
-RELOCATE="y"
-GDB="n"
-DEBUGGER="n"
-INTERPRETER="n"
-VERIFY="y"
-OPTIMIZE="y"
-ZYGOTE=""
-QUIET="n"
-DEV_MODE="n"
-INVOKE_WITH=""
-FLAGS=""
-TARGET_SUFFIX="32"
-GDB_TARGET_SUFFIX=""
-COMPILE_FLAGS=""
-SECONDARY_DEX=""
-DEX_VERIFY=""
-
-while true; do
-    if [ "x$1" = "x--quiet" ]; then
-        QUIET="y"
-        shift
-    elif [ "x$1" = "x--lib" ]; then
-        shift
-        if [ "x$1" = "x" ]; then
-            echo "$0 missing argument to --lib" 1>&2
-            exit 1
-        fi
-        LIB="$1"
-        shift
-    elif [ "x$1" = "x-Xcompiler-option" ]; then
-        shift
-        option="$1"
-        FLAGS="${FLAGS} -Xcompiler-option $option"
-        COMPILE_FLAGS="${COMPILE_FLAGS} $option"
-        shift
-    elif [ "x$1" = "x--runtime-option" ]; then
-        shift
-        option="$1"
-        FLAGS="${FLAGS} $option"
-        shift
-    elif [ "x$1" = "x--boot" ]; then
-        shift
-        BOOT_OPT="$1"
-        BUILD_BOOT_OPT="--boot-image=${1#-Ximage:}"
-        shift
-    elif [ "x$1" = "x--relocate" ]; then
-        RELOCATE="y"
-        shift
-    elif [ "x$1" = "x--no-relocate" ]; then
-        RELOCATE="n"
-        shift
-    elif [ "x$1" = "x--debug" ]; then
-        DEBUGGER="y"
-        shift
-    elif [ "x$1" = "x--gdb" ]; then
-        GDB="y"
-        DEV_MODE="y"
-        shift
-    elif [ "x$1" = "x--zygote" ]; then
-        ZYGOTE="--zygote"
-        msg "Spawning from zygote"
-        shift
-    elif [ "x$1" = "x--dev" ]; then
-        DEV_MODE="y"
-        shift
-    elif [ "x$1" = "x--interpreter" ]; then
-        INTERPRETER="y"
-        shift
-    elif [ "x$1" = "x--invoke-with" ]; then
-        shift
-        if [ "x$1" = "x" ]; then
-            echo "$0 missing argument to --invoke-with" 1>&2
-            exit 1
-        fi
-        if [ "x$INVOKE_WITH" = "x" ]; then
-            INVOKE_WITH="$1"
-        else
-            INVOKE_WITH="$INVOKE_WITH $1"
-        fi
-        shift
-    elif [ "x$1" = "x--no-verify" ]; then
-        VERIFY="n"
-        shift
-    elif [ "x$1" = "x--no-optimize" ]; then
-        OPTIMIZE="n"
-        shift
-    elif [ "x$1" = "x--" ]; then
-        shift
-        break
-    elif [ "x$1" = "x--64" ]; then
-        TARGET_SUFFIX="64"
-        GDB_TARGET_SUFFIX="64"
-        ARCHITECTURES_PATTERN="${ARCHITECTURES_64}"
-        shift
-    elif [ "x$1" = "x--secondary" ]; then
-        SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
-        shift
-    elif expr "x$1" : "x--" >/dev/null 2>&1; then
-        echo "unknown $0 option: $1" 1>&2
-        exit 1
-    else
-        break
-    fi
-done
-
-if [ "$ZYGOTE" = "" ]; then
-    if [ "$OPTIMIZE" = "y" ]; then
-        if [ "$VERIFY" = "y" ]; then
-            DEX_OPTIMIZE="-Xdexopt:verified"
-        else
-            DEX_OPTIMIZE="-Xdexopt:all"
-        fi
-        msg "Performing optimizations"
-    else
-        DEX_OPTIMIZE="-Xdexopt:none"
-        msg "Skipping optimizations"
-    fi
-
-    if [ "$VERIFY" = "y" ]; then
-        msg "Performing verification"
-    else
-        DEX_VERIFY="-Xverify:none"
-        msg "Skipping verification"
-    fi
-fi
-
-msg "------------------------------"
-
-ARCH=$(adb shell ls -F /data/dalvik-cache | grep -Ewo "${ARCHITECTURES_PATTERN}")
-if [ x"$ARCH" = "x" ]; then
-  echo "Unable to determine architecture"
-  exit 1
-fi
-
-if [ "$QUIET" = "n" ]; then
-  adb shell rm -r $DEX_LOCATION
-  adb shell mkdir -p $DEX_LOCATION
-  adb push $TEST_NAME.jar $DEX_LOCATION
-  adb push $TEST_NAME-ex.jar $DEX_LOCATION
-else
-  adb shell rm -r $DEX_LOCATION >/dev/null 2>&1
-  adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1
-  adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1
-  adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
-fi
-
-if [ "$DEBUGGER" = "y" ]; then
-  # Use this instead for ddms and connect by running 'ddms':
-  # DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
-  # TODO: add a separate --ddms option?
-
-  PORT=12345
-  msg "Waiting for jdb to connect:"
-  msg "    adb forward tcp:$PORT tcp:$PORT"
-  msg "    jdb -attach localhost:$PORT"
-  DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
-fi
-
-if [ "$GDB" = "y" ]; then
-    gdb="gdbserver$GDB_TARGET_SUFFIX :5039"
-    gdbargs="$exe"
-fi
-
-if [ "$INTERPRETER" = "y" ]; then
-    INT_OPTS="-Xint"
-    if [ "$VERIFY" = "y" ] ; then
-      COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
-    else
-      COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none"
-      DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
-    fi
-fi
-
-JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-
-if [ "$RELOCATE" = "y" ]; then
-    RELOCATE_OPT="-Xrelocate"
-    BUILD_RELOCATE_OPT="--runtime-arg -Xnorelocate"
-    COMPILE_FLAGS="${COMPILE_FLAGS} --include-patch-information"
-    FLAGS="${FLAGS} -Xcompiler-option --include-patch-information"
-else
-    RELOCATE_OPT="-Xnorelocate"
-    BUILD_RELOCATE_OPT="--runtime-arg -Xnorelocate"
-fi
-
-# This is due to the fact this cmdline can get longer than the longest allowed
-# adb command and there is no way to get the exit status from a adb shell
-# command.
-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$SECONDARY_DEX Main $@"
-cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
-echo "$cmdline" > $cmdfile
-
-if [ "$DEV_MODE" = "y" ]; then
-  echo $cmdline
-fi
-
-if [ "$QUIET" = "n" ]; then
-  adb push $cmdfile $DEX_LOCATION/cmdline.sh
-else
-  adb push $cmdfile $DEX_LOCATION/cmdline.sh > /dev/null 2>&1
-fi
-
-adb shell sh $DEX_LOCATION/cmdline.sh
-
-rm -f $cmdfile
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
deleted file mode 100755
index a6a4e78..0000000
--- a/test/etc/push-and-run-test-jar
+++ /dev/null
@@ -1,190 +0,0 @@
-#!/bin/sh
-#
-# Run the code in test.jar on the device. The jar should contain a top-level
-# class named Main to run.
-
-msg() {
-    if [ "$QUIET" = "n" ]; then
-        echo "$@"
-    fi
-}
-
-RELOCATE="y"
-GDB="n"
-DEBUGGER="n"
-INTERPRETER="n"
-VERIFY="y"
-OPTIMIZE="y"
-ZYGOTE=""
-QUIET="n"
-DEV_MODE="n"
-INVOKE_WITH=""
-FLAGS=""
-TARGET_SUFFIX="32"
-GDB_TARGET_SUFFIX=""
-SECONDARY_DEX=""
-DEX_VERIFY=""
-
-while true; do
-    if [ "x$1" = "x--quiet" ]; then
-        QUIET="y"
-        shift
-    elif [ "x$1" = "x--lib" ]; then
-        shift
-        if [ "x$1" = "x" ]; then
-            echo "$0 missing argument to --lib" 1>&2
-            exit 1
-        fi
-        LIB="$1"
-        shift
-    elif [ "x$1" = "x-Xcompiler-option" ]; then
-        shift
-        option="$1"
-        FLAGS="${FLAGS} -Xcompiler-option $option"
-        shift
-    elif [ "x$1" = "x--runtime-option" ]; then
-        shift
-        option="$1"
-        FLAGS="${FLAGS} $option"
-        shift
-    elif [ "x$1" = "x--boot" ]; then
-        shift
-        BOOT_OPT="$1"
-        shift
-    elif [ "x$1" = "x--debug" ]; then
-        DEBUGGER="y"
-        shift
-    elif [ "x$1" = "x--gdb" ]; then
-        GDB="y"
-        DEV_MODE="y"
-        shift
-    elif [ "x$1" = "x--zygote" ]; then
-        ZYGOTE="--zygote"
-        msg "Spawning from zygote"
-        shift
-    elif [ "x$1" = "x--dev" ]; then
-        DEV_MODE="y"
-        shift
-    elif [ "x$1" = "x--relocate" ]; then
-        RELOCATE="y"
-        shift
-    elif [ "x$1" = "x--no-relocate" ]; then
-        RELOCATE="n"
-        shift
-    elif [ "x$1" = "x--interpreter" ]; then
-        INTERPRETER="y"
-        shift
-    elif [ "x$1" = "x--invoke-with" ]; then
-        shift
-        if [ "x$1" = "x" ]; then
-            echo "$0 missing argument to --invoke-with" 1>&2
-            exit 1
-        fi
-        if [ "x$INVOKE_WITH" = "x" ]; then
-            INVOKE_WITH="$1"
-        else
-            INVOKE_WITH="$INVOKE_WITH $1"
-        fi
-        shift
-    elif [ "x$1" = "x--no-verify" ]; then
-        VERIFY="n"
-        shift
-    elif [ "x$1" = "x--no-optimize" ]; then
-        OPTIMIZE="n"
-        shift
-    elif [ "x$1" = "x--" ]; then
-        shift
-        break
-    elif [ "x$1" = "x--64" ]; then
-        TARGET_SUFFIX="64"
-        GDB_TARGET_SUFFIX="64"
-        shift
-    elif [ "x$1" = "x--secondary" ]; then
-        SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
-        shift
-    elif expr "x$1" : "x--" >/dev/null 2>&1; then
-        echo "unknown $0 option: $1" 1>&2
-        exit 1
-    else
-        break
-    fi
-done
-
-if [ "$ZYGOTE" = "" ]; then
-    if [ "$OPTIMIZE" = "y" ]; then
-        if [ "$VERIFY" = "y" ]; then
-            DEX_OPTIMIZE="-Xdexopt:verified"
-        else
-            DEX_OPTIMIZE="-Xdexopt:all"
-        fi
-        msg "Performing optimizations"
-    else
-        DEX_OPTIMIZE="-Xdexopt:none"
-        msg "Skipping optimizations"
-    fi
-
-    if [ "$VERIFY" = "y" ]; then
-        msg "Performing verification"
-    else
-        DEX_VERIFY="-Xverify:none"
-        msg "Skipping verification"
-    fi
-fi
-
-msg "------------------------------"
-
-if [ "$QUIET" = "n" ]; then
-  adb shell rm -r $DEX_LOCATION
-  adb shell mkdir -p $DEX_LOCATION
-  adb push $TEST_NAME.jar $DEX_LOCATION
-  adb push $TEST_NAME-ex.jar $DEX_LOCATION
-else
-  adb shell rm -r $DEX_LOCATION >/dev/null 2>&1
-  adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1
-  adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1
-  adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
-fi
-
-if [ "$DEBUGGER" = "y" ]; then
-  # Use this instead for ddms and connect by running 'ddms':
-  # DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
-  # TODO: add a separate --ddms option?
-
-  PORT=12345
-  msg "Waiting for jdb to connect:"
-  msg "    adb forward tcp:$PORT tcp:$PORT"
-  msg "    jdb -attach localhost:$PORT"
-  DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
-fi
-
-if [ "$GDB" = "y" ]; then
-    gdb="gdbserver$GDB_TARGET_SUFFIX :5039"
-    gdbargs="$exe"
-fi
-
-if [ "$INTERPRETER" = "y" ]; then
-    INT_OPTS="-Xint"
-    if [ "$VERIFY" = "y" ] ; then
-      COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
-    else
-      COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=verify-none"
-      DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
-    fi
-fi
-
-JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-
-if [ "$RELOCATE" = "y" ]; then
-  RELOCATE_OPT="-Xrelocate"
-  FLAGS="${FLAGS} -Xcompiler-option --include-patch-information"
-else
-  RELOCATE_OPT="-Xnorelocate"
-fi
-
-cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
-    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $DEX_VERIFY $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX Main"
-if [ "$DEV_MODE" = "y" ]; then
-  echo $cmdline "$@"
-fi
-
-adb shell $cmdline "$@"
diff --git a/test/etc/reference-run-test-classes b/test/etc/reference-run-test-classes
deleted file mode 100755
index 6f10f5a..0000000
--- a/test/etc/reference-run-test-classes
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/sh
-#
-# Run the code in a classes directory on a host-local reference virtual
-# machine. The jar should contain a top-level class named Main to run.
-#
-# Options:
-#   --quiet       -- don't chatter
-#   --debug       -- wait for debugger to attach
-#   --no-verify   -- turn off verification (on by default)
-#   --dev         -- development mode
-
-msg() {
-    if [ "$QUIET" = "n" ]; then
-        echo "$@"
-    fi
-}
-
-DEBUG="n"
-QUIET="n"
-VERIFY="y"
-
-while true; do
-    if [ "x$1" = "x--quiet" ]; then
-        QUIET="y"
-        shift
-    elif [ "x$1" = "x--debug" ]; then
-        DEBUG="y"
-        shift
-    elif [ "x$1" = "x--no-verify" ]; then
-        VERIFY="n"
-        shift
-    elif [ "x$1" = "x--dev" ]; then
-        # not used; ignore
-        shift
-    elif [ "x$1" = "x--" ]; then
-        shift
-        break
-    elif expr "x$1" : "x--" >/dev/null 2>&1; then
-        echo "unknown $0 option: $1" 1>&2
-        exit 1
-    else
-        break
-    fi
-done
-
-if [ "$VERIFY" = "y" ]; then
-    VERIFY_ARG="-Xverify:all"
-    msg "Performing verification"
-else
-    VERIFY_ARG="-Xverify:none"
-    msg "Skipping verification"
-fi
-
-if [ "$DEBUG" = "y" ]; then
-    PORT=8000
-    msg "Waiting for jdb to connect:"
-    msg "    jdb -attach localhost:$PORT"
-    DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
-fi
-
-${JAVA} ${DEBUG_OPTS} ${VERIFY_ARG} -classpath classes Main "$@"
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
new file mode 100755
index 0000000..d2cd8ab
--- /dev/null
+++ b/test/etc/run-test-jar
@@ -0,0 +1,403 @@
+#!/bin/bash
+#
+# Runner for an individual run-test.
+
+msg() {
+    if [ "$QUIET" = "n" ]; then
+        echo "$@"
+    fi
+}
+
+ANDROID_ROOT="/system"
+ARCHITECTURES_32="(arm|x86|mips|none)"
+ARCHITECTURES_64="(arm64|x86_64|none)"
+ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
+BOOT_IMAGE=""
+COMPILE_FLAGS=""
+DALVIKVM="dalvikvm32"
+DEBUGGER="n"
+DEV_MODE="n"
+DEX2OAT=""
+FALSE_BIN="/system/bin/false"
+FLAGS=""
+GDB=""
+GDB_ARGS=""
+GDB_SERVER="gdbserver"
+HAVE_IMAGE="y"
+HOST="n"
+INTERPRETER="n"
+INVOKE_WITH=""
+ISA=x86
+LIBRARY_DIRECTORY="lib"
+MAIN=""
+OPTIMIZE="y"
+PATCHOAT=""
+PREBUILD="y"
+QUIET="n"
+RELOCATE="y"
+SECONDARY_DEX=""
+TIME_OUT="y"
+TIME_OUT_VALUE=5m
+USE_GDB="n"
+USE_JVM="n"
+VERIFY="y"
+ZYGOTE=""
+DEX_VERIFY=""
+
+while true; do
+    if [ "x$1" = "x--quiet" ]; then
+        QUIET="y"
+        shift
+    elif [ "x$1" = "x--lib" ]; then
+        shift
+        if [ "x$1" = "x" ]; then
+            echo "$0 missing argument to --lib" 1>&2
+            exit 1
+        fi
+        LIB="$1"
+        shift
+    elif [ "x$1" = "x-Xcompiler-option" ]; then
+        shift
+        option="$1"
+        FLAGS="${FLAGS} -Xcompiler-option $option"
+        COMPILE_FLAGS="${COMPILE_FLAGS} $option"
+        shift
+    elif [ "x$1" = "x--runtime-option" ]; then
+        shift
+        option="$1"
+        FLAGS="${FLAGS} $option"
+        shift
+    elif [ "x$1" = "x--boot" ]; then
+        shift
+        BOOT_IMAGE="$1"
+        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--prebuild" ]; then
+        PREBUILD="y"
+        shift
+    elif [ "x$1" = "x--host" ]; then
+        HOST="y"
+        ANDROID_ROOT="$ANDROID_HOST_OUT"
+        shift
+    elif [ "x$1" = "x--no-prebuild" ]; then
+        PREBUILD="n"
+        shift
+    elif [ "x$1" = "x--no-image" ]; then
+        HAVE_IMAGE="n"
+        shift
+    elif [ "x$1" = "x--secondary" ]; then
+        SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
+        shift
+    elif [ "x$1" = "x--debug" ]; then
+        DEBUGGER="y"
+        TIME_OUT="n"
+        shift
+    elif [ "x$1" = "x--gdb" ]; then
+        USE_GDB="y"
+        DEV_MODE="y"
+        TIME_OUT="n"
+        shift
+    elif [ "x$1" = "x--zygote" ]; then
+        ZYGOTE="-Xzygote"
+        msg "Spawning from zygote"
+        shift
+    elif [ "x$1" = "x--dev" ]; then
+        DEV_MODE="y"
+        shift
+    elif [ "x$1" = "x--interpreter" ]; then
+        INTERPRETER="y"
+        shift
+    elif [ "x$1" = "x--jvm" ]; then
+        USE_JVM="y"
+        shift
+    elif [ "x$1" = "x--invoke-with" ]; then
+        shift
+        if [ "x$1" = "x" ]; then
+            echo "$0 missing argument to --invoke-with" 1>&2
+            exit 1
+        fi
+        if [ "x$INVOKE_WITH" = "x" ]; then
+            INVOKE_WITH="$1"
+        else
+            INVOKE_WITH="$INVOKE_WITH $1"
+        fi
+        shift
+    elif [ "x$1" = "x--no-verify" ]; then
+        VERIFY="n"
+        shift
+    elif [ "x$1" = "x--no-optimize" ]; then
+        OPTIMIZE="n"
+        shift
+    elif [ "x$1" = "x--android-root" ]; then
+        shift
+        ANDROID_ROOT="$1"
+        shift
+    elif [ "x$1" = "x--" ]; then
+        shift
+        break
+    elif [ "x$1" = "x--64" ]; then
+        ISA="x86_64"
+        GDB_SERVER="gdbserver64"
+        DALVIKVM="dalvikvm64"
+        LIBRARY_DIRECTORY="lib64"
+        ARCHITECTURES_PATTERN="${ARCHITECTURES_64}"
+        shift
+    elif [ "x$1" = "x--pic-test" ]; then
+        FLAGS="${FLAGS} -Xcompiler-option --compile-pic"
+        COMPILE_FLAGS="${COMPILE_FLAGS} --compile-pic"
+        shift
+    elif expr "x$1" : "x--" >/dev/null 2>&1; then
+        echo "unknown $0 option: $1" 1>&2
+        exit 1
+    else
+        break
+    fi
+done
+
+if [ "x$1" = "x" ] ; then
+  MAIN="Main"
+else
+  MAIN="$1"
+fi
+
+if [ "$ZYGOTE" = "" ]; then
+    if [ "$OPTIMIZE" = "y" ]; then
+        if [ "$VERIFY" = "y" ]; then
+            DEX_OPTIMIZE="-Xdexopt:verified"
+        else
+            DEX_OPTIMIZE="-Xdexopt:all"
+        fi
+        msg "Performing optimizations"
+    else
+        DEX_OPTIMIZE="-Xdexopt:none"
+        msg "Skipping optimizations"
+    fi
+
+    if [ "$VERIFY" = "y" ]; then
+        JVM_VERIFY_ARG="-Xverify:all"
+        msg "Performing verification"
+    else
+        DEX_VERIFY="-Xverify:none"
+        JVM_VERIFY_ARG="-Xverify:none"
+        msg "Skipping verification"
+    fi
+fi
+
+msg "------------------------------"
+
+if [ "$DEBUGGER" = "y" ]; then
+  # Use this instead for ddms and connect by running 'ddms':
+  # DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
+  # TODO: add a separate --ddms option?
+
+  PORT=12345
+  msg "Waiting for jdb to connect:"
+  if [ "$HOST" = "n" ]; then
+    msg "    adb forward tcp:$PORT tcp:$PORT"
+  fi
+  msg "    jdb -attach localhost:$PORT"
+  DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+fi
+
+if [ "$USE_JVM" = "y" ]; then
+  ${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -classpath classes $MAIN "$@"
+  exit
+fi
+
+
+if [ "$HAVE_IMAGE" = "n" ]; then
+    DALVIKVM_BOOT_OPT="-Ximage:/system/non-existant/core.art"
+else
+    DALVIKVM_BOOT_OPT="-Ximage:${BOOT_IMAGE}"
+fi
+
+
+if [ "$USE_GDB" = "y" ]; then
+  if [ "$HOST" = "n" ]; then
+    GDB="$GDB_SERVER :5039"
+  else
+    if [ `uname` = "Darwin" ]; then
+        GDB=lldb
+        GDB_ARGS="-- $DALVIKVM"
+        DALVIKVM=
+    else
+        GDB=gdb
+        GDB_ARGS="--args $DALVIKVM"
+        # Enable for Emacs "M-x gdb" support. TODO: allow extra gdb arguments on command line.
+        # gdbargs="--annotate=3 $gdbargs"
+    fi
+  fi
+fi
+
+if [ "$INTERPRETER" = "y" ]; then
+    INT_OPTS="-Xint"
+    if [ "$VERIFY" = "y" ] ; then
+      COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
+    else
+      COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none"
+      DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
+    fi
+fi
+
+JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+
+if [ "$RELOCATE" = "y" ]; then
+    COMPILE_FLAGS="${COMPILE_FLAGS} --include-patch-information --runtime-arg -Xnorelocate"
+    FLAGS="${FLAGS} -Xrelocate -Xcompiler-option --include-patch-information"
+    if [ "$HOST" = "y" ]; then
+        # Run test sets a fairly draconian ulimit that we will likely blow right over
+        # since we are relocating. Get the total size of the /system/framework directory
+        # in 512 byte blocks and set it as the ulimit. This should be more than enough
+        # room.
+        if [ ! `uname` = "Darwin" ]; then  # TODO: Darwin doesn't support "du -B..."
+          ulimit -S $(du -c -B512 ${ANDROID_HOST_OUT}/framework | tail -1 | cut -f1) || exit 1
+        fi
+    fi
+else
+    FLAGS="$FLAGS -Xnorelocate"
+    COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate"
+fi
+
+if [ "$HOST" = "n" ]; then
+  ISA=$(adb shell ls -F /data/dalvik-cache | grep -Ewo "${ARCHITECTURES_PATTERN}")
+  if [ x"$ISA" = "x" ]; then
+    echo "Unable to determine architecture"
+    exit 1
+  fi
+fi
+
+dex2oat_cmdline="true"
+mkdir_cmdline="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA"
+
+if [ "$PREBUILD" = "y" ]; then
+  dex2oat_cmdline="$INVOKE_WITH $ANDROID_ROOT/bin/dex2oatd \
+                      $COMPILE_FLAGS \
+                      --boot-image=${BOOT_IMAGE} \
+                      --dex-file=$DEX_LOCATION/$TEST_NAME.jar \
+                      --oat-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") \
+                      --instruction-set=$ISA"
+fi
+
+dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \
+                  $GDB_ARGS \
+                  $FLAGS \
+                  $DEX_VERIFY \
+                  -XXlib:$LIB \
+                  $PATCHOAT \
+                  $DEX2OAT \
+                  $ZYGOTE \
+                  $JNI_OPTS \
+                  $INT_OPTS \
+                  $DEBUGGER_OPTS \
+                  $DALVIKVM_BOOT_OPT \
+                  -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN"
+
+
+if [ "$HOST" = "n" ]; then
+    adb root > /dev/null
+    adb wait-for-device
+    if [ "$QUIET" = "n" ]; then
+      adb shell rm -r $DEX_LOCATION
+      adb shell mkdir -p $DEX_LOCATION
+      adb push $TEST_NAME.jar $DEX_LOCATION
+      adb push $TEST_NAME-ex.jar $DEX_LOCATION
+    else
+      adb shell rm -r $DEX_LOCATION >/dev/null 2>&1
+      adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1
+      adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1
+      adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
+    fi
+
+    LD_LIBRARY_PATH=
+    if [ "$ANDROID_ROOT" != "/system" ]; then
+      # Current default installation is dalvikvm 64bits and dex2oat 32bits,
+      # so we can only use LD_LIBRARY_PATH when testing on a local
+      # installation.
+      LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY
+    fi
+
+    # Create a script with the command. The command can get longer than the longest
+    # allowed adb command and there is no way to get the exit status from a adb shell
+    # command.
+    cmdline="cd $DEX_LOCATION && \
+             export ANDROID_DATA=$DEX_LOCATION && \
+             export DEX_LOCATION=$DEX_LOCATION && \
+             export ANDROID_ROOT=$ANDROID_ROOT && \
+             export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
+             $mkdir_cmdline && \
+             $dex2oat_cmdline && \
+             $dalvikvm_cmdline"
+
+    cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
+    echo "$cmdline" > $cmdfile
+
+    if [ "$DEV_MODE" = "y" ]; then
+      echo $cmdline
+    fi
+
+    if [ "$QUIET" = "n" ]; then
+      adb push $cmdfile $DEX_LOCATION/cmdline.sh
+    else
+      adb push $cmdfile $DEX_LOCATION/cmdline.sh > /dev/null 2>&1
+    fi
+
+    adb shell sh $DEX_LOCATION/cmdline.sh
+
+    rm -f $cmdfile
+else
+    export ANDROID_PRINTF_LOG=brief
+    if [ "$DEV_MODE" = "y" ]; then
+        export ANDROID_LOG_TAGS='*:d'
+    else
+        export ANDROID_LOG_TAGS='*:s'
+    fi
+    export ANDROID_DATA="$DEX_LOCATION"
+    export ANDROID_ROOT="${ANDROID_ROOT}"
+    export LD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
+    export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
+    export PATH="$PATH:${ANDROID_ROOT}/bin"
+
+    cmdline="$dalvikvm_cmdline"
+
+    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_cmdline && $dex2oat_cmdline && $cmdline"
+      elif [ "$RELOCATE" = "y" ]; then
+        echo "$mkdir_cmdline && $cmdline"
+      else
+        echo $cmdline
+      fi
+    fi
+
+    cd $ANDROID_BUILD_TOP
+
+    $mkdir_cmdline || exit 1
+    $dex2oat_cmdline || exit 2
+
+    if [ "$USE_GDB" = "y" ]; then
+      # When running under gdb, we cannot do piping and grepping...
+      $cmdline "$@"
+    else
+      $cmdline "$@" 2>&1
+      # 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
+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..b43668d 100755
--- a/test/run-test
+++ b/test/run-test
@@ -17,6 +17,7 @@
 # Set up prog to be the path of this script, including following symlinks,
 # and set up progdir to be the fully-qualified pathname of its directory.
 prog="$0"
+args="$@"
 while [ -h "${prog}" ]; do
     newProg=`/bin/ls -ld "${prog}"`
     newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
@@ -41,7 +42,7 @@
 
 export JAVA="java"
 export JAVAC="javac -g"
-export RUN="${progdir}/etc/push-and-run-test-jar"
+export RUN="${progdir}/etc/run-test-jar"
 export DEX_LOCATION=/data/run-test/${test_dir}
 export NEED_DEX="true"
 
@@ -55,6 +56,16 @@
   export JASMIN="jasmin"
 fi
 
+# If smali was not set by the environment variable, assume it is in the path.
+if [ -z "$SMALI" ]; then
+  export SMALI="smali"
+fi
+
+# If dexmerger was not set by the environment variable, assume it is in the path.
+if [ -z "$DXMERGER" ]; then
+  export DXMERGER="dexmerger"
+fi
+
 
 info="info.txt"
 build="build"
@@ -77,17 +88,29 @@
 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"
+image_suffix=""
+pic_image_suffix=""
+android_root="/system"
 
 while true; do
     if [ "x$1" = "x--host" ]; then
         target_mode="no"
         DEX_LOCATION=$tmp_dir
+        run_args="${run_args} --host"
         shift
     elif [ "x$1" = "x--jvm" ]; then
         target_mode="no"
         runtime="jvm"
+        prebuild_mode="no"
         NEED_DEX="false"
+        run_args="${run_args} --jvm"
         shift
     elif [ "x$1" = "x-O" ]; then
         lib="libart.so"
@@ -96,6 +119,21 @@
         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--pic-image" ]; then
+        pic_image_suffix="-pic"
+        shift
+    elif [ "x$1" = "x--pic-test" ]; then
+        run_args="${run_args} --pic-test"
+        shift
     elif [ "x$1" = "x--relocate" ]; then
         relocate="yes"
         shift
@@ -103,11 +141,21 @@
         relocate="no"
         shift
     elif [ "x$1" = "x--prebuild" ]; then
+        run_args="${run_args} --prebuild"
         prebuild_mode="yes"
         shift;
     elif [ "x$1" = "x--no-prebuild" ]; then
+        run_args="${run_args} --no-prebuild"
         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"
@@ -135,6 +183,11 @@
         shift
     elif [ "x$1" = "x--interpreter" ]; then
         run_args="${run_args} --interpreter"
+        image_suffix="-interpreter"
+        shift
+    elif [ "x$1" = "x--optimizing" ]; then
+        run_args="${run_args} -Xcompiler-option --compiler-backend=Optimizing"
+        image_suffix="-optimizing"
         shift
     elif [ "x$1" = "x--no-verify" ]; then
         run_args="${run_args} --no-verify"
@@ -171,6 +224,16 @@
             break
         fi
         shift
+    elif [ "x$1" = "x--android-root" ]; then
+        shift
+        if [ "x$1" = "x" ]; then
+            echo "$0 missing argument to --android-root" 1>&2
+            usage="yes"
+            break
+        fi
+        android_root="$1"
+        run_args="${run_args} --android-root $1"
+        shift
     elif [ "x$1" = "x--update" ]; then
         update_mode="yes"
         shift
@@ -202,9 +265,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
@@ -223,21 +296,19 @@
 
 if [ "$target_mode" = "no" ]; then
     if [ "$runtime" = "jvm" ]; then
-        RUN="${progdir}/etc/reference-run-test-classes"
         if [ "$prebuild_mode" = "yes" ]; then
             echo "--prebuild with --jvm is unsupported";
             exit 1;
         fi
-    else
-        RUN="${progdir}/etc/host-run-test-jar"
-        if [ "$prebuild_mode" = "yes" ]; then
-            run_args="${run_args} --prebuild"
-        fi
     fi
-else
-    if [ "$prebuild_mode" = "yes" ]; then
-        RUN="${progdir}/etc/push-and-run-prebuilt-test-jar"
-    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
@@ -254,19 +325,19 @@
     fi
 elif [ "$runtime" = "art" ]; then
     if [ "$target_mode" = "no" ]; then
-	# ANDROID_BUILD_TOP and ANDROID_HOST_OUT are not set in a build environment.
+        # ANDROID_BUILD_TOP and ANDROID_HOST_OUT are not set in a build environment.
         if [ -z "$ANDROID_BUILD_TOP" ]; then
-	    export ANDROID_BUILD_TOP=$oldwd
+            export ANDROID_BUILD_TOP=$oldwd
         fi
         if [ -z "$ANDROID_HOST_OUT" ]; then
-	    export ANDROID_HOST_OUT=$ANDROID_BUILD_TOP/out/host/linux-x86
+            export ANDROID_HOST_OUT=$ANDROID_BUILD_TOP/out/host/linux-x86
         fi
-        run_args="${run_args} --boot -Ximage:${ANDROID_HOST_OUT}/framework/core.art"
+        run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${pic_image_suffix}.art"
         run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}"
     else
         guess_arch_name
         run_args="${run_args} --runtime-option -Djava.library.path=/data/art-test/${target_arch_name}"
-        run_args="${run_args} --boot -Ximage:/data/art-test/core.art"
+        run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}.art"
     fi
     if [ "$relocate" = "yes" ]; then
       run_args="${run_args} --relocate"
@@ -275,6 +346,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="${android_root}/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"
@@ -313,35 +408,41 @@
         echo '  Omitting the test name or specifying "-" will use the' \
              "current directory."
         echo "  Runtime Options:"
-        echo "    -O                   Run non-debug rather than debug build (off by default)."
-        echo "    -Xcompiler-option    Pass an option to the compiler."
-        echo "    --runtime-option     Pass an option to the runtime."
-        echo "    --debug              Wait for a debugger to attach."
-        echo "    --gdb                Run under gdb; incompatible with some tests."
-        echo "    --build-only         Build test files only (off by default)."
-        echo "    --interpreter        Enable interpreter only mode (off by default)."
-        echo "    --no-verify          Turn off verification (on by default)."
-        echo "    --no-optimize        Turn off optimization (on by default)."
-        echo "    --no-precise         Turn off precise GC (on by default)."
-        echo "    --zygote             Spawn the process from the Zygote." \
+        echo "    -O                    Run non-debug rather than debug build (off by default)."
+        echo "    -Xcompiler-option     Pass an option to the compiler."
+        echo "    --runtime-option      Pass an option to the runtime."
+        echo "    --debug               Wait for a debugger to attach."
+        echo "    --gdb                 Run under gdb; incompatible with some tests."
+        echo "    --build-only          Build test files only (off by default)."
+        echo "    --interpreter         Enable interpreter only mode (off by default)."
+        echo "    --optimizing          Enable optimizing compiler (off by default)."
+        echo "    --no-verify           Turn off verification (on by default)."
+        echo "    --no-optimize         Turn off optimization (on by default)."
+        echo "    --no-precise          Turn off precise GC (on by default)."
+        echo "    --zygote              Spawn the process from the Zygote." \
              "If used, then the"
-        echo "                         other runtime options are ignored."
-        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."
-        echo "    --relocate           Force the use of relocating in the test, making"
-        echo "                         the image and oat files be relocated to a random"
-        echo "                         address before running. (default)"
-        echo "    --no-relocate        Force the use of no relocating in the test"
-        echo "    --host               Use the host-mode virtual machine."
-        echo "    --invoke-with        Pass --invoke-with option to runtime."
-        echo "    --dalvik             Use Dalvik (off by default)."
-        echo "    --jvm                Use a host-local RI virtual machine."
-        echo "    --output-path [path] Location where to store the build" \
+        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."
+        echo "    --relocate            Force the use of relocating in the test, making"
+        echo "                          the image and oat files be relocated to a random"
+        echo "                          address before running. (default)"
+        echo "    --no-relocate         Force the use of no relocating in the test"
+        echo "    --host                Use the host-mode virtual machine."
+        echo "    --invoke-with         Pass --invoke-with option to runtime."
+        echo "    --dalvik              Use Dalvik (off by default)."
+        echo "    --jvm                 Use a host-local RI virtual machine."
+        echo "    --output-path [path]  Location where to store the build" \
              "files."
-        echo "    --64                 Run the test in 64-bit mode"
-        echo "    --trace              Run with method tracing"
-        echo "    --always-clean       Delete the test files even if the test fails."
+        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."
+        echo "    --android-root [path] The path on target for the android root. (/system by default)."
     ) 1>&2
     exit 1
 fi
@@ -394,9 +495,6 @@
   file_size_limit=5120
 elif echo "$test_dir" | grep 083; then
   file_size_limit=5120
-elif echo "$test_dir" | grep 115; then
-# Native bridge test copies libarttest.so into its directory, which needs 2MB already.
-  file_size_limit=5120
 fi
 if ! ulimit -S "$file_size_limit"; then
    echo "ulimit file size setting failed"
@@ -452,7 +550,14 @@
         "./${run}" $run_args "$@" >"$output" 2>&1
     else
         cp "$build_output" "$output"
-        echo "build exit status: $build_exit" >>"$output"
+        echo "Failed to build in tmpdir=${tmp_dir} from oldwd=${oldwd} and cwd=`pwd`" >> "$output"
+        echo "Non-canonical tmpdir was ${noncanonical_tmp_dir}" >> "$output"
+        echo "Args: ${args}" >> "$output"
+        echo "build exit status: $build_exit" >> "$output"
+        max_name_length=$(getconf NAME_MAX ${tmp_dir})
+        echo "Max filename (NAME_MAX): ${max_name_length}" >> "$output"
+        max_path_length=$(getconf PATH_MAX ${tmp_dir})
+        echo "Max pathlength (PATH_MAX): ${max_path_length}" >> "$output"
     fi
     ./$check_cmd "$expected" "$output"
     if [ "$?" = "0" ]; then
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..af96b47
--- 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,19 @@
     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 [ "$1" = "--perf" ]; then
+    PERF="record"
+    shift
+  elif [ "$1" = "--perf-report" ]; then
+    PERF="report"
     shift
   elif expr "$1" : "--" >/dev/null 2>&1; then
     echo "unknown option: $1" 1>&2
@@ -33,39 +67,37 @@
   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)"
+if [ z"$PERF" != z ]; then
+  invoke_with="perf record -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with"
+fi
 
-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 \
+    -Xcompiler-option --include-debug-symbols \
+    "$@"
+
 EXIT_STATUS=$?
-rm -rf $ANDROID_DATA
+
+if [ z"$PERF" != z ]; then
+  if [ z"$PERF" = zreport ]; then
+    perf report -i $ANDROID_DATA/perf.data
+  fi
+  echo "Perf data saved in: $ANDROID_DATA/perf.data"
+else
+  rm -rf $ANDROID_DATA
+fi
+
 exit $EXIT_STATUS
diff --git a/tools/generate-operator-out.py b/tools/generate-operator-out.py
index f666ad1..2b57222 100755
--- a/tools/generate-operator-out.py
+++ b/tools/generate-operator-out.py
@@ -39,6 +39,7 @@
 def ProcessFile(filename):
   lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
   in_enum = False
+  is_enum_private = False
   is_enum_class = False
   line_number = 0
   
@@ -57,15 +58,16 @@
         
         # Except when it's private
         if m.group(3) is not None:
-          continue
-        
-        is_enum_class = m.group(1) is not None
-        enum_name = m.group(2)
-        if len(enclosing_classes) > 0:
-          enum_name = '::'.join(enclosing_classes) + '::' + enum_name
-        _ENUMS[enum_name] = []
-        _NAMESPACES[enum_name] = '::'.join(namespaces)
-        _ENUM_CLASSES[enum_name] = is_enum_class
+          is_enum_private = True
+        else:
+          is_enum_private = False
+          is_enum_class = m.group(1) is not None
+          enum_name = m.group(2)
+          if len(enclosing_classes) > 0:
+            enum_name = '::'.join(enclosing_classes) + '::' + enum_name
+          _ENUMS[enum_name] = []
+          _NAMESPACES[enum_name] = '::'.join(namespaces)
+          _ENUM_CLASSES[enum_name] = is_enum_class
         in_enum = True
         continue
 
@@ -80,11 +82,11 @@
         continue
 
       # Is this the start or end of an enclosing class or struct?
-      m = re.compile(r'^(?:class|struct)(?: MANAGED)? (\S+).* \{').search(raw_line)
+      m = re.compile(r'^\s*(?:class|struct)(?: MANAGED)?(?: PACKED\([0-9]\))? (\S+).* \{').search(raw_line)
       if m:
         enclosing_classes.append(m.group(1))
         continue
-      m = re.compile(r'^\};').search(raw_line)
+      m = re.compile(r'^\s*\}( .*)?;').search(raw_line)
       if m:
         enclosing_classes = enclosing_classes[0:len(enclosing_classes) - 1]
         continue
@@ -99,6 +101,9 @@
       in_enum = False
       continue
 
+    if is_enum_private:
+      continue
+
     # The only useful thing in comments is the <<alternate text>> syntax for
     # overriding the default enum value names. Pull that out...
     enum_text = None
@@ -146,6 +151,7 @@
 
     # There shouldn't be anything left.
     if len(rest):
+      sys.stderr.write('%s\n' % (rest))
       Confused(filename, line_number, raw_line)
 
     if len(enclosing_classes) > 0:
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