Merge "ART: Skip duplicate classes during compilation"
diff --git a/Android.bp b/Android.bp
index e09b774..34a6469 100644
--- a/Android.bp
+++ b/Android.bp
@@ -10,13 +10,12 @@
     "libz",
     "libbacktrace",
     "libcutils",
-    "libunwindbacktrace",
-    "libunwind",
     "libunwindstack",
     "libutils",
     "libbase",
     "liblz4",
     "liblzma",
+    "libmetricslogger_static",
 ]
 
 subdirs = [
@@ -37,6 +36,7 @@
     "imgdiag",
     "libartbase",
     "libdexfile",
+    "libprofile",
     "oatdump",
     "openjdkjvm",
     "openjdkjvmti",
diff --git a/Android.mk b/Android.mk
index 47bcaac..1c94629 100644
--- a/Android.mk
+++ b/Android.mk
@@ -110,22 +110,33 @@
 
 # Sync test files to the target, depends upon all things that must be pushed to the target.
 .PHONY: test-art-target-sync
-# Check if we need to sync. In case ART_TEST_ANDROID_ROOT is not empty,
-# the code below uses 'adb push' instead of 'adb sync', which does not
-# check if the files on the device have changed.
+# Check if we need to sync. In case ART_TEST_CHROOT or ART_TEST_ANDROID_ROOT
+# is not empty, the code below uses 'adb push' instead of 'adb sync',
+# which does not check if the files on the device have changed.
+# TODO: Remove support for ART_TEST_ANDROID_ROOT when it is no longer needed.
 ifneq ($(ART_TEST_NO_SYNC),true)
+# Sync system and data partitions.
 ifeq ($(ART_TEST_ANDROID_ROOT),)
+ifeq ($(ART_TEST_CHROOT),)
 test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
 	$(TEST_ART_ADB_ROOT_AND_REMOUNT)
 	adb sync system && adb sync data
 else
 test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
 	$(TEST_ART_ADB_ROOT_AND_REMOUNT)
-	adb wait-for-device push $(PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT)
-# Push the contents of the `data` dir into `/data` on the device.  If
-# `/data` already exists on the device, it is not overwritten, but its
-# contents are updated.
-	adb push $(PRODUCT_OUT)/data /
+	adb wait-for-device
+	adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)/
+	adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/
+endif
+else
+test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
+	$(TEST_ART_ADB_ROOT_AND_REMOUNT)
+	adb wait-for-device
+	adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)$(ART_TEST_ANDROID_ROOT)
+# Push the contents of the `data` dir into `$(ART_TEST_CHROOT)/data` on the device (note
+# that $(ART_TEST_CHROOT) can be empty).  If `$(ART_TEST_CHROOT)/data` already exists on
+# the device, it is not overwritten, but its content is updated.
+	adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/
 endif
 endif
 
@@ -234,19 +245,6 @@
 test-art-host-dexdump: $(addprefix $(HOST_OUT_EXECUTABLES)/, dexdump2 dexlist)
 	ANDROID_HOST_OUT=$(realpath $(HOST_OUT)) art/test/dexdump/run-all-tests
 
-# Valgrind.
-.PHONY: valgrind-test-art-host
-valgrind-test-art-host: valgrind-test-art-host-gtest
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
-.PHONY: valgrind-test-art-host32
-valgrind-test-art-host32: valgrind-test-art-host-gtest32
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
-.PHONY: valgrind-test-art-host64
-valgrind-test-art-host64: valgrind-test-art-host-gtest64
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
 ########################################################################
 # target test rules
 
@@ -321,19 +319,6 @@
 	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
 endif
 
-# Valgrind.
-.PHONY: valgrind-test-art-target
-valgrind-test-art-target: valgrind-test-art-target-gtest
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
-.PHONY: valgrind-test-art-target32
-valgrind-test-art-target32: valgrind-test-art-target-gtest32
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
-.PHONY: valgrind-test-art-target64
-valgrind-test-art-target64: valgrind-test-art-target-gtest64
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
 
 #######################
 # Fake packages for ART
@@ -471,10 +456,12 @@
                         $(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \
                         $(TARGET_CORE_IMG_OUT_BASE).art \
                         $(TARGET_CORE_IMG_OUT_BASE)-interpreter.art
-	# remove libartd.so and libdexfiled.so from public.libraries.txt because golem builds
+	# remove debug libraries from public.libraries.txt because golem builds
 	# won't have it.
 	sed -i '/libartd.so/d' $(TARGET_OUT)/etc/public.libraries.txt
 	sed -i '/libdexfiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt
+	sed -i '/libprofiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt
+	sed -i '/libartbased.so/d' $(TARGET_OUT)/etc/public.libraries.txt
 
 ########################################################################
 # Phony target for building what go/lem requires on host.
diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp
index 441b706..95fc274 100644
--- a/adbconnection/Android.bp
+++ b/adbconnection/Android.bp
@@ -65,6 +65,7 @@
     defaults: ["adbconnection-defaults"],
     shared_libs: [
         "libart",
+        "libartbase",
     ],
 }
 
@@ -76,5 +77,6 @@
     ],
     shared_libs: [
         "libartd",
+        "libartbased",
     ],
 }
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index 4c2d4d7..8cd0d8b 100644
--- a/adbconnection/adbconnection.cc
+++ b/adbconnection/adbconnection.cc
@@ -23,8 +23,8 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/mutex.h"
-#include "java_vm_ext.h"
-#include "jni_env_ext.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_env_ext.h"
 #include "mirror/throwable.h"
 #include "nativehelper/ScopedLocalRef.h"
 #include "runtime-inl.h"
diff --git a/benchmark/jobject-benchmark/jobject_benchmark.cc b/benchmark/jobject-benchmark/jobject_benchmark.cc
index 7e0a536..2f38b78 100644
--- a/benchmark/jobject-benchmark/jobject_benchmark.cc
+++ b/benchmark/jobject-benchmark/jobject_benchmark.cc
@@ -16,7 +16,7 @@
 
 #include "jni.h"
 
-#include "java_vm_ext.h"
+#include "jni/java_vm_ext.h"
 #include "mirror/class-inl.h"
 #include "scoped_thread_state_change-inl.h"
 
diff --git a/build/Android.bp b/build/Android.bp
index 2a5598f..3a1d583 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -127,8 +127,6 @@
     },
 
     include_dirs: [
-        "external/valgrind/include",
-        "external/valgrind",
         "external/vixl/src",
     ],
 
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index a4a72f4..c3f81a6 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -277,6 +277,16 @@
   $(TARGET_CORE_IMAGE_DEFAULT_32) \
   dexdump2-target
 
+# The dexanalyze test requires an image and the dexanalyze utility.
+ART_GTEST_dexanalyze_test_HOST_DEPS := \
+  $(HOST_CORE_IMAGE_DEFAULT_64) \
+  $(HOST_CORE_IMAGE_DEFAULT_32) \
+  dexanalyze-host
+ART_GTEST_dexanalyze_test_TARGET_DEPS := \
+  $(TARGET_CORE_IMAGE_DEFAULT_64) \
+  $(TARGET_CORE_IMAGE_DEFAULT_32) \
+  dexanalyze-target
+
 # The dexlayout test requires an image and the dexlayout utility.
 # TODO: rename into dexdump when migration completes
 ART_GTEST_dexlayout_test_HOST_DEPS := \
@@ -370,6 +380,7 @@
     art_imgdiag_tests \
     art_libartbase_tests \
     art_libdexfile_tests \
+    art_libprofile_tests \
     art_oatdump_tests \
     art_patchoat_tests \
     art_profman_tests \
@@ -397,15 +408,9 @@
 ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST_RULES :=
 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_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST_RULES :=
 ART_TEST_HOST_GTEST_DEPENDENCIES :=
 
 ART_GTEST_TARGET_ANDROID_ROOT := '/system'
@@ -413,40 +418,6 @@
   ART_GTEST_TARGET_ANDROID_ROOT := $(ART_TEST_ANDROID_ROOT)
 endif
 
-ART_VALGRIND_TARGET_DEPENDENCIES :=
-
-# Has to match list in external/valgrind/Android.build_one.mk
-ART_VALGRIND_SUPPORTED_ARCH := arm arm64 x86_64
-
-# Valgrind is not supported for x86
-ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_ARCH)))
-art_vg_arch := $(if $(filter x86_64,$(TARGET_ARCH)),amd64,$(TARGET_ARCH))
-ART_VALGRIND_TARGET_DEPENDENCIES += \
-  $(TARGET_OUT_EXECUTABLES)/valgrind \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(art_vg_arch)-linux \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(art_vg_arch)-linux.so \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(art_vg_arch)-linux.so \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/default.supp
-art_vg_arch :=
-endif
-
-ifdef TARGET_2ND_ARCH
-ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_2ND_ARCH)))
-ART_VALGRIND_TARGET_DEPENDENCIES += \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(TARGET_2ND_ARCH)-linux \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(TARGET_2ND_ARCH)-linux.so \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(TARGET_2ND_ARCH)-linux.so
-endif
-endif
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := valgrind-target-suppressions.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := test/valgrind-target-suppressions.txt
-LOCAL_MODULE_PATH := $(ART_TARGET_TEST_OUT)
-include $(BUILD_PREBUILT)
-
 # 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): path relative to $OUT to the test binary
@@ -466,22 +437,36 @@
     $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so \
     $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libopenjdkd.so \
     $$(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar \
-    $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar \
-    $$(ART_TARGET_TEST_OUT)/valgrind-target-suppressions.txt
+    $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar
 
-$$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe)
+$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe)
+
+ifeq ($(ART_TEST_CHROOT),)
+# Non-chroot configuration.
+maybe_art_test_chroot :=
+maybe_chroot_command :=
+else
+# Chroot configuration.
+maybe_art_test_chroot := $(ART_TEST_CHROOT)/
+maybe_chroot_command := chroot $(ART_TEST_CHROOT)
+endif
+
+# File witnessing the success of the gtest, the presence of which means the gtest's success.
+gtest_witness := \
+  $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$(gtest_rule)-$$$$PPID
+
+$$(gtest_rule): GTEST_WITNESS := $$(gtest_witness)
 
 .PHONY: $$(gtest_rule)
 $$(gtest_rule): test-art-target-sync
-	$(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
-	$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
-	$(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE)
+	$(hide) adb shell touch $$(GTEST_WITNESS)
+	$(hide) adb shell rm $$(GTEST_WITNESS)
+	$(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE)
 	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
+	  (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
 	       ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \
-	     && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
-	   && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
-	       && $$(call ART_TEST_PASSED,$$@)) \
+	     && touch $$(GTEST_WITNESS)" \
+	   && (adb pull $$(GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \
 	   || $$(call ART_TEST_FAILED,$$@))
 	$(hide) rm -f /tmp/$$@-$$$$PPID
 
@@ -489,46 +474,15 @@
   ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule)
   ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule)
 
-.PHONY: valgrind-$$(gtest_rule)
-valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync
-	$(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
-	$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
-	$(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE)
-	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
-	       ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
-	       $$$$ANDROID_ROOT/bin/valgrind \
-	       --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \
-	       --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \
-	       --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \
-	     && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
-	   && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
-	       && $$(call ART_TEST_PASSED,$$@)) \
-	   || $$(call ART_TEST_FAILED,$$@))
-	$(hide) rm -f /tmp/$$@-$$$$PPID
-
-  ART_TEST_TARGET_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += \
-    valgrind-$$(gtest_rule)
-  ART_TEST_TARGET_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule)
-  ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule)
-
   # Clear locally defined variables.
-  valgrind_gtest_rule :=
-  gtest_rule :=
-  gtest_exe :=
+  gtest_witness :=
+  maybe_chroot_command :=
+  maybe_art_test_chroot :=
   gtest_target_exe :=
+  gtest_exe :=
+  gtest_rule :=
 endef  # define-art-gtest-rule-target
 
-ART_VALGRIND_DEPENDENCIES := \
-  $(HOST_OUT_EXECUTABLES)/valgrind \
-  $(HOST_OUT)/lib64/valgrind/memcheck-amd64-linux \
-  $(HOST_OUT)/lib64/valgrind/memcheck-x86-linux \
-  $(HOST_OUT)/lib64/valgrind/default.supp \
-  $(HOST_OUT)/lib64/valgrind/vgpreload_core-amd64-linux.so \
-  $(HOST_OUT)/lib64/valgrind/vgpreload_core-x86-linux.so \
-  $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-amd64-linux.so \
-  $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-x86-linux.so
-
 # Define make rules for a host gtests.
 # $(1): gtest name - the name of the test we're building such as leb128_test.
 # $(2): path relative to $OUT to the test binary
@@ -580,24 +534,10 @@
   ART_TEST_HOST_GTEST_$(1)_RULES += $$(gtest_rule)
 
 
-.PHONY: valgrind-$$(gtest_rule)
-valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES)
-	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  VALGRIND_LIB=$(HOST_OUT)/lib64/valgrind \
-	  $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 \
-	    --suppressions=art/test/valgrind-suppressions.txt --num-callers=50 \
-	    $$< && \
-	    $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@)
-
-  ART_TEST_HOST_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_RULES += valgrind-$$(gtest_rule)
-  ART_TEST_HOST_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule)
-  ART_TEST_HOST_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule)
-
   # Clear locally defined variables.
-  valgrind_gtest_rule :=
-  gtest_rule :=
-  gtest_exe :=
   gtest_deps :=
+  gtest_exe :=
+  gtest_rule :=
 endef  # define-art-gtest-rule-host
 
 # Define the rules to build and run host and target gtests.
@@ -626,7 +566,6 @@
 
   ifndef ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES
     ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES :=
-    ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
   endif
   $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),$$(art_gtest_filename),$(2),$$($(2)library_path)))
 
@@ -646,7 +585,6 @@
   art_gtest_name := $$(notdir $$(basename $$(art_gtest_filename)))
   ifndef ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES
     ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES :=
-    ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
   endif
   $$(eval $$(call define-art-gtest-rule-host,$$(art_gtest_name),$$(art_gtest_filename),$(2)))
 
@@ -665,13 +603,8 @@
 test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES)
 	$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
 
-.PHONY: valgrind-test-art-target-gtest-$$(art_gtest_name)
-valgrind-test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES)
-	$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
-
   # Clear now unused variables.
   ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES :=
-  ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
   art_gtest_name :=
 endef  # define-art-gtest-target-both
 
@@ -684,13 +617,8 @@
 test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES)
 	$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
 
-.PHONY: valgrind-test-art-host-gtest-$$(art_gtest_name)
-valgrind-test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES)
-	$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
-
   # Clear now unused variables.
   ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES :=
-  ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
   art_gtest_name :=
 endef  # define-art-gtest-host-both
 
@@ -716,12 +644,11 @@
 $(foreach file, $(ART_TARGET_GTEST_FILES), $(eval RUNTIME_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(patsubst %/,%,$$(dir $$(file))))_$$(notdir $$(basename $$(file)))))
 COMPILER_TARGET_GTEST_MAKE_TARGETS :=
 
-# Define all the combinations of host/target, valgrind and suffix such as:
-# test-art-host-gtest or valgrind-test-art-host-gtest64
+# Define all the combinations of host/target and suffix such as:
+# test-art-host-gtest or test-art-host-gtest64
 # $(1): host or target
 # $(2): HOST or TARGET
-# $(3): valgrind- or undefined
-# $(4): undefined, 32 or 64
+# $(3): undefined, 32 or 64
 define define-test-art-gtest-combination
   ifeq ($(1),host)
     ifneq ($(2),HOST)
@@ -736,12 +663,8 @@
     endif
   endif
 
-  rule_name := $(3)test-art-$(1)-gtest$(4)
-  ifeq ($(3),valgrind-)
-    dependencies := $$(ART_TEST_$(2)_VALGRIND_GTEST$(4)_RULES)
-  else
-    dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES)
-  endif
+  rule_name := test-art-$(1)-gtest$(3)
+  dependencies := $$(ART_TEST_$(2)_GTEST$(3)_RULES)
 
 .PHONY: $$(rule_name)
 $$(rule_name): $$(dependencies) dx d8-compat-dx desugar
@@ -752,21 +675,15 @@
   dependencies :=
 endef  # define-test-art-gtest-combination
 
-$(eval $(call define-test-art-gtest-combination,target,TARGET,,))
-$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,))
-$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(ART_PHONY_TEST_TARGET_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,target,TARGET,))
+$(eval $(call define-test-art-gtest-combination,target,TARGET,$(ART_PHONY_TEST_TARGET_SUFFIX)))
 ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX
-$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,target,TARGET,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
 endif
-$(eval $(call define-test-art-gtest-combination,host,HOST,,))
-$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,))
-$(eval $(call define-test-art-gtest-combination,host,HOST,,$(ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(ART_PHONY_TEST_HOST_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,host,HOST,))
+$(eval $(call define-test-art-gtest-combination,host,HOST,$(ART_PHONY_TEST_HOST_SUFFIX)))
 ifneq ($(HOST_PREFER_32_BIT),true)
-$(eval $(call define-test-art-gtest-combination,host,HOST,,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,host,HOST,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
 endif
 
 # Clear locally defined variables.
@@ -783,15 +700,9 @@
 ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST_RULES :=
 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_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST_RULES :=
 ART_GTEST_TARGET_ANDROID_ROOT :=
 ART_GTEST_class_linker_test_DEX_DEPS :=
 ART_GTEST_class_table_test_DEX_DEPS :=
@@ -830,8 +741,6 @@
 ART_GTEST_dex2oat_environment_tests_DEX_DEPS :=
 ART_GTEST_heap_verification_test_DEX_DEPS :=
 ART_GTEST_verifier_deps_test_DEX_DEPS :=
-ART_VALGRIND_DEPENDENCIES :=
-ART_VALGRIND_TARGET_DEPENDENCIES :=
 $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=))
 $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=))
 ART_TEST_HOST_GTEST_MainStripped_DEX :=
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 517ac5c..ba3ef05 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -37,11 +37,9 @@
 endif
 
 # Use dex2oat debug version for better error reporting
-# $(1): compiler - optimizing, interpreter or interpreter-access-checks.
+# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
 # $(2): 2ND_ or undefined, 2ND_ for 32-bit host builds.
-# $(3): wrapper, e.g., valgrind.
-# $(4): dex2oat suffix, e.g, valgrind requires 32 right now.
-# $(5): multi-image.
+# $(3): multi-image.
 # 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
@@ -65,11 +63,11 @@
   endif
   ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),)
     #Technically this test is not precise, but hopefully good enough.
-    $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing)
+    $$(error found $(1) expected interpreter, interp-ac, or optimizing)
   endif
 
-  # If $(5) is true, generate a multi-image.
-  ifeq ($(5),true)
+  # If $(3) is true, generate a multi-image.
+  ifeq ($(3),true)
     core_multi_infix := -multi
     core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar
     core_multi_group := _multi
@@ -79,22 +77,18 @@
     core_multi_group :=
   endif
 
-  core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_IMG_SUFFIX)
-  core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_OAT_SUFFIX)
+  core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_IMG_SUFFIX)
+  core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_OAT_SUFFIX)
 
   # Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
   ifeq ($(2),)
-    $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name)
+    HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name)
   else
-    $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name)
+    HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name)
   endif
-  $(3)HOST_CORE_IMG_OUTS += $$(core_image_name)
-  $(3)HOST_CORE_OAT_OUTS += $$(core_oat_name)
+  HOST_CORE_IMG_OUTS += $$(core_image_name)
+  HOST_CORE_OAT_OUTS += $$(core_oat_name)
 
-  # If we have a wrapper, make the target phony.
-  ifneq ($(3),)
-.PHONY: $$(core_image_name)
-  endif
 $$(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)
@@ -102,7 +96,7 @@
 $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
 	@echo "host dex2oat: $$@"
 	@mkdir -p $$(dir $$@)
-	$$(hide) $(3) $$(DEX2OAT)$(4) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+	$$(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=$$(PRIVATE_CORE_OAT_NAME) \
@@ -124,35 +118,27 @@
   core_infix :=
 endef  # create-core-oat-host-rules
 
-# $(1): compiler - optimizing, interpreter or interpreter-access-checks.
-# $(2): wrapper.
-# $(3): dex2oat suffix.
-# $(4): multi-image.
+# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
+# $(2): multi-image.
 define create-core-oat-host-rule-combination
-  $(call create-core-oat-host-rules,$(1),,$(2),$(3),$(4))
+  $(call create-core-oat-host-rules,$(1),,$(2))
 
   ifneq ($(HOST_PREFER_32_BIT),true)
-    $(call create-core-oat-host-rules,$(1),2ND_,$(2),$(3),$(4))
+    $(call create-core-oat-host-rules,$(1),2ND_,$(2))
   endif
 endef
 
-$(eval $(call create-core-oat-host-rule-combination,optimizing,,,false))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,,,false))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,false))
-$(eval $(call create-core-oat-host-rule-combination,optimizing,,,true))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,,,true))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,true))
-
-valgrindHOST_CORE_IMG_OUTS :=
-valgrindHOST_CORE_OAT_OUTS :=
-$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32,false))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32,false))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32,false))
-
-valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS)
+$(eval $(call create-core-oat-host-rule-combination,optimizing,false))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,false))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac,false))
+$(eval $(call create-core-oat-host-rule-combination,optimizing,true))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,true))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac,true))
 
 test-art-host-dex2oat-host: $(HOST_CORE_IMG_OUTS)
 
+# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
+# $(2): 2ND_ or undefined
 define create-core-oat-target-rules
   core_compile_options :=
   core_image_name :=
@@ -176,36 +162,32 @@
   endif
   ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),)
     # Technically this test is not precise, but hopefully good enough.
-    $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing)
+    $$(error found $(1) expected interpreter, interp-ac, or optimizing)
   endif
 
-  core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(3)$(CORE_IMG_SUFFIX)
-  core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(3)$(CORE_OAT_SUFFIX)
+  core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX)
+  core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX)
 
   # Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
   ifeq ($(2),)
     ifdef TARGET_2ND_ARCH
-      $(3)TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name)
+      TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name)
     else
-      $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
+      TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
     endif
   else
-    $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
+    TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
   endif
-  $(3)TARGET_CORE_IMG_OUTS += $$(core_image_name)
-  $(3)TARGET_CORE_OAT_OUTS += $$(core_oat_name)
+  TARGET_CORE_IMG_OUTS += $$(core_image_name)
+  TARGET_CORE_OAT_OUTS += $$(core_oat_name)
 
-  # If we have a wrapper, make the target phony.
-  ifneq ($(3),)
-.PHONY: $$(core_image_name)
-  endif
 $$(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) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+	$$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
 	  --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
 	  --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \
 	  $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
@@ -228,30 +210,18 @@
   core_infix :=
 endef  # create-core-oat-target-rules
 
-# $(1): compiler - optimizing, interpreter or interpreter-access-checks.
-# $(2): wrapper.
-# $(3): dex2oat suffix.
+# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
 define create-core-oat-target-rule-combination
-  $(call create-core-oat-target-rules,$(1),,$(2),$(3))
+  $(call create-core-oat-target-rules,$(1),)
 
   ifdef TARGET_2ND_ARCH
-    $(call create-core-oat-target-rules,$(1),2ND_,$(2),$(3))
+    $(call create-core-oat-target-rules,$(1),2ND_)
   endif
 endef
 
-$(eval $(call create-core-oat-target-rule-combination,optimizing,,))
-$(eval $(call create-core-oat-target-rule-combination,interpreter,,))
-$(eval $(call create-core-oat-target-rule-combination,interp-ac,,))
-
-valgrindTARGET_CORE_IMG_OUTS :=
-valgrindTARGET_CORE_OAT_OUTS :=
-$(eval $(call create-core-oat-target-rule-combination,optimizing,valgrind,32))
-$(eval $(call create-core-oat-target-rule-combination,interpreter,valgrind,32))
-$(eval $(call create-core-oat-target-rule-combination,interp-ac,valgrind,32))
-
-valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS)
-
-valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-test-art-host-dex2oat-target
+$(eval $(call create-core-oat-target-rule-combination,optimizing))
+$(eval $(call create-core-oat-target-rule-combination,interpreter))
+$(eval $(call create-core-oat-target-rule-combination,interp-ac))
 
 # Define a default core image that can be used for things like gtests that
 # need some image to run, but don't otherwise care which image is used.
diff --git a/build/art.go b/build/art.go
index 59480a0..3dabce3 100644
--- a/build/art.go
+++ b/build/art.go
@@ -278,6 +278,7 @@
 	android.RegisterModuleType("art_cc_test", artTest)
 	android.RegisterModuleType("art_cc_test_library", artTestLibrary)
 	android.RegisterModuleType("art_cc_defaults", artDefaultsFactory)
+	android.RegisterModuleType("libart_cc_defaults", libartDefaultsFactory)
 	android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
 	android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
 }
@@ -304,6 +305,33 @@
 	return module
 }
 
+func libartDefaultsFactory() android.Module {
+	c := &codegenProperties{}
+	module := cc.DefaultsFactory(c)
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		codegen(ctx, c, true)
+
+		type props struct {
+		  Target struct {
+		    Android struct {
+		      Shared_libs []string
+		    }
+		  }
+		}
+
+		p := &props{}
+		// TODO: express this in .bp instead b/79671158
+		if !envTrue(ctx, "ART_TARGET_LINUX") {
+		  p.Target.Android.Shared_libs = []string {
+		    "libmetricslogger",
+		  }
+		}
+		ctx.AppendProperties(p)
+	})
+
+	return module
+}
+
 func artLibrary() android.Module {
 	m, _ := cc.NewLibrary(android.HostAndDeviceSupported)
 	module := m.Init()
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 5a25a6c..48da755 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -643,6 +643,16 @@
       return Result::SuccessNoValue();
     }
 
+    if (option == "profile-aot-code") {
+      existing.profile_aot_code_ = true;
+      return Result::SuccessNoValue();
+    }
+
+    if (option == "save-without-jit-notifications") {
+      existing.wait_for_jit_notifications_to_save_ = false;
+      return Result::SuccessNoValue();
+    }
+
     // The rest of these options are always the wildcard from '-Xps-*'
     std::string suffix = RemovePrefix(option);
 
diff --git a/cmdline/unit.h b/cmdline/unit.h
index ad6a03d..f73981f 100644
--- a/cmdline/unit.h
+++ b/cmdline/unit.h
@@ -21,8 +21,9 @@
 
 // Used for arguments that simply indicate presence (e.g. "-help") without any values.
 struct Unit {
-  // Avoid 'Conditional jump or move depends on uninitialised value(s)' errors
-  // when running valgrind by specifying a user-defined constructor.
+  // Historical note: We specified a user-defined constructor to avoid
+  // 'Conditional jump or move depends on uninitialised value(s)' errors
+  // when running Valgrind.
   Unit() {}
   Unit(const Unit&) = default;
   ~Unit() {}
diff --git a/compiler/Android.bp b/compiler/Android.bp
index ec9fef7..be963fb 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -244,7 +244,9 @@
     },
     shared_libs: [
         "libart",
+        "libprofile",
         "libdexfile",
+        "libartbase",
     ],
 
     target: {
@@ -292,7 +294,9 @@
     },
     shared_libs: [
         "libartd",
+        "libprofiled",
         "libdexfiled",
+        "libartbased",
     ],
 }
 
@@ -408,6 +412,7 @@
     ],
 
     shared_libs: [
+        "libprofiled",
         "libartd-compiler",
         "libartd-simulator-container",
         "libvixld-arm",
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index 893cad2..87e679f 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -207,11 +207,10 @@
       std::vector<DexRegisterMap> dex_reg_maps;
       if (accessor.HasCodeItem() && mi->code_info != nullptr) {
         const CodeInfo code_info(mi->code_info);
-        CodeInfoEncoding encoding = code_info.ExtractEncoding();
-        for (size_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); ++s) {
-          const StackMap& stack_map = code_info.GetStackMapAt(s, encoding);
+        for (size_t s = 0; s < code_info.GetNumberOfStackMaps(); ++s) {
+          const StackMap stack_map = code_info.GetStackMapAt(s);
           dex_reg_maps.push_back(code_info.GetDexRegisterMapOf(
-              stack_map, encoding, accessor.RegistersSize()));
+              stack_map, accessor.RegistersSize()));
         }
       }
 
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 44504c1..a7adab5 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -100,15 +100,14 @@
       if (mi->code_info != nullptr) {
         // Use stack maps to create mapping table from pc to dex.
         const CodeInfo code_info(mi->code_info);
-        const CodeInfoEncoding encoding = code_info.ExtractEncoding();
-        pc2dex_map.reserve(code_info.GetNumberOfStackMaps(encoding));
-        for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); s++) {
-          StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+        pc2dex_map.reserve(code_info.GetNumberOfStackMaps());
+        for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+          StackMap stack_map = code_info.GetStackMapAt(s);
           DCHECK(stack_map.IsValid());
-          const uint32_t pc = stack_map.GetNativePcOffset(encoding.stack_map.encoding, isa);
-          const int32_t dex = stack_map.GetDexPc(encoding.stack_map.encoding);
+          const uint32_t pc = stack_map.GetNativePcOffset(isa);
+          const int32_t dex = stack_map.GetDexPc();
           pc2dex_map.push_back({pc, dex});
-          if (stack_map.HasDexRegisterMap(encoding.stack_map.encoding)) {
+          if (stack_map.HasDexRegisterMap()) {
             // Guess that the first map with local variables is the end of prologue.
             prologue_end = std::min(prologue_end, pc);
           }
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
index 9ea9f01..c1bf915 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -99,12 +99,11 @@
   // Get stack maps sorted by pc (they might not be sorted internally).
   // TODO(dsrbecky) Remove this once stackmaps get sorted by pc.
   const CodeInfo code_info(method_info->code_info);
-  const CodeInfoEncoding encoding = code_info.ExtractEncoding();
   std::map<uint32_t, uint32_t> stack_maps;  // low_pc -> stack_map_index.
-  for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); s++) {
-    StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+  for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+    StackMap stack_map = code_info.GetStackMapAt(s);
     DCHECK(stack_map.IsValid());
-    if (!stack_map.HasDexRegisterMap(encoding.stack_map.encoding)) {
+    if (!stack_map.HasDexRegisterMap()) {
       // The compiler creates stackmaps without register maps at the start of
       // basic blocks in order to keep instruction-accurate line number mapping.
       // However, we never stop at those (breakpoint locations always have map).
@@ -112,7 +111,7 @@
       // The main reason for this is to save space by avoiding undefined gaps.
       continue;
     }
-    const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding.stack_map.encoding, isa);
+    const uint32_t pc_offset = stack_map.GetNativePcOffset(isa);
     DCHECK_LE(pc_offset, method_info->code_size);
     DCHECK_LE(compilation_unit_code_address, method_info->code_address);
     const uint32_t low_pc = dchecked_integral_cast<uint32_t>(
@@ -124,7 +123,7 @@
   for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) {
     const uint32_t low_pc = it->first;
     const uint32_t stack_map_index = it->second;
-    const StackMap& stack_map = code_info.GetStackMapAt(stack_map_index, encoding);
+    const StackMap stack_map = code_info.GetStackMapAt(stack_map_index);
     auto next_it = it;
     next_it++;
     const uint32_t high_pc = next_it != stack_maps.end()
@@ -136,7 +135,7 @@
     }
 
     // Check that the stack map is in the requested range.
-    uint32_t dex_pc = stack_map.GetDexPc(encoding.stack_map.encoding);
+    uint32_t dex_pc = stack_map.GetDexPc();
     if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) {
       // The variable is not in scope at this PC. Therefore omit the entry.
       // Note that this is different to None() entry which means in scope, but unknown location.
@@ -151,10 +150,10 @@
     DCHECK(dex_register_map.IsValid());
     CodeItemDataAccessor accessor(*method_info->dex_file, method_info->code_item);
     reg_lo = dex_register_map.GetDexRegisterLocation(
-        vreg, accessor.RegistersSize(), code_info, encoding);
+        vreg, accessor.RegistersSize(), code_info);
     if (is64bitValue) {
       reg_hi = dex_register_map.GetDexRegisterLocation(
-          vreg + 1, accessor.RegistersSize(), code_info, encoding);
+          vreg + 1, accessor.RegistersSize(), code_info);
     }
 
     // Add location entry for this address range.
diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h
index a88c5cb..fd132f4 100644
--- a/compiler/debug/elf_gnu_debugdata_writer.h
+++ b/compiler/debug/elf_gnu_debugdata_writer.h
@@ -41,23 +41,23 @@
   Lzma2EncProps_Normalize(&lzma2Props);
   CXzProps props;
   XzProps_Init(&props);
-  props.lzma2Props = &lzma2Props;
+  props.lzma2Props = lzma2Props;
   // Implement the required interface for communication (written in C so no virtual methods).
   struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress {
-    static SRes ReadImpl(void* p, void* buf, size_t* size) {
-      auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqInStream*>(p));
+    static SRes ReadImpl(const ISeqInStream* p, void* buf, size_t* size) {
+      auto* ctx = static_cast<XzCallbacks*>(const_cast<ISeqInStream*>(p));
       *size = std::min(*size, ctx->src_->size() - ctx->src_pos_);
       memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size);
       ctx->src_pos_ += *size;
       return SZ_OK;
     }
-    static size_t WriteImpl(void* p, const void* buf, size_t size) {
-      auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqOutStream*>(p));
+    static size_t WriteImpl(const ISeqOutStream* p, const void* buf, size_t size) {
+      auto* ctx = static_cast<const XzCallbacks*>(p);
       const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf);
       ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size);
       return size;
     }
-    static SRes ProgressImpl(void* , UInt64, UInt64) {
+    static SRes ProgressImpl(const ICompressProgress* , UInt64, UInt64) {
       return SZ_OK;
     }
     size_t src_pos_;
@@ -113,4 +113,3 @@
 }  // namespace art
 
 #endif  // ART_COMPILER_DEBUG_ELF_GNU_DEBUGDATA_WRITER_H_
-
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 19b1900..082e609 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -20,6 +20,7 @@
 #include "common_compiler_test.h"
 #include "compiled_method-inl.h"
 #include "compiler_callbacks.h"
+#include "dex/class_accessor-inl.h"
 #include "dex/dex_file.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
@@ -82,30 +83,21 @@
 
     // Unquicken the dex file.
     for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) {
-      const DexFile::ClassDef& class_def = updated_dex_file->GetClassDef(i);
-      const uint8_t* class_data = updated_dex_file->GetClassData(class_def);
-      if (class_data == nullptr) {
-        continue;
-      }
-      ClassDataItemIterator it(*updated_dex_file, class_data);
-      it.SkipAllFields();
-
       // Unquicken each method.
-      while (it.HasNextMethod()) {
-        uint32_t method_idx = it.GetMemberIndex();
-        CompiledMethod* compiled_method =
-            compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
+      ClassAccessor accessor(*updated_dex_file, updated_dex_file->GetClassDef(i));
+      accessor.VisitMethods([&](const ClassAccessor::Method& method) {
+        CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(
+            MethodReference(updated_dex_file,
+                            method.GetIndex()));
         ArrayRef<const uint8_t> table;
         if (compiled_method != nullptr) {
           table = compiled_method->GetVmapTable();
         }
         optimizer::ArtDecompileDEX(*updated_dex_file,
-                                   *it.GetMethodCodeItem(),
+                                   *accessor.GetCodeItem(method),
                                    table,
                                    /* decompile_return_instruction */ true);
-        it.Next();
-      }
-      DCHECK(!it.HasNext());
+      });
     }
 
     // Make sure after unquickening we go back to the same contents as the original dex file.
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index dc044c1..fe8b766 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -724,7 +724,8 @@
     return false;
   }
   DCHECK_GE(field->GetOffset().Int32Value(), 0);
-  // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451.
+  // Historical note: We made sure not to interleave function calls with bit field writes to
+  // placate Valgrind. Bug: 27552451.
   uint32_t field_offset = field->GetOffset().Uint32Value();
   bool is_volatile = field->IsVolatile();
   result->field_idx = field_idx;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 723c619..7dc44fa 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -57,8 +57,7 @@
 #include "gc/space/space.h"
 #include "handle_scope-inl.h"
 #include "intrinsics_enum.h"
-#include "jit/profile_compilation_info.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "linker/linker_patch.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
@@ -69,6 +68,7 @@
 #include "mirror/throwable.h"
 #include "nativehelper/ScopedLocalRef.h"
 #include "object_lock.h"
+#include "profile/profile_compilation_info.h"
 #include "runtime.h"
 #include "runtime_intrinsics.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 1332280..856cb36 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -30,12 +30,12 @@
 #include "dex/dex_file_types.h"
 #include "gc/heap.h"
 #include "handle_scope-inl.h"
-#include "jit/profile_compilation_info.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 "profile/profile_compilation_info.h"
 #include "scoped_thread_state_change-inl.h"
 
 namespace art {
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 2d82d79..933be4f 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -51,6 +51,7 @@
       implicit_suspend_checks_(false),
       compile_pic_(false),
       dump_timings_(false),
+      dump_pass_timings_(false),
       dump_stats_(false),
       verbose_methods_(),
       abort_on_hard_verifier_failure_(false),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 05d8805..cee989b 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -21,10 +21,10 @@
 #include <string>
 #include <vector>
 
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/utils.h"
 #include "compiler_filter.h"
-#include "globals.h"
 #include "optimizing/register_allocator.h"
 
 namespace art {
@@ -270,6 +270,10 @@
     return dump_timings_;
   }
 
+  bool GetDumpPassTimings() const {
+    return dump_pass_timings_;
+  }
+
   bool GetDumpStats() const {
     return dump_stats_;
   }
@@ -316,6 +320,7 @@
   bool implicit_suspend_checks_;
   bool compile_pic_;
   bool dump_timings_;
+  bool dump_pass_timings_;
   bool dump_stats_;
 
   // Vector of methods to have verbose output enabled for.
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index 3b18db0..32fc887 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -85,6 +85,10 @@
     options->dump_timings_ = true;
   }
 
+  if (map.Exists(Base::DumpPassTimings)) {
+    options->dump_pass_timings_ = true;
+  }
+
   if (map.Exists(Base::DumpStats)) {
     options->dump_stats_ = true;
   }
@@ -146,6 +150,9 @@
       .Define({"--dump-timings"})
           .IntoKey(Map::DumpTimings)
 
+      .Define({"--dump-pass-timings"})
+          .IntoKey(Map::DumpPassTimings)
+
       .Define({"--dump-stats"})
           .IntoKey(Map::DumpStats)
 
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index acddae7..529d43f 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -60,6 +60,7 @@
 COMPILER_OPTIONS_KEY (bool,                        DeduplicateCode,        true)
 COMPILER_OPTIONS_KEY (Unit,                        CountHotnessInCompiledCode)
 COMPILER_OPTIONS_KEY (Unit,                        DumpTimings)
+COMPILER_OPTIONS_KEY (Unit,                        DumpPassTimings)
 COMPILER_OPTIONS_KEY (Unit,                        DumpStats)
 
 #undef COMPILER_OPTIONS_KEY
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 730a1a6..c643af7 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -28,8 +28,8 @@
 #include "dex/dex_file.h"
 #include "gtest/gtest.h"
 #include "indirect_reference_table.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 8cb1998..0902bf2 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -37,7 +37,7 @@
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "jni_env_ext.h"
+#include "jni/jni_env_ext.h"
 #include "thread.h"
 #include "utils/arm/managed_register_arm.h"
 #include "utils/arm64/managed_register_arm64.h"
diff --git a/compiler/linker/buffered_output_stream.h b/compiler/linker/buffered_output_stream.h
index 66994e8..512409c 100644
--- a/compiler/linker/buffered_output_stream.h
+++ b/compiler/linker/buffered_output_stream.h
@@ -21,7 +21,7 @@
 
 #include "output_stream.h"
 
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 namespace linker {
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index d893cc8..dfefa52 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1938,9 +1938,9 @@
   DISALLOW_COPY_AND_ASSIGN(BCEVisitor);
 };
 
-void BoundsCheckElimination::Run() {
+bool BoundsCheckElimination::Run() {
   if (!graph_->HasBoundsChecks()) {
-    return;
+    return false;
   }
 
   // Reverse post order guarantees a node's dominators are visited first.
@@ -1968,6 +1968,8 @@
 
   // Perform cleanup.
   visitor.Finish();
+
+  return true;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h
index 79c67a8..92ab798 100644
--- a/compiler/optimizing/bounds_check_elimination.h
+++ b/compiler/optimizing/bounds_check_elimination.h
@@ -34,7 +34,7 @@
         side_effects_(side_effects),
         induction_analysis_(induction_analysis) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kBoundsCheckEliminationPassName = "BCE";
 
diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc
index 3addaee..bdc395b 100644
--- a/compiler/optimizing/cha_guard_optimization.cc
+++ b/compiler/optimizing/cha_guard_optimization.cc
@@ -241,14 +241,15 @@
   GetGraph()->IncrementNumberOfCHAGuards();
 }
 
-void CHAGuardOptimization::Run() {
+bool CHAGuardOptimization::Run() {
   if (graph_->GetNumberOfCHAGuards() == 0) {
-    return;
+    return false;
   }
   CHAGuardVisitor visitor(graph_);
   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
     visitor.VisitBasicBlock(block);
   }
+  return true;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h
index f14e07b..d2c5a34 100644
--- a/compiler/optimizing/cha_guard_optimization.h
+++ b/compiler/optimizing/cha_guard_optimization.h
@@ -30,7 +30,7 @@
                                 const char* name = kCHAGuardOptimizationPassName)
       : HOptimization(graph, name) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kCHAGuardOptimizationPassName = "cha_guard_optimization";
 
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 231017f..de1be5b 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -736,6 +736,46 @@
   }
 }
 
+void CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(
+    HLoadMethodHandle* method_handle,
+    Location runtime_proto_index_location,
+    Location runtime_return_location) {
+  DCHECK_EQ(method_handle->InputCount(), 1u);
+  LocationSummary* locations =
+      new (method_handle->GetBlock()->GetGraph()->GetAllocator()) LocationSummary(
+          method_handle, LocationSummary::kCallOnMainOnly);
+  locations->SetInAt(0, Location::NoLocation());
+  locations->AddTemp(runtime_proto_index_location);
+  locations->SetOut(runtime_return_location);
+}
+
+void CodeGenerator::GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle) {
+  LocationSummary* locations = method_handle->GetLocations();
+  MoveConstant(locations->GetTemp(0), method_handle->GetMethodHandleIndex());
+  CheckEntrypointTypes<kQuickResolveMethodHandle, void*, uint32_t>();
+  InvokeRuntime(kQuickResolveMethodHandle, method_handle, method_handle->GetDexPc());
+}
+
+void CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(
+    HLoadMethodType* method_type,
+    Location runtime_proto_index_location,
+    Location runtime_return_location) {
+  DCHECK_EQ(method_type->InputCount(), 1u);
+  LocationSummary* locations =
+      new (method_type->GetBlock()->GetGraph()->GetAllocator()) LocationSummary(
+          method_type, LocationSummary::kCallOnMainOnly);
+  locations->SetInAt(0, Location::NoLocation());
+  locations->AddTemp(runtime_proto_index_location);
+  locations->SetOut(runtime_return_location);
+}
+
+void CodeGenerator::GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type) {
+  LocationSummary* locations = method_type->GetLocations();
+  MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex().index_);
+  CheckEntrypointTypes<kQuickResolveMethodType, void*, uint32_t>();
+  InvokeRuntime(kQuickResolveMethodType, method_type, method_type->GetDexPc());
+}
+
 static uint32_t GetBootImageOffsetImpl(const void* object, ImageHeader::ImageSections section) {
   Runtime* runtime = Runtime::Current();
   DCHECK(runtime->IsAotCompiler());
@@ -935,11 +975,10 @@
                         const CodeInfo& code_info,
                         const ArenaVector<HSuspendCheck*>& loop_headers,
                         ArenaVector<size_t>* covered) {
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
   for (size_t i = 0; i < loop_headers.size(); ++i) {
     if (loop_headers[i]->GetDexPc() == dex_pc) {
       if (graph.IsCompilingOsr()) {
-        DCHECK(code_info.GetOsrStackMapForDexPc(dex_pc, encoding).IsValid());
+        DCHECK(code_info.GetOsrStackMapForDexPc(dex_pc).IsValid());
       }
       ++(*covered)[i];
     }
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 62caceb..a340446 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -25,17 +25,16 @@
 #include "base/bit_field.h"
 #include "base/bit_utils.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "base/memory_region.h"
 #include "dex/string_reference.h"
 #include "dex/type_reference.h"
-#include "globals.h"
 #include "graph_visualizer.h"
 #include "locations.h"
 #include "nodes.h"
 #include "optimizing_compiler_stats.h"
 #include "read_barrier_option.h"
 #include "stack.h"
-#include "stack_map.h"
 #include "utils/label.h"
 
 namespace art {
@@ -564,6 +563,16 @@
                                                         Location runtime_return_location);
   void GenerateLoadClassRuntimeCall(HLoadClass* cls);
 
+  static void CreateLoadMethodHandleRuntimeCallLocationSummary(HLoadMethodHandle* method_handle,
+                                                             Location runtime_handle_index_location,
+                                                             Location runtime_return_location);
+  void GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle);
+
+  static void CreateLoadMethodTypeRuntimeCallLocationSummary(HLoadMethodType* method_type,
+                                                             Location runtime_type_index_location,
+                                                             Location runtime_return_location);
+  void GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type);
+
   uint32_t GetBootImageOffset(HLoadClass* load_class);
   uint32_t GetBootImageOffset(HLoadString* load_string);
   uint32_t GetBootImageOffset(HInvokeStaticOrDirect* invoke);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index d4cfab8..6f173e1 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -5144,6 +5144,26 @@
   }
 }
 
+void LocationsBuilderARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location location = LocationFrom(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  codegen_->GenerateLoadMethodHandleRuntimeCall(load);
+}
+
+void LocationsBuilderARM64::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location location = LocationFrom(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorARM64::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 static MemOperand GetExceptionTlsAddress() {
   return MemOperand(tr, Thread::ExceptionOffset<kArm64PointerSize>().Int32Value());
 }
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index aa343b1..e7fe5b7 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -17,7 +17,6 @@
 #ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
 #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
 
-#include "arch/arm64/quick_method_frame_info_arm64.h"
 #include "base/bit_field.h"
 #include "code_generator.h"
 #include "common_arm64.h"
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 58ce9aa..859e159 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -7527,6 +7527,26 @@
   }
 }
 
+void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  InvokeRuntimeCallingConventionARMVIXL calling_convention;
+  Location location = LocationFrom(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  codegen_->GenerateLoadMethodHandleRuntimeCall(load);
+}
+
+void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConventionARMVIXL calling_convention;
+  Location location = LocationFrom(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 25e2edd..7f3441f 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -8226,6 +8226,26 @@
   }
 }
 
+void LocationsBuilderMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc);
+}
+
+void InstructionCodeGeneratorMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  codegen_->GenerateLoadMethodHandleRuntimeCall(load);
+}
+
+void LocationsBuilderMIPS::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc);
+}
+
+void InstructionCodeGeneratorMIPS::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 static int32_t GetExceptionTlsOffset() {
   return Thread::ExceptionOffset<kMipsPointerSize>().Int32Value();
 }
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 5b07b55..ee32b96 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -6262,6 +6262,26 @@
   }
 }
 
+void LocationsBuilderMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  codegen_->GenerateLoadMethodHandleRuntimeCall(load);
+}
+
+void LocationsBuilderMIPS64::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 static int32_t GetExceptionTlsOffset() {
   return Thread::ExceptionOffset<kMips64PointerSize>().Int32Value();
 }
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 82d1fda..9e31538 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6539,6 +6539,26 @@
   }
 }
 
+void LocationsBuilderX86::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorX86::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  codegen_->GenerateLoadMethodHandleRuntimeCall(load);
+}
+
+void LocationsBuilderX86::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorX86::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 void LocationsBuilderX86::VisitClinitCheck(HClinitCheck* check) {
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 322b0cf..f739704 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5908,6 +5908,26 @@
   }
 }
 
+void LocationsBuilderX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  // Custom calling convention: RAX serves as both input and output.
+  Location location = Location::RegisterLocation(RAX);
+  CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) {
+  codegen_->GenerateLoadMethodHandleRuntimeCall(load);
+}
+
+void LocationsBuilderX86_64::VisitLoadMethodType(HLoadMethodType* load) {
+  // Custom calling convention: RAX serves as both input and output.
+  Location location = Location::RegisterLocation(RAX);
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorX86_64::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) {
   // We assume the class to not be null.
   SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathX86_64(
diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc
index 2e31d35..d6c9755 100644
--- a/compiler/optimizing/code_sinking.cc
+++ b/compiler/optimizing/code_sinking.cc
@@ -25,11 +25,11 @@
 
 namespace art {
 
-void CodeSinking::Run() {
+bool CodeSinking::Run() {
   HBasicBlock* exit = graph_->GetExitBlock();
   if (exit == nullptr) {
     // Infinite loop, just bail.
-    return;
+    return false;
   }
   // TODO(ngeoffray): we do not profile branches yet, so use throw instructions
   // as an indicator of an uncommon branch.
@@ -40,6 +40,7 @@
       SinkCodeToUncommonBranch(exit_predecessor);
     }
   }
+  return true;
 }
 
 static bool IsInterestingInstruction(HInstruction* instruction) {
diff --git a/compiler/optimizing/code_sinking.h b/compiler/optimizing/code_sinking.h
index 836d9d4..5db0b6d 100644
--- a/compiler/optimizing/code_sinking.h
+++ b/compiler/optimizing/code_sinking.h
@@ -33,7 +33,7 @@
               const char* name = kCodeSinkingPassName)
       : HOptimization(graph, name, stats) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kCodeSinkingPassName = "code_sinking";
 
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index 6f11e62..bb78c23 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -68,13 +68,14 @@
 };
 
 
-void HConstantFolding::Run() {
+bool HConstantFolding::Run() {
   HConstantFoldingVisitor visitor(graph_);
   // 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.
   visitor.VisitReversePostOrder();
+  return true;
 }
 
 
diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h
index 05c6df4..f4dbc80 100644
--- a/compiler/optimizing/constant_folding.h
+++ b/compiler/optimizing/constant_folding.h
@@ -41,7 +41,7 @@
  public:
   HConstantFolding(HGraph* graph, const char* name) : HOptimization(graph, name) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kConstantFoldingPassName = "constant_folding";
 
diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.cc b/compiler/optimizing/constructor_fence_redundancy_elimination.cc
index 4a66cd2..1a7f926 100644
--- a/compiler/optimizing/constructor_fence_redundancy_elimination.cc
+++ b/compiler/optimizing/constructor_fence_redundancy_elimination.cc
@@ -250,13 +250,14 @@
   DISALLOW_COPY_AND_ASSIGN(CFREVisitor);
 };
 
-void ConstructorFenceRedundancyElimination::Run() {
+bool ConstructorFenceRedundancyElimination::Run() {
   CFREVisitor cfre_visitor(graph_, stats_);
 
   // Arbitrarily visit in reverse-post order.
   // The exact block visit order does not matter, as the algorithm
   // only operates on a single block at a time.
   cfre_visitor.VisitReversePostOrder();
+  return true;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.h b/compiler/optimizing/constructor_fence_redundancy_elimination.h
index f4b06d5..367d9f2 100644
--- a/compiler/optimizing/constructor_fence_redundancy_elimination.h
+++ b/compiler/optimizing/constructor_fence_redundancy_elimination.h
@@ -52,7 +52,7 @@
                                         const char* name = kCFREPassName)
       : HOptimization(graph, name, stats) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kCFREPassName = "constructor_fence_redundancy_elimination";
 
diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h
index be26e67..5ac6e46 100644
--- a/compiler/optimizing/data_type.h
+++ b/compiler/optimizing/data_type.h
@@ -216,6 +216,21 @@
         Size(result_type) > Size(input_type);
   }
 
+  static Type ToSigned(Type type) {
+    switch (type) {
+      case Type::kUint8:
+        return Type::kInt8;
+      case Type::kUint16:
+        return Type::kInt16;
+      case Type::kUint32:
+        return Type::kInt32;
+      case Type::kUint64:
+        return Type::kInt64;
+      default:
+        return type;
+    }
+  }
+
   static const char* PrettyDescriptor(Type type);
 
  private:
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 9fa0f72..1dc1094 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -508,7 +508,7 @@
   }
 }
 
-void HDeadCodeElimination::Run() {
+bool HDeadCodeElimination::Run() {
   // Do not eliminate dead blocks if the graph has irreducible loops. We could
   // support it, but that would require changes in our loop representation to handle
   // multiple entry points. We decided it was not worth the complexity.
@@ -526,6 +526,7 @@
   }
   SsaRedundantPhiElimination(graph_).Run();
   RemoveDeadInstructions();
+  return true;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h
index 92a7f56..90caa53 100644
--- a/compiler/optimizing/dead_code_elimination.h
+++ b/compiler/optimizing/dead_code_elimination.h
@@ -32,7 +32,8 @@
   HDeadCodeElimination(HGraph* graph, OptimizingCompilerStats* stats, const char* name)
       : HOptimization(graph, name, stats) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
+
   static constexpr const char* kDeadCodeEliminationPassName = "dead_code_elimination";
 
  private:
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index fbcbe36..a689f35 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -58,6 +58,30 @@
          !boundary->IsEntry();
 }
 
+
+size_t GraphChecker::Run(bool pass_change, size_t last_size) {
+  size_t current_size = GetGraph()->GetReversePostOrder().size();
+  if (!pass_change) {
+    // Nothing changed for certain. Do a quick sanity check on that assertion
+    // for anything other than the first call (when last size was still 0).
+    if (last_size != 0) {
+      if (current_size != last_size) {
+        AddError(StringPrintf("Incorrect no-change assertion, "
+                              "last graph size %zu vs current graph size %zu",
+                              last_size, current_size));
+      }
+    }
+    // TODO: if we would trust the "false" value of the flag completely, we
+    // could skip checking the graph at this point.
+  }
+
+  // VisitReversePostOrder is used instead of VisitInsertionOrder,
+  // as the latter might visit dead blocks removed by the dominator
+  // computation.
+  VisitReversePostOrder();
+  return current_size;
+}
+
 void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
   current_block_ = block;
 
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index dbedc40..3a2bb7a 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -38,13 +38,11 @@
     seen_ids_.ClearAllBits();
   }
 
-  // Check the whole graph (in reverse post-order).
-  void Run() {
-    // VisitReversePostOrder is used instead of VisitInsertionOrder,
-    // as the latter might visit dead blocks removed by the dominator
-    // computation.
-    VisitReversePostOrder();
-  }
+  // Check the whole graph. The pass_change parameter indicates whether changes
+  // may have occurred during the just executed pass. The default value is
+  // conservatively "true" (something may have changed). The last_size parameter
+  // and return value pass along the observed graph sizes.
+  size_t Run(bool pass_change = true, size_t last_size = 0);
 
   void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
 
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 54d4644..d65ad40 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -386,6 +386,18 @@
         << load_class->NeedsAccessCheck() << std::noboolalpha;
   }
 
+  void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) OVERRIDE {
+    StartAttributeStream("load_kind") << "RuntimeCall";
+    StartAttributeStream("method_handle_index") << load_method_handle->GetMethodHandleIndex();
+  }
+
+  void VisitLoadMethodType(HLoadMethodType* load_method_type) OVERRIDE {
+    StartAttributeStream("load_kind") << "RuntimeCall";
+    const DexFile& dex_file = load_method_type->GetDexFile();
+    const DexFile::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex());
+    StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id);
+  }
+
   void VisitLoadString(HLoadString* load_string) OVERRIDE {
     StartAttributeStream("load_kind") << load_string->GetLoadKind();
   }
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index f05159b..4863718 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -352,7 +352,7 @@
     visited_blocks_.ClearAllBits();
   }
 
-  void Run();
+  bool Run();
 
  private:
   // Per-block GVN. Will also update the ValueSet of the dominated and
@@ -397,7 +397,7 @@
   DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
 };
 
-void GlobalValueNumberer::Run() {
+bool GlobalValueNumberer::Run() {
   DCHECK(side_effects_.HasRun());
   sets_[graph_->GetEntryBlock()->GetBlockId()] = new (&allocator_) ValueSet(&allocator_);
 
@@ -406,6 +406,7 @@
   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
     VisitBasicBlock(block);
   }
+  return true;
 }
 
 void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
@@ -557,9 +558,9 @@
   return secondary_match;
 }
 
-void GVNOptimization::Run() {
+bool GVNOptimization::Run() {
   GlobalValueNumberer gvn(graph_, side_effects_);
-  gvn.Run();
+  return gvn.Run();
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h
index 4fdba26..75cfff2 100644
--- a/compiler/optimizing/gvn.h
+++ b/compiler/optimizing/gvn.h
@@ -31,7 +31,7 @@
                   const char* pass_name = kGlobalValueNumberingPassName)
       : HOptimization(graph, pass_name), side_effects_(side_effects) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kGlobalValueNumberingPassName = "GVN";
 
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index d270c6a..a4d638f 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -243,7 +243,7 @@
               graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)) {
 }
 
-void HInductionVarAnalysis::Run() {
+bool HInductionVarAnalysis::Run() {
   // Detects sequence variables (generalized induction variables) during an outer to inner
   // traversal of all loops using Gerlek's algorithm. The order is important to enable
   // range analysis on outer loop while visiting inner loops.
@@ -253,6 +253,7 @@
       VisitLoop(graph_block->GetLoopInformation());
     }
   }
+  return !induction_.empty();
 }
 
 void HInductionVarAnalysis::VisitLoop(HLoopInformation* loop) {
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index acad77d..89fed2e 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -37,7 +37,7 @@
  public:
   explicit HInductionVarAnalysis(HGraph* graph, const char* name = kInductionPassName);
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kInductionPassName = "induction_var_analysis";
 
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 8b10a78..ffa000e 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -124,13 +124,18 @@
   }
 }
 
-void HInliner::Run() {
-  if (graph_->IsDebuggable()) {
+bool HInliner::Run() {
+  if (compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits() == 0) {
+    // Inlining effectively disabled.
+    return false;
+  } else if (graph_->IsDebuggable()) {
     // For simplicity, we currently never inline when the graph is debuggable. This avoids
     // doing some logic in the runtime to discover if a method could have been inlined.
-    return;
+    return false;
   }
 
+  bool didInline = false;
+
   // Initialize the number of instructions for the method being compiled. Recursive calls
   // to HInliner::Run have already updated the instruction count.
   if (outermost_graph_ == graph_) {
@@ -171,7 +176,9 @@
               call->GetDexMethodIndex(), /* with_signature */ false);
           // Tests prevent inlining by having $noinline$ in their method names.
           if (callee_name.find("$noinline$") == std::string::npos) {
-            if (!TryInline(call) && honor_inline_directives) {
+            if (TryInline(call)) {
+              didInline = true;
+            } else if (honor_inline_directives) {
               bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos);
               CHECK(!should_have_inlined) << "Could not inline " << callee_name;
             }
@@ -179,12 +186,16 @@
         } else {
           DCHECK(!honor_inline_directives);
           // Normal case: try to inline.
-          TryInline(call);
+          if (TryInline(call)) {
+            didInline = true;
+          }
         }
       }
       instruction = next;
     }
   }
+
+  return didInline;
 }
 
 static bool IsMethodOrDeclaringClassFinal(ArtMethod* method)
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 02465d3..2fdf6a1 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -19,8 +19,8 @@
 
 #include "dex/dex_file_types.h"
 #include "dex/invoke_type.h"
-#include "jit/profile_compilation_info.h"
 #include "optimization.h"
+#include "profile/profile_compilation_info.h"
 
 namespace art {
 
@@ -60,7 +60,7 @@
         handles_(handles),
         inline_stats_(nullptr) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kInlinerPassName = "inliner";
 
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 9647dd5..24dc2ee 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -448,11 +448,9 @@
       invoke_type,
       target_method,
       HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
+  RangeInstructionOperands operands(graph_->GetNumberOfVRegs() - in_vregs, in_vregs);
   HandleInvoke(invoke,
-               in_vregs,
-               /* args */ nullptr,
-               graph_->GetNumberOfVRegs() - in_vregs,
-               /* is_range */ true,
+               operands,
                dex_file_->GetMethodShorty(method_idx),
                /* clinit_check */ nullptr,
                /* is_unresolved */ false);
@@ -916,10 +914,7 @@
 bool HInstructionBuilder::BuildInvoke(const Instruction& instruction,
                                       uint32_t dex_pc,
                                       uint32_t method_idx,
-                                      uint32_t number_of_vreg_arguments,
-                                      bool is_range,
-                                      uint32_t* args,
-                                      uint32_t register_index) {
+                                      const InstructionOperands& operands) {
   InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode());
   const char* descriptor = dex_file_->GetMethodShorty(method_idx);
   DataType::Type return_type = DataType::FromShorty(descriptor[0]);
@@ -943,12 +938,9 @@
                                                          method_idx,
                                                          invoke_type);
     return HandleInvoke(invoke,
-                        number_of_vreg_arguments,
-                        args,
-                        register_index,
-                        is_range,
+                        operands,
                         descriptor,
-                        nullptr, /* clinit_check */
+                        nullptr /* clinit_check */,
                         true /* is_unresolved */);
   }
 
@@ -976,12 +968,7 @@
         invoke_type,
         target_method,
         HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
-    return HandleStringInit(invoke,
-                            number_of_vreg_arguments,
-                            args,
-                            register_index,
-                            is_range,
-                            descriptor);
+    return HandleStringInit(invoke, operands, descriptor);
   }
 
   // Potential class initialization check, in the case of a static method call.
@@ -1042,26 +1029,16 @@
                                                ImTable::GetImtIndex(resolved_method));
   }
 
-  return HandleInvoke(invoke,
-                      number_of_vreg_arguments,
-                      args,
-                      register_index,
-                      is_range,
-                      descriptor,
-                      clinit_check,
-                      false /* is_unresolved */);
+  return HandleInvoke(invoke, operands, descriptor, clinit_check, false /* is_unresolved */);
 }
 
 bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED,
                                                  uint32_t dex_pc,
                                                  uint32_t method_idx,
-                                                 uint32_t proto_idx,
-                                                 uint32_t number_of_vreg_arguments,
-                                                 bool is_range,
-                                                 uint32_t* args,
-                                                 uint32_t register_index) {
+                                                 dex::ProtoIndex proto_idx,
+                                                 const InstructionOperands& operands) {
   const char* descriptor = dex_file_->GetShorty(proto_idx);
-  DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), number_of_vreg_arguments);
+  DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), operands.GetNumberOfOperands());
   DataType::Type return_type = DataType::FromShorty(descriptor[0]);
   size_t number_of_arguments = strlen(descriptor);
   HInvoke* invoke = new (allocator_) HInvokePolymorphic(allocator_,
@@ -1070,10 +1047,7 @@
                                                         dex_pc,
                                                         method_idx);
   return HandleInvoke(invoke,
-                      number_of_vreg_arguments,
-                      args,
-                      register_index,
-                      is_range,
+                      operands,
                       descriptor,
                       nullptr /* clinit_check */,
                       false /* is_unresolved */);
@@ -1222,26 +1196,22 @@
 }
 
 bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke,
-                                               uint32_t number_of_vreg_arguments,
-                                               uint32_t* args,
-                                               uint32_t register_index,
-                                               bool is_range,
+                                               const InstructionOperands& operands,
                                                const char* descriptor,
                                                size_t start_index,
                                                size_t* argument_index) {
   uint32_t descriptor_index = 1;  // Skip the return type.
-
+  const size_t number_of_operands = operands.GetNumberOfOperands();
   for (size_t i = start_index;
        // Make sure we don't go over the expected arguments or over the number of
        // dex registers given. If the instruction was seen as dead by the verifier,
        // it hasn't been properly checked.
-       (i < number_of_vreg_arguments) && (*argument_index < invoke->GetNumberOfArguments());
+       (i < number_of_operands) && (*argument_index < invoke->GetNumberOfArguments());
        i++, (*argument_index)++) {
     DataType::Type type = DataType::FromShorty(descriptor[descriptor_index++]);
     bool is_wide = (type == DataType::Type::kInt64) || (type == DataType::Type::kFloat64);
-    if (!is_range
-        && is_wide
-        && ((i + 1 == number_of_vreg_arguments) || (args[i] + 1 != args[i + 1]))) {
+    if (is_wide && ((i + 1 == number_of_operands) ||
+                    (operands.GetOperand(i) + 1 != operands.GetOperand(i + 1)))) {
       // Longs and doubles should be in pairs, that is, sequential registers. The verifier should
       // reject any class where this is violated. However, the verifier only does these checks
       // on non trivially dead instructions, so we just bailout the compilation.
@@ -1252,7 +1222,7 @@
                       MethodCompilationStat::kNotCompiledMalformedOpcode);
       return false;
     }
-    HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
+    HInstruction* arg = LoadLocal(operands.GetOperand(i), type);
     invoke->SetArgumentAt(*argument_index, arg);
     if (is_wide) {
       i++;
@@ -1279,10 +1249,7 @@
 }
 
 bool HInstructionBuilder::HandleInvoke(HInvoke* invoke,
-                                       uint32_t number_of_vreg_arguments,
-                                       uint32_t* args,
-                                       uint32_t register_index,
-                                       bool is_range,
+                                       const InstructionOperands& operands,
                                        const char* descriptor,
                                        HClinitCheck* clinit_check,
                                        bool is_unresolved) {
@@ -1291,7 +1258,7 @@
   size_t start_index = 0;
   size_t argument_index = 0;
   if (invoke->GetInvokeType() != InvokeType::kStatic) {  // Instance call.
-    uint32_t obj_reg = is_range ? register_index : args[0];
+    uint32_t obj_reg = operands.GetOperand(0);
     HInstruction* arg = is_unresolved
         ? LoadLocal(obj_reg, DataType::Type::kReference)
         : LoadNullCheckedLocal(obj_reg, invoke->GetDexPc());
@@ -1300,14 +1267,7 @@
     argument_index = 1;
   }
 
-  if (!SetupInvokeArguments(invoke,
-                            number_of_vreg_arguments,
-                            args,
-                            register_index,
-                            is_range,
-                            descriptor,
-                            start_index,
-                            &argument_index)) {
+  if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) {
     return false;
   }
 
@@ -1327,24 +1287,14 @@
 }
 
 bool HInstructionBuilder::HandleStringInit(HInvoke* invoke,
-                                           uint32_t number_of_vreg_arguments,
-                                           uint32_t* args,
-                                           uint32_t register_index,
-                                           bool is_range,
+                                           const InstructionOperands& operands,
                                            const char* descriptor) {
   DCHECK(invoke->IsInvokeStaticOrDirect());
   DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit());
 
   size_t start_index = 1;
   size_t argument_index = 0;
-  if (!SetupInvokeArguments(invoke,
-                            number_of_vreg_arguments,
-                            args,
-                            register_index,
-                            is_range,
-                            descriptor,
-                            start_index,
-                            &argument_index)) {
+  if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) {
     return false;
   }
 
@@ -1352,7 +1302,7 @@
 
   // This is a StringFactory call, not an actual String constructor. Its result
   // replaces the empty String pre-allocated by NewInstance.
-  uint32_t orig_this_reg = is_range ? register_index : args[0];
+  uint32_t orig_this_reg = operands.GetOperand(0);
   HInstruction* arg_this = LoadLocal(orig_this_reg, DataType::Type::kReference);
 
   // Replacing the NewInstance might render it redundant. Keep a list of these
@@ -1360,9 +1310,15 @@
   if (arg_this->IsNewInstance()) {
     ssa_builder_->AddUninitializedString(arg_this->AsNewInstance());
   } else {
+    // The only reason a HPhi can flow in a String.<init> is when there is an
+    // irreducible loop, which will create HPhi for all dex registers at loop entry.
     DCHECK(arg_this->IsPhi());
-    // NewInstance is not the direct input of the StringFactory call. It might
-    // be redundant but optimizing this case is not worth the effort.
+    DCHECK(graph_->HasIrreducibleLoops());
+    // Don't bother compiling a method in that situation. While we could look at all
+    // phis related to the HNewInstance, it's not worth the trouble.
+    MaybeRecordStat(compilation_stats_,
+                    MethodCompilationStat::kNotCompiledIrreducibleAndStringInit);
+    return false;
   }
 
   // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`.
@@ -1699,11 +1655,9 @@
 
 HNewArray* HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc,
                                                     dex::TypeIndex type_index,
-                                                    uint32_t number_of_vreg_arguments,
-                                                    bool is_range,
-                                                    uint32_t* args,
-                                                    uint32_t register_index) {
-  HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc);
+                                                    const InstructionOperands& operands) {
+  const size_t number_of_operands = operands.GetNumberOfOperands();
+  HInstruction* length = graph_->GetIntConstant(number_of_operands, dex_pc);
   HLoadClass* cls = BuildLoadClass(type_index, dex_pc);
   HNewArray* const object = new (allocator_) HNewArray(cls, length, dex_pc);
   AppendInstruction(object);
@@ -1717,8 +1671,8 @@
   bool is_reference_array = (primitive == 'L') || (primitive == '[');
   DataType::Type type = is_reference_array ? DataType::Type::kReference : DataType::Type::kInt32;
 
-  for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
-    HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type);
+  for (size_t i = 0; i < number_of_operands; ++i) {
+    HInstruction* value = LoadLocal(operands.GetOperand(i), type);
     HInstruction* index = graph_->GetIntConstant(i, dex_pc);
     HArraySet* aset = new (allocator_) HArraySet(object, index, value, type, dex_pc);
     ssa_builder_->MaybeAddAmbiguousArraySet(aset);
@@ -1896,6 +1850,20 @@
   }
 }
 
+void HInstructionBuilder::BuildLoadMethodHandle(uint16_t method_handle_index, uint32_t dex_pc) {
+  const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
+  HLoadMethodHandle* load_method_handle = new (allocator_) HLoadMethodHandle(
+      graph_->GetCurrentMethod(), method_handle_index, dex_file, dex_pc);
+  AppendInstruction(load_method_handle);
+}
+
+void HInstructionBuilder::BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc) {
+  const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
+  HLoadMethodType* load_method_type =
+      new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_index, dex_file, dex_pc);
+  AppendInstruction(load_method_type);
+}
+
 void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
                                          uint8_t destination,
                                          uint8_t reference,
@@ -2137,11 +2105,10 @@
       } else {
         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_pc, method_idx,
-                       number_of_vreg_arguments, false, args, -1)) {
+      uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args);
+      VarArgsInstructionOperands operands(args, number_of_vreg_arguments);
+      if (!BuildInvoke(instruction, dex_pc, method_idx, operands)) {
         return false;
       }
       break;
@@ -2164,10 +2131,8 @@
       } else {
         method_idx = instruction.VRegB_3rc();
       }
-      uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
-      uint32_t register_index = instruction.VRegC();
-      if (!BuildInvoke(instruction, dex_pc, method_idx,
-                       number_of_vreg_arguments, true, nullptr, register_index)) {
+      RangeInstructionOperands operands(instruction.VRegC(), instruction.VRegA_3rc());
+      if (!BuildInvoke(instruction, dex_pc, method_idx, operands)) {
         return false;
       }
       break;
@@ -2175,33 +2140,18 @@
 
     case Instruction::INVOKE_POLYMORPHIC: {
       uint16_t method_idx = instruction.VRegB_45cc();
-      uint16_t proto_idx = instruction.VRegH_45cc();
-      uint32_t number_of_vreg_arguments = instruction.VRegA_45cc();
+      dex::ProtoIndex proto_idx(instruction.VRegH_45cc());
       uint32_t args[5];
-      instruction.GetVarArgs(args);
-      return BuildInvokePolymorphic(instruction,
-                                    dex_pc,
-                                    method_idx,
-                                    proto_idx,
-                                    number_of_vreg_arguments,
-                                    false,
-                                    args,
-                                    -1);
+      uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args);
+      VarArgsInstructionOperands operands(args, number_of_vreg_arguments);
+      return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands);
     }
 
     case Instruction::INVOKE_POLYMORPHIC_RANGE: {
       uint16_t method_idx = instruction.VRegB_4rcc();
-      uint16_t proto_idx = instruction.VRegH_4rcc();
-      uint32_t number_of_vreg_arguments = instruction.VRegA_4rcc();
-      uint32_t register_index = instruction.VRegC_4rcc();
-      return BuildInvokePolymorphic(instruction,
-                                    dex_pc,
-                                    method_idx,
-                                    proto_idx,
-                                    number_of_vreg_arguments,
-                                    true,
-                                    nullptr,
-                                    register_index);
+      dex::ProtoIndex proto_idx(instruction.VRegH_4rcc());
+      RangeInstructionOperands operands(instruction.VRegC_4rcc(), instruction.VRegA_4rcc());
+      return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands);
     }
 
     case Instruction::NEG_INT: {
@@ -2749,30 +2699,19 @@
     }
 
     case Instruction::FILLED_NEW_ARRAY: {
-      uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
       dex::TypeIndex type_index(instruction.VRegB_35c());
       uint32_t args[5];
-      instruction.GetVarArgs(args);
-      HNewArray* new_array = BuildFilledNewArray(dex_pc,
-                                                 type_index,
-                                                 number_of_vreg_arguments,
-                                                 /* is_range */ false,
-                                                 args,
-                                                 /* register_index */ 0);
+      uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args);
+      VarArgsInstructionOperands operands(args, number_of_vreg_arguments);
+      HNewArray* new_array = BuildFilledNewArray(dex_pc, type_index, operands);
       BuildConstructorFenceForAllocation(new_array);
       break;
     }
 
     case Instruction::FILLED_NEW_ARRAY_RANGE: {
-      uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
       dex::TypeIndex type_index(instruction.VRegB_3rc());
-      uint32_t register_index = instruction.VRegC_3rc();
-      HNewArray* new_array = BuildFilledNewArray(dex_pc,
-                                                 type_index,
-                                                 number_of_vreg_arguments,
-                                                 /* is_range */ true,
-                                                 /* args*/ nullptr,
-                                                 register_index);
+      RangeInstructionOperands operands(instruction.VRegC_3rc(), instruction.VRegA_3rc());
+      HNewArray* new_array = BuildFilledNewArray(dex_pc, type_index, operands);
       BuildConstructorFenceForAllocation(new_array);
       break;
     }
@@ -2927,6 +2866,20 @@
       break;
     }
 
+    case Instruction::CONST_METHOD_HANDLE: {
+      uint16_t method_handle_idx = instruction.VRegB_21c();
+      BuildLoadMethodHandle(method_handle_idx, dex_pc);
+      UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+      break;
+    }
+
+    case Instruction::CONST_METHOD_TYPE: {
+      dex::ProtoIndex proto_idx(instruction.VRegB_21c());
+      BuildLoadMethodType(proto_idx, dex_pc);
+      UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+      break;
+    }
+
     case Instruction::MOVE_EXCEPTION: {
       AppendInstruction(new (allocator_) HLoadException(dex_pc));
       UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction());
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index f788292..2218a69 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -38,6 +38,7 @@
 class DexCompilationUnit;
 class HBasicBlockBuilder;
 class Instruction;
+class InstructionOperands;
 class OptimizingCompilerStats;
 class ScopedObjectAccess;
 class SsaBuilder;
@@ -45,6 +46,7 @@
 
 namespace mirror {
 class Class;
+class MethodType;
 }  // namespace mirror
 
 class HInstructionBuilder : public ValueObject {
@@ -167,29 +169,20 @@
   bool BuildInvoke(const Instruction& instruction,
                    uint32_t dex_pc,
                    uint32_t method_idx,
-                   uint32_t number_of_vreg_arguments,
-                   bool is_range,
-                   uint32_t* args,
-                   uint32_t register_index);
+                   const InstructionOperands& operands);
 
   // Builds an invocation node for invoke-polymorphic and returns whether the
   // instruction is supported.
   bool BuildInvokePolymorphic(const Instruction& instruction,
                               uint32_t dex_pc,
                               uint32_t method_idx,
-                              uint32_t proto_idx,
-                              uint32_t number_of_vreg_arguments,
-                              bool is_range,
-                              uint32_t* args,
-                              uint32_t register_index);
+                              dex::ProtoIndex proto_idx,
+                              const InstructionOperands& operands);
 
   // Builds a new array node and the instructions that fill it.
   HNewArray* BuildFilledNewArray(uint32_t dex_pc,
                                  dex::TypeIndex type_index,
-                                 uint32_t number_of_vreg_arguments,
-                                 bool is_range,
-                                 uint32_t* args,
-                                 uint32_t register_index);
+                                 const InstructionOperands& operands);
 
   void BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc);
 
@@ -239,6 +232,12 @@
   bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Builds a `HLoadMethodHandle` loading the given `method_handle_index`.
+  void BuildLoadMethodHandle(uint16_t method_handle_idx, uint32_t dex_pc);
+
+  // Builds a `HLoadMethodType` loading the given `proto_index`.
+  void BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc);
+
   // Returns the outer-most compiling method's class.
   ObjPtr<mirror::Class> GetOutermostCompilingClass() const;
 
@@ -253,28 +252,19 @@
                                      HInvoke* invoke);
 
   bool SetupInvokeArguments(HInvoke* invoke,
-                            uint32_t number_of_vreg_arguments,
-                            uint32_t* args,
-                            uint32_t register_index,
-                            bool is_range,
+                            const InstructionOperands& operands,
                             const char* descriptor,
                             size_t start_index,
                             size_t* argument_index);
 
   bool HandleInvoke(HInvoke* invoke,
-                    uint32_t number_of_vreg_arguments,
-                    uint32_t* args,
-                    uint32_t register_index,
-                    bool is_range,
+                    const InstructionOperands& operands,
                     const char* descriptor,
                     HClinitCheck* clinit_check,
                     bool is_unresolved);
 
   bool HandleStringInit(HInvoke* invoke,
-                        uint32_t number_of_vreg_arguments,
-                        uint32_t* args,
-                        uint32_t register_index,
-                        bool is_range,
+                        const InstructionOperands& operands,
                         const char* descriptor);
   void HandleStringInitResult(HInvokeStaticOrDirect* invoke);
 
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index d3cf956..6e618f4 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -42,7 +42,7 @@
         compiler_driver_(compiler_driver),
         stats_(stats) {}
 
-  void Run();
+  bool Run();
 
  private:
   void RecordSimplification() {
@@ -136,17 +136,18 @@
   static constexpr int kMaxSamePositionSimplifications = 50;
 };
 
-void InstructionSimplifier::Run() {
+bool InstructionSimplifier::Run() {
   if (kTestInstructionClonerExhaustively) {
     CloneAndReplaceInstructionVisitor visitor(graph_);
     visitor.VisitReversePostOrder();
   }
 
   InstructionSimplifierVisitor visitor(graph_, codegen_, compiler_driver_, stats_);
-  visitor.Run();
+  return visitor.Run();
 }
 
-void InstructionSimplifierVisitor::Run() {
+bool InstructionSimplifierVisitor::Run() {
+  bool didSimplify = false;
   // Iterate in reverse post order to open up more simplifications to users
   // of instructions that got simplified.
   for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
@@ -156,10 +157,14 @@
     do {
       simplification_occurred_ = false;
       VisitBasicBlock(block);
+      if (simplification_occurred_) {
+        didSimplify = true;
+      }
     } while (simplification_occurred_ &&
              (simplifications_at_current_position_ < kMaxSamePositionSimplifications));
     simplifications_at_current_position_ = 0;
   }
+  return didSimplify;
 }
 
 namespace {
@@ -631,8 +636,8 @@
     return;
   }
 
-  // Note: The `outcome` is initialized to please valgrind - the compiler can reorder
-  // the return value check with the `outcome` check, b/27651442 .
+  // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder
+  // the return value check with the `outcome` check, b/27651442.
   bool outcome = false;
   if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) {
     if (outcome) {
@@ -677,8 +682,8 @@
     return;
   }
 
-  // Note: The `outcome` is initialized to please valgrind - the compiler can reorder
-  // the return value check with the `outcome` check, b/27651442 .
+  // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder
+  // the return value check with the `outcome` check, b/27651442.
   bool outcome = false;
   if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) {
     MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf);
@@ -2661,10 +2666,10 @@
   HConstant* const2;
   HBinaryOperation* y;
 
-  if (instruction->InstructionTypeEquals(left) && right->IsConstant()) {
+  if (instruction->GetKind() == left->GetKind() && right->IsConstant()) {
     const2 = right->AsConstant();
     y = left->AsBinaryOperation();
-  } else if (left->IsConstant() && instruction->InstructionTypeEquals(right)) {
+  } else if (left->IsConstant() && instruction->GetKind() == right->GetKind()) {
     const2 = left->AsConstant();
     y = right->AsBinaryOperation();
   } else {
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index 5e20455..f409e87 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -49,7 +49,7 @@
 
   static constexpr const char* kInstructionSimplifierPassName = "instruction_simplifier";
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
  private:
   CodeGenerator* codegen_;
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 92081e3..37fcdb9 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -283,9 +283,10 @@
   }
 }
 
-void InstructionSimplifierArm::Run() {
+bool InstructionSimplifierArm::Run() {
   InstructionSimplifierArmVisitor visitor(graph_, stats_);
   visitor.VisitReversePostOrder();
+  return true;
 }
 
 }  // namespace arm
diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h
index 2f65729..f1a16ef 100644
--- a/compiler/optimizing/instruction_simplifier_arm.h
+++ b/compiler/optimizing/instruction_simplifier_arm.h
@@ -30,7 +30,7 @@
 
   static constexpr const char* kInstructionSimplifierArmPassName = "instruction_simplifier_arm";
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 };
 
 }  // namespace arm
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index 1c44e5a..e0a6279 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -278,9 +278,10 @@
   }
 }
 
-void InstructionSimplifierArm64::Run() {
+bool InstructionSimplifierArm64::Run() {
   InstructionSimplifierArm64Visitor visitor(graph_, stats_);
   visitor.VisitReversePostOrder();
+  return true;
 }
 
 }  // namespace arm64
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index d180a8d..8659c1f 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -30,7 +30,7 @@
 
   static constexpr const char* kInstructionSimplifierArm64PassName = "instruction_simplifier_arm64";
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 };
 
 }  // namespace arm64
diff --git a/compiler/optimizing/instruction_simplifier_mips.cc b/compiler/optimizing/instruction_simplifier_mips.cc
index fa97401..3bdf90f 100644
--- a/compiler/optimizing/instruction_simplifier_mips.cc
+++ b/compiler/optimizing/instruction_simplifier_mips.cc
@@ -131,9 +131,10 @@
   }
 }
 
-void InstructionSimplifierMips::Run() {
+bool InstructionSimplifierMips::Run() {
   InstructionSimplifierMipsVisitor visitor(graph_, codegen_, stats_);
   visitor.VisitReversePostOrder();
+  return true;
 }
 
 }  // namespace mips
diff --git a/compiler/optimizing/instruction_simplifier_mips.h b/compiler/optimizing/instruction_simplifier_mips.h
index 6cb8aff..94ef73d 100644
--- a/compiler/optimizing/instruction_simplifier_mips.h
+++ b/compiler/optimizing/instruction_simplifier_mips.h
@@ -35,7 +35,7 @@
 
   static constexpr const char* kInstructionSimplifierMipsPassName = "instruction_simplifier_mips";
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
  private:
   CodeGeneratorMIPS* codegen_;
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index f8dc316..dfe6d79 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -178,7 +178,8 @@
   return true;
 }
 
-void IntrinsicsRecognizer::Run() {
+bool IntrinsicsRecognizer::Run() {
+  bool didRecognize = false;
   ScopedObjectAccess soa(Thread::Current());
   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
     for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
@@ -187,6 +188,7 @@
       if (inst->IsInvoke()) {
         bool wrong_invoke_type = false;
         if (Recognize(inst->AsInvoke(), /* art_method */ nullptr, &wrong_invoke_type)) {
+          didRecognize = true;
           MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized);
         } else if (wrong_invoke_type) {
           LOG(WARNING)
@@ -197,6 +199,7 @@
       }
     }
   }
+  return didRecognize;
 }
 
 std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 1035cbc..30cffac 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -42,7 +42,7 @@
                        const char* name = kIntrinsicsRecognizerPassName)
       : HOptimization(graph, name, stats) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   // Static helper that recognizes intrinsic call. Returns true on success.
   // If it fails due to invoke type mismatch, wrong_invoke_type is set.
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index d3a0376..0edb23b 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -78,7 +78,8 @@
   }
 }
 
-void LICM::Run() {
+bool LICM::Run() {
+  bool didLICM = false;
   DCHECK(side_effects_.HasRun());
 
   // Only used during debug.
@@ -157,6 +158,7 @@
           }
           instruction->MoveBefore(pre_header->GetLastInstruction());
           MaybeRecordStat(stats_, MethodCompilationStat::kLoopInvariantMoved);
+          didLICM = true;
         }
 
         if (!can_move && (instruction->CanThrow() || instruction->DoesAnyWrite())) {
@@ -167,6 +169,7 @@
       }
     }
   }
+  return didLICM;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
index ee567ae..f72d195 100644
--- a/compiler/optimizing/licm.h
+++ b/compiler/optimizing/licm.h
@@ -33,7 +33,7 @@
       : HOptimization(graph, name, stats),
         side_effects_(side_effects) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kLoopInvariantCodeMotionPassName = "licm";
 
diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc
index 8b1812a..7d7bb94 100644
--- a/compiler/optimizing/load_store_analysis.cc
+++ b/compiler/optimizing/load_store_analysis.cc
@@ -152,7 +152,7 @@
   return true;
 }
 
-void LoadStoreAnalysis::Run() {
+bool LoadStoreAnalysis::Run() {
   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
     heap_location_collector_.VisitBasicBlock(block);
   }
@@ -160,22 +160,23 @@
   if (heap_location_collector_.GetNumberOfHeapLocations() > kMaxNumberOfHeapLocations) {
     // Bail out if there are too many heap locations to deal with.
     heap_location_collector_.CleanUp();
-    return;
+    return false;
   }
   if (!heap_location_collector_.HasHeapStores()) {
     // Without heap stores, this pass would act mostly as GVN on heap accesses.
     heap_location_collector_.CleanUp();
-    return;
+    return false;
   }
   if (heap_location_collector_.HasVolatile() || heap_location_collector_.HasMonitorOps()) {
     // Don't do load/store elimination if the method has volatile field accesses or
     // monitor operations, for now.
     // TODO: do it right.
     heap_location_collector_.CleanUp();
-    return;
+    return false;
   }
 
   heap_location_collector_.BuildAliasingMatrix();
+  return true;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h
index 437e6be..769a3f1 100644
--- a/compiler/optimizing/load_store_analysis.h
+++ b/compiler/optimizing/load_store_analysis.h
@@ -94,11 +94,13 @@
   static constexpr int16_t kDeclaringClassDefIndexForArrays = -1;
 
   HeapLocation(ReferenceInfo* ref_info,
+               DataType::Type type,
                size_t offset,
                HInstruction* index,
                size_t vector_length,
                int16_t declaring_class_def_index)
       : ref_info_(ref_info),
+        type_(DataType::ToSigned(type)),
         offset_(offset),
         index_(index),
         vector_length_(vector_length),
@@ -116,6 +118,7 @@
   }
 
   ReferenceInfo* GetReferenceInfo() const { return ref_info_; }
+  DataType::Type GetType() const { return type_; }
   size_t GetOffset() const { return offset_; }
   HInstruction* GetIndex() const { return index_; }
   size_t GetVectorLength() const { return vector_length_; }
@@ -149,6 +152,10 @@
  private:
   // Reference for instance/static field, array element or vector data.
   ReferenceInfo* const ref_info_;
+  // Type of data residing at HeapLocation (always signed for integral
+  // data since e.g. a[i] and a[i] & 0xff are represented by differently
+  // signed types; char vs short are disambiguated through the reference).
+  const DataType::Type type_;
   // Offset of static/instance field.
   // Invalid when this HeapLocation is not field.
   const size_t offset_;
@@ -237,19 +244,31 @@
     DCHECK(object != nullptr);
     DCHECK(field != nullptr);
     return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(object)),
+                                 field->GetFieldType(),
                                  field->GetFieldOffset().SizeValue(),
                                  nullptr,
                                  HeapLocation::kScalar,
                                  field->GetDeclaringClassDefIndex());
   }
 
-  size_t GetArrayHeapLocation(HInstruction* array,
-                              HInstruction* index,
-                              size_t vector_length = HeapLocation::kScalar) const {
-    DCHECK(array != nullptr);
-    DCHECK(index != nullptr);
-    DCHECK_GE(vector_length, HeapLocation::kScalar);
+  size_t GetArrayHeapLocation(HInstruction* instruction) const {
+    DCHECK(instruction != nullptr);
+    HInstruction* array = instruction->InputAt(0);
+    HInstruction* index = instruction->InputAt(1);
+    DataType::Type type = instruction->GetType();
+    size_t vector_length = HeapLocation::kScalar;
+    if (instruction->IsArraySet()) {
+      type = instruction->AsArraySet()->GetComponentType();
+    } else if (instruction->IsVecStore() ||
+               instruction->IsVecLoad()) {
+      HVecOperation* vec_op = instruction->AsVecOperation();
+      type = vec_op->GetPackedType();
+      vector_length = vec_op->GetVectorLength();
+    } else {
+      DCHECK(instruction->IsArrayGet());
+    }
     return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(array)),
+                                 type,
                                  HeapLocation::kInvalidFieldOffset,
                                  index,
                                  vector_length,
@@ -279,13 +298,16 @@
   // In later analysis, ComputeMayAlias() and MayAlias() compute and tell whether
   // these indexes alias.
   size_t FindHeapLocationIndex(ReferenceInfo* ref_info,
+                               DataType::Type type,
                                size_t offset,
                                HInstruction* index,
                                size_t vector_length,
                                int16_t declaring_class_def_index) const {
+    DataType::Type lookup_type = DataType::ToSigned(type);
     for (size_t i = 0; i < heap_locations_.size(); i++) {
       HeapLocation* loc = heap_locations_[i];
       if (loc->GetReferenceInfo() == ref_info &&
+          loc->GetType() == lookup_type &&
           loc->GetOffset() == offset &&
           loc->GetIndex() == index &&
           loc->GetVectorLength() == vector_length &&
@@ -425,6 +447,7 @@
   }
 
   HeapLocation* GetOrCreateHeapLocation(HInstruction* ref,
+                                        DataType::Type type,
                                         size_t offset,
                                         HInstruction* index,
                                         size_t vector_length,
@@ -432,10 +455,10 @@
     HInstruction* original_ref = HuntForOriginalReference(ref);
     ReferenceInfo* ref_info = GetOrCreateReferenceInfo(original_ref);
     size_t heap_location_idx = FindHeapLocationIndex(
-        ref_info, offset, index, vector_length, declaring_class_def_index);
+        ref_info, type, offset, index, vector_length, declaring_class_def_index);
     if (heap_location_idx == kHeapLocationNotFound) {
       HeapLocation* heap_loc = new (GetGraph()->GetAllocator())
-          HeapLocation(ref_info, offset, index, vector_length, declaring_class_def_index);
+          HeapLocation(ref_info, type, offset, index, vector_length, declaring_class_def_index);
       heap_locations_.push_back(heap_loc);
       return heap_loc;
     }
@@ -446,17 +469,23 @@
     if (field_info.IsVolatile()) {
       has_volatile_ = true;
     }
+    DataType::Type type = field_info.GetFieldType();
     const uint16_t declaring_class_def_index = field_info.GetDeclaringClassDefIndex();
     const size_t offset = field_info.GetFieldOffset().SizeValue();
     return GetOrCreateHeapLocation(ref,
+                                   type,
                                    offset,
                                    nullptr,
                                    HeapLocation::kScalar,
                                    declaring_class_def_index);
   }
 
-  void VisitArrayAccess(HInstruction* array, HInstruction* index, size_t vector_length) {
+  void VisitArrayAccess(HInstruction* array,
+                        HInstruction* index,
+                        DataType::Type type,
+                        size_t vector_length) {
     GetOrCreateHeapLocation(array,
+                            type,
                             HeapLocation::kInvalidFieldOffset,
                             index,
                             vector_length,
@@ -510,28 +539,32 @@
   void VisitArrayGet(HArrayGet* instruction) OVERRIDE {
     HInstruction* array = instruction->InputAt(0);
     HInstruction* index = instruction->InputAt(1);
-    VisitArrayAccess(array, index, HeapLocation::kScalar);
+    DataType::Type type = instruction->GetType();
+    VisitArrayAccess(array, index, type, HeapLocation::kScalar);
     CreateReferenceInfoForReferenceType(instruction);
   }
 
   void VisitArraySet(HArraySet* instruction) OVERRIDE {
     HInstruction* array = instruction->InputAt(0);
     HInstruction* index = instruction->InputAt(1);
-    VisitArrayAccess(array, index, HeapLocation::kScalar);
+    DataType::Type type = instruction->GetComponentType();
+    VisitArrayAccess(array, index, type, HeapLocation::kScalar);
     has_heap_stores_ = true;
   }
 
   void VisitVecLoad(HVecLoad* instruction) OVERRIDE {
     HInstruction* array = instruction->InputAt(0);
     HInstruction* index = instruction->InputAt(1);
-    VisitArrayAccess(array, index, instruction->GetVectorLength());
+    DataType::Type type = instruction->GetPackedType();
+    VisitArrayAccess(array, index, type, instruction->GetVectorLength());
     CreateReferenceInfoForReferenceType(instruction);
   }
 
   void VisitVecStore(HVecStore* instruction) OVERRIDE {
     HInstruction* array = instruction->InputAt(0);
     HInstruction* index = instruction->InputAt(1);
-    VisitArrayAccess(array, index, instruction->GetVectorLength());
+    DataType::Type type = instruction->GetPackedType();
+    VisitArrayAccess(array, index, type, instruction->GetVectorLength());
     has_heap_stores_ = true;
   }
 
@@ -572,7 +605,7 @@
     return heap_location_collector_;
   }
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kLoadStoreAnalysisPassName = "load_store_analysis";
 
diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc
index 56361a8..bfe7a4f 100644
--- a/compiler/optimizing/load_store_analysis_test.cc
+++ b/compiler/optimizing/load_store_analysis_test.cc
@@ -78,12 +78,16 @@
 
   // Test queries on HeapLocationCollector's ref info and index records.
   ReferenceInfo* ref = heap_location_collector.FindReferenceInfoOf(array);
+  DataType::Type type = DataType::Type::kInt32;
   size_t field = HeapLocation::kInvalidFieldOffset;
   size_t vec = HeapLocation::kScalar;
   size_t class_def = HeapLocation::kDeclaringClassDefIndexForArrays;
-  size_t loc1 = heap_location_collector.FindHeapLocationIndex(ref, field, c1, vec, class_def);
-  size_t loc2 = heap_location_collector.FindHeapLocationIndex(ref, field, c2, vec, class_def);
-  size_t loc3 = heap_location_collector.FindHeapLocationIndex(ref, field, index, vec, class_def);
+  size_t loc1 = heap_location_collector.FindHeapLocationIndex(
+      ref, type, field, c1, vec, class_def);
+  size_t loc2 = heap_location_collector.FindHeapLocationIndex(
+      ref, type, field, c2, vec, class_def);
+  size_t loc3 = heap_location_collector.FindHeapLocationIndex(
+      ref, type, field, index, vec, class_def);
   // must find this reference info for array in HeapLocationCollector.
   ASSERT_TRUE(ref != nullptr);
   // must find these heap locations;
@@ -246,28 +250,28 @@
   size_t loc2 = HeapLocationCollector::kHeapLocationNotFound;
 
   // Test alias: array[0] and array[1]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, c0);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, c1);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set1);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set2);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+0] and array[i-0]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, add0);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, sub0);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set3);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set5);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+1] and array[i-1]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, add1);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, sub1);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set4);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set6);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+1] and array[1-i]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, add1);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, rev_sub1);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set4);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set7);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+1] and array[i-(-1)]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, add1);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_neg1);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set4);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set8);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 }
 
@@ -409,70 +413,75 @@
   size_t loc1, loc2;
 
   // Test alias: array[0] and array[0,1,2,3]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, c0);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_0);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
+  // Test alias: array[0] and array[1,2,3,4]
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_1);
+  ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
+
   // Test alias: array[0] and array[8,9,10,11]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, c0);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_8);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[1] and array[8,9,10,11]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, c1);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_1);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_8);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[1] and array[0,1,2,3]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, c1);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_1);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_0);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[0,1,2,3] and array[8,9,10,11]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, c0, 4);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(vstore_0);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_8);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[0,1,2,3] and array[1,2,3,4]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, c1, 4);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(vstore_0);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_1);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[0] and array[i,i+1,i+2,i+3]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, c0);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i] and array[0,1,2,3]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, index);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_0);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i] and array[i,i+1,i+2,i+3]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, index);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i] and array[i+8,i+9,i+10,i+11]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, index);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i_add8);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+6,i+7,i+8,i+9] and array[i+8,i+9,i+10,i+11]
   // Test partial overlap.
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 4);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(vstore_i_add6);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i_add8);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+6,i+7] and array[i,i+1,i+2,i+3]
   // Test different vector lengths.
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 2);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(vstore_i_add6_vlen2);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+6,i+7] and array[i+8,i+9,i+10,i+11]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 2);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4);
+  loc1 = heap_location_collector.GetArrayHeapLocation(vstore_i_add6_vlen2);
+  loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i_add8);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 }
 
@@ -563,33 +572,33 @@
   size_t loc2 = HeapLocationCollector::kHeapLocationNotFound;
 
   // Test alias: array[i+0x80000000] and array[i-0x80000000]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x80000000);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_1);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_2);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+0x10] and array[i-0xFFFFFFF0]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x10);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0xFFFFFFF0);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_3);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_4);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+0x7FFFFFFF] and array[i-0x80000001]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x7FFFFFFF);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_5);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_6);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Test alias: array[i+0] and array[i-0]
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_7);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_8);
   ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Should not alias:
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_2);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_6);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 
   // Should not alias:
-  loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0);
-  loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000);
+  loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_7);
+  loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_2);
   ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
 }
 
@@ -647,10 +656,10 @@
   // times the original reference has been transformed by BoundType,
   // NullCheck, IntermediateAddress, etc.
   ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 1U);
-  size_t loc1 = heap_location_collector.GetArrayHeapLocation(array, c1);
-  size_t loc2 = heap_location_collector.GetArrayHeapLocation(bound_type, c1);
-  size_t loc3 = heap_location_collector.GetArrayHeapLocation(null_check, c1);
-  size_t loc4 = heap_location_collector.GetArrayHeapLocation(inter_addr, c1);
+  size_t loc1 = heap_location_collector.GetArrayHeapLocation(array_get1);
+  size_t loc2 = heap_location_collector.GetArrayHeapLocation(array_get2);
+  size_t loc3 = heap_location_collector.GetArrayHeapLocation(array_get3);
+  size_t loc4 = heap_location_collector.GetArrayHeapLocation(array_get4);
   ASSERT_TRUE(loc1 != HeapLocationCollector::kHeapLocationNotFound);
   ASSERT_EQ(loc1, loc2);
   ASSERT_EQ(loc1, loc3);
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 237ecd3..28ac942 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -160,7 +160,7 @@
 
   // Scan the list of removed loads to see if we can reuse `type_conversion`, if
   // the other removed load has the same substitute and type and is dominated
-  // by `type_conversioni`.
+  // by `type_conversion`.
   void TryToReuseTypeConversion(HInstruction* type_conversion, size_t index) {
     size_t size = removed_loads_.size();
     HInstruction* load = removed_loads_[index];
@@ -458,8 +458,13 @@
       }
       if (from_all_predecessors) {
         if (ref_info->IsSingletonAndRemovable() &&
-            block->IsSingleReturnOrReturnVoidAllowingPhis()) {
-          // Values in the singleton are not needed anymore.
+            (block->IsSingleReturnOrReturnVoidAllowingPhis() ||
+             (block->EndsWithReturn() && (merged_value != kUnknownHeapValue ||
+                                          merged_store_value != kUnknownHeapValue)))) {
+          // Values in the singleton are not needed anymore:
+          // (1) if this block consists of a sole return, or
+          // (2) if this block returns and a usable merged value is obtained
+          //     (loads prior to the return will always use that value).
         } else if (!IsStore(merged_value)) {
           // We don't track merged value as a store anymore. We have to
           // hold the stores in predecessors live here.
@@ -542,16 +547,7 @@
     }
   }
 
-  void VisitGetLocation(HInstruction* instruction,
-                        HInstruction* ref,
-                        size_t offset,
-                        HInstruction* index,
-                        size_t vector_length,
-                        int16_t declaring_class_def_index) {
-    HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref);
-    ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(original_ref);
-    size_t idx = heap_location_collector_.FindHeapLocationIndex(
-        ref_info, offset, index, vector_length, declaring_class_def_index);
+  void VisitGetLocation(HInstruction* instruction, size_t idx) {
     DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound);
     ScopedArenaVector<HInstruction*>& heap_values =
         heap_values_for_[instruction->GetBlock()->GetBlockId()];
@@ -569,23 +565,7 @@
       heap_values[idx] = instruction;
       KeepStoresIfAliasedToLocation(heap_values, idx);
     } else {
-      if (DataType::Kind(heap_value->GetType()) != DataType::Kind(instruction->GetType())) {
-        // The only situation where the same heap location has different type is when
-        // we do an array get on an instruction that originates from the null constant
-        // (the null could be behind a field access, an array access, a null check or
-        // a bound type).
-        // In order to stay properly typed on primitive types, we do not eliminate
-        // the array gets.
-        if (kIsDebugBuild) {
-          DCHECK(heap_value->IsArrayGet()) << heap_value->DebugName();
-          DCHECK(instruction->IsArrayGet()) << instruction->DebugName();
-        }
-        // Load isn't eliminated. Put the load as the value into the HeapLocation.
-        // This acts like GVN but with better aliasing analysis.
-        heap_values[idx] = instruction;
-        KeepStoresIfAliasedToLocation(heap_values, idx);
-        return;
-      }
+      // Load is eliminated.
       AddRemovedLoad(instruction, heap_value);
       TryRemovingNullCheck(instruction);
     }
@@ -610,21 +590,11 @@
     return false;
   }
 
-  void VisitSetLocation(HInstruction* instruction,
-                        HInstruction* ref,
-                        size_t offset,
-                        HInstruction* index,
-                        size_t vector_length,
-                        int16_t declaring_class_def_index,
-                        HInstruction* value) {
+  void VisitSetLocation(HInstruction* instruction, size_t idx, HInstruction* value) {
+    DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound);
     DCHECK(!IsStore(value)) << value->DebugName();
     // value may already have a substitute.
     value = FindSubstitute(value);
-    HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref);
-    ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(original_ref);
-    size_t idx = heap_location_collector_.FindHeapLocationIndex(
-        ref_info, offset, index, vector_length, declaring_class_def_index);
-    DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound);
     ScopedArenaVector<HInstruction*>& heap_values =
         heap_values_for_[instruction->GetBlock()->GetBlockId()];
     HInstruction* heap_value = heap_values[idx];
@@ -644,7 +614,8 @@
       } else if (!loop_info->IsIrreducible()) {
         // instruction is a store in the loop so the loop must do write.
         DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite());
-        if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(original_ref)) {
+        ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo();
+        if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(ref_info->GetReference())) {
           // original_ref is created inside the loop. Value stored to it isn't needed at
           // the loop header. This is true for outer loops also.
           possibly_redundant = true;
@@ -686,79 +657,39 @@
   }
 
   void VisitInstanceFieldGet(HInstanceFieldGet* instruction) OVERRIDE {
-    HInstruction* obj = instruction->InputAt(0);
-    size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue();
-    int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex();
-    VisitGetLocation(instruction,
-                     obj,
-                     offset,
-                     nullptr,
-                     HeapLocation::kScalar,
-                     declaring_class_def_index);
+    HInstruction* object = instruction->InputAt(0);
+    const FieldInfo& field = instruction->GetFieldInfo();
+    VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(object, &field));
   }
 
   void VisitInstanceFieldSet(HInstanceFieldSet* instruction) OVERRIDE {
-    HInstruction* obj = instruction->InputAt(0);
-    size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue();
-    int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex();
+    HInstruction* object = instruction->InputAt(0);
+    const FieldInfo& field = instruction->GetFieldInfo();
     HInstruction* value = instruction->InputAt(1);
-    VisitSetLocation(instruction,
-                     obj,
-                     offset,
-                     nullptr,
-                     HeapLocation::kScalar,
-                     declaring_class_def_index,
-                     value);
+    size_t idx = heap_location_collector_.GetFieldHeapLocation(object, &field);
+    VisitSetLocation(instruction, idx, value);
   }
 
   void VisitStaticFieldGet(HStaticFieldGet* instruction) OVERRIDE {
     HInstruction* cls = instruction->InputAt(0);
-    size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue();
-    int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex();
-    VisitGetLocation(instruction,
-                     cls,
-                     offset,
-                     nullptr,
-                     HeapLocation::kScalar,
-                     declaring_class_def_index);
+    const FieldInfo& field = instruction->GetFieldInfo();
+    VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(cls, &field));
   }
 
   void VisitStaticFieldSet(HStaticFieldSet* instruction) OVERRIDE {
     HInstruction* cls = instruction->InputAt(0);
-    size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue();
-    int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex();
-    HInstruction* value = instruction->InputAt(1);
-    VisitSetLocation(instruction,
-                     cls,
-                     offset,
-                     nullptr,
-                     HeapLocation::kScalar,
-                     declaring_class_def_index,
-                     value);
+    const FieldInfo& field = instruction->GetFieldInfo();
+    size_t idx = heap_location_collector_.GetFieldHeapLocation(cls, &field);
+    VisitSetLocation(instruction, idx, instruction->InputAt(1));
   }
 
   void VisitArrayGet(HArrayGet* instruction) OVERRIDE {
-    HInstruction* array = instruction->InputAt(0);
-    HInstruction* index = instruction->InputAt(1);
-    VisitGetLocation(instruction,
-                     array,
-                     HeapLocation::kInvalidFieldOffset,
-                     index,
-                     HeapLocation::kScalar,
-                     HeapLocation::kDeclaringClassDefIndexForArrays);
+    VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction));
   }
 
   void VisitArraySet(HArraySet* instruction) OVERRIDE {
-    HInstruction* array = instruction->InputAt(0);
-    HInstruction* index = instruction->InputAt(1);
-    HInstruction* value = instruction->InputAt(2);
-    VisitSetLocation(instruction,
-                     array,
-                     HeapLocation::kInvalidFieldOffset,
-                     index,
-                     HeapLocation::kScalar,
-                     HeapLocation::kDeclaringClassDefIndexForArrays,
-                     value);
+    size_t idx = heap_location_collector_.GetArrayHeapLocation(instruction);
+    VisitSetLocation(instruction, idx, instruction->InputAt(2));
   }
 
   void VisitDeoptimize(HDeoptimize* instruction) {
@@ -948,22 +879,22 @@
   DISALLOW_COPY_AND_ASSIGN(LSEVisitor);
 };
 
-void LoadStoreElimination::Run() {
+bool LoadStoreElimination::Run() {
   if (graph_->IsDebuggable() || graph_->HasTryCatch()) {
     // Debugger may set heap values or trigger deoptimization of callers.
     // Try/catch support not implemented yet.
     // Skip this optimization.
-    return;
+    return false;
   }
   const HeapLocationCollector& heap_location_collector = lsa_.GetHeapLocationCollector();
   if (heap_location_collector.GetNumberOfHeapLocations() == 0) {
     // No HeapLocation information from LSA, skip this optimization.
-    return;
+    return false;
   }
 
   // TODO: analyze VecLoad/VecStore better.
   if (graph_->HasSIMD()) {
-    return;
+    return false;
   }
 
   LSEVisitor lse_visitor(graph_, heap_location_collector, side_effects_, stats_);
@@ -971,6 +902,8 @@
     lse_visitor.VisitBasicBlock(block);
   }
   lse_visitor.RemoveInstructions();
+
+  return true;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h
index 7153541..408386b 100644
--- a/compiler/optimizing/load_store_elimination.h
+++ b/compiler/optimizing/load_store_elimination.h
@@ -35,7 +35,7 @@
         side_effects_(side_effects),
         lsa_(lsa) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kLoadStoreEliminationPassName = "load_store_elimination";
 
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 1462404..1ce3524 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -153,18 +153,6 @@
         return false;
     }
   }
-  // A MIN-MAX on narrower operands qualifies as well
-  // (returning the operator itself).
-  if (instruction->IsMin() || instruction->IsMax()) {
-    HBinaryOperation* min_max = instruction->AsBinaryOperation();
-    DCHECK(min_max->GetType() == DataType::Type::kInt32 ||
-           min_max->GetType() == DataType::Type::kInt64);
-    if (IsSignExtensionAndGet(min_max->InputAt(0), type, operand) &&
-        IsSignExtensionAndGet(min_max->InputAt(1), type, operand)) {
-      *operand = min_max;
-      return true;
-    }
-  }
   return false;
 }
 
@@ -228,18 +216,6 @@
         return false;
     }
   }
-  // A MIN-MAX on narrower operands qualifies as well
-  // (returning the operator itself).
-  if (instruction->IsMin() || instruction->IsMax()) {
-    HBinaryOperation* min_max = instruction->AsBinaryOperation();
-    DCHECK(min_max->GetType() == DataType::Type::kInt32 ||
-           min_max->GetType() == DataType::Type::kInt64);
-    if (IsZeroExtensionAndGet(min_max->InputAt(0), type, operand) &&
-        IsZeroExtensionAndGet(min_max->InputAt(1), type, operand)) {
-      *operand = min_max;
-      return true;
-    }
-  }
   return false;
 }
 
@@ -363,128 +339,11 @@
   return false;
 }
 
-// Detect clipped [lo, hi] range for nested MIN-MAX operations on a clippee,
-// such as MIN(hi, MAX(lo, clippee)) for an arbitrary clippee expression.
-// Example: MIN(10, MIN(20, MAX(0, x))) yields [0, 10] with clippee x.
-static HInstruction* FindClippee(HInstruction* instruction,
-                                 /*out*/ int64_t* lo,
-                                 /*out*/ int64_t* hi) {
-  // Iterate into MIN(.., c)-MAX(.., c) expressions and 'tighten' the range [lo, hi].
-  while (instruction->IsMin() || instruction->IsMax()) {
-    HBinaryOperation* min_max = instruction->AsBinaryOperation();
-    DCHECK(min_max->GetType() == DataType::Type::kInt32 ||
-           min_max->GetType() == DataType::Type::kInt64);
-    // Process the constant.
-    HConstant* right = min_max->GetConstantRight();
-    if (right == nullptr) {
-      break;
-    } else if (instruction->IsMin()) {
-      *hi = std::min(*hi, Int64FromConstant(right));
-    } else {
-      *lo = std::max(*lo, Int64FromConstant(right));
-    }
-    instruction = min_max->GetLeastConstantLeft();
-  }
-  // Iteration ends in any other expression (possibly MIN/MAX without constant).
-  // This leaf expression is the clippee with range [lo, hi].
-  return instruction;
-}
-
-// Set value range for type (or fail).
-static bool CanSetRange(DataType::Type type,
-                        /*out*/ int64_t* uhi,
-                        /*out*/ int64_t* slo,
-                        /*out*/ int64_t* shi) {
-  if (DataType::Size(type) == 1) {
-    *uhi = std::numeric_limits<uint8_t>::max();
-    *slo = std::numeric_limits<int8_t>::min();
-    *shi = std::numeric_limits<int8_t>::max();
-    return true;
-  } else if (DataType::Size(type) == 2) {
-    *uhi = std::numeric_limits<uint16_t>::max();
-    *slo = std::numeric_limits<int16_t>::min();
-    *shi = std::numeric_limits<int16_t>::max();
-    return true;
-  }
-  return false;
-}
-
-// Accept various saturated addition forms.
-static bool IsSaturatedAdd(HInstruction* a,
-                           HInstruction* b,
-                           DataType::Type type,
-                           int64_t lo,
-                           int64_t hi,
-                           bool is_unsigned) {
-  int64_t ulo = 0, uhi = 0, slo = 0, shi = 0;
-  if (!CanSetRange(type, &uhi, &slo, &shi)) {
-    return false;
-  }
-  // Tighten the range for signed single clipping on constant.
-  if (!is_unsigned) {
-    int64_t c = 0;
-    if (IsInt64AndGet(a, &c) || IsInt64AndGet(b, &c)) {
-      // For c in proper range and narrower operand r:
-      //    MIN(r + c,  127) c > 0
-      // or MAX(r + c, -128) c < 0 (and possibly redundant bound).
-      if (0 < c && c <= shi && hi == shi) {
-        if (lo <= (slo + c)) {
-          return true;
-        }
-      } else if (slo <= c && c < 0 && lo == slo) {
-        if (hi >= (shi + c)) {
-          return true;
-        }
-      }
-    }
-  }
-  // Detect for narrower operands r and s:
-  //     MIN(r + s, 255)        => SAT_ADD_unsigned
-  // MAX(MIN(r + s, 127), -128) => SAT_ADD_signed.
-  return is_unsigned ? (lo <= ulo && hi == uhi) : (lo == slo && hi == shi);
-}
-
-// Accept various saturated subtraction forms.
-static bool IsSaturatedSub(HInstruction* a,
-                           DataType::Type type,
-                           int64_t lo,
-                           int64_t hi,
-                           bool is_unsigned) {
-  int64_t ulo = 0, uhi = 0, slo = 0, shi = 0;
-  if (!CanSetRange(type, &uhi, &slo, &shi)) {
-    return false;
-  }
-  // Tighten the range for signed single clipping on constant.
-  if (!is_unsigned) {
-    int64_t c = 0;
-    if (IsInt64AndGet(a, /*out*/ &c)) {
-      // For c in proper range and narrower operand r:
-      //    MIN(c - r,  127) c > 0
-      // or MAX(c - r, -128) c < 0 (and possibly redundant bound).
-      if (0 < c && c <= shi && hi == shi) {
-        if (lo <= (c - shi)) {
-          return true;
-        }
-      } else if (slo <= c && c < 0 && lo == slo) {
-        if (hi >= (c - slo)) {
-          return true;
-        }
-      }
-    }
-  }
-  // Detect for narrower operands r and s:
-  //     MAX(r - s, 0)          => SAT_SUB_unsigned
-  // MIN(MAX(r - s, -128), 127) => SAT_ADD_signed.
-  return is_unsigned ? (lo == ulo && hi >= uhi) : (lo == slo && hi == shi);
-}
-
 // Detect reductions of the following forms,
 //   x = x_phi + ..
 //   x = x_phi - ..
-//   x = min(x_phi, ..)
-//   x = max(x_phi, ..)
 static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) {
-  if (reduction->IsAdd() || reduction->IsMin() || reduction->IsMax()) {
+  if (reduction->IsAdd()) {
     return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) ||
            (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi);
   } else if (reduction->IsSub()) {
@@ -497,10 +356,6 @@
 static HVecReduce::ReductionKind GetReductionKind(HVecOperation* reduction) {
   if (reduction->IsVecAdd() || reduction->IsVecSub() || reduction->IsVecSADAccumulate()) {
     return HVecReduce::kSum;
-  } else if (reduction->IsVecMin()) {
-    return HVecReduce::kMin;
-  } else if (reduction->IsVecMax()) {
-    return HVecReduce::kMax;
   }
   LOG(FATAL) << "Unsupported SIMD reduction " << reduction->GetId();
   UNREACHABLE();
@@ -608,11 +463,11 @@
                                                       global_allocator_)) {
 }
 
-void HLoopOptimization::Run() {
+bool HLoopOptimization::Run() {
   // Skip if there is no loop or the graph has try-catch/irreducible loops.
   // TODO: make this less of a sledgehammer.
   if (!graph_->HasLoops() || graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) {
-    return;
+    return false;
   }
 
   // Phase-local allocator.
@@ -620,7 +475,7 @@
   loop_allocator_ = &allocator;
 
   // Perform loop optimizations.
-  LocalRun();
+  bool didLoopOpt = LocalRun();
   if (top_loop_ == nullptr) {
     graph_->SetHasLoops(false);  // no more loops
   }
@@ -628,13 +483,16 @@
   // Detach.
   loop_allocator_ = nullptr;
   last_loop_ = top_loop_ = nullptr;
+
+  return didLoopOpt;
 }
 
 //
 // Loop setup and traversal.
 //
 
-void HLoopOptimization::LocalRun() {
+bool HLoopOptimization::LocalRun() {
+  bool didLoopOpt = false;
   // Build the linear order using the phase-local allocator. This step enables building
   // a loop hierarchy that properly reflects the outer-inner and previous-next relation.
   ScopedArenaVector<HBasicBlock*> linear_order(loop_allocator_->Adapter(kArenaAllocLinearOrder));
@@ -666,7 +524,7 @@
     vector_map_ = &map;
     vector_permanent_map_ = &perm;
     // Traverse.
-    TraverseLoopsInnerToOuter(top_loop_);
+    didLoopOpt = TraverseLoopsInnerToOuter(top_loop_);
     // Detach.
     iset_ = nullptr;
     reductions_ = nullptr;
@@ -674,6 +532,7 @@
     vector_map_ = nullptr;
     vector_permanent_map_ = nullptr;
   }
+  return didLoopOpt;
 }
 
 void HLoopOptimization::AddLoop(HLoopInformation* loop_info) {
@@ -730,6 +589,7 @@
     // loop if the induction of any inner loop has changed.
     if (TraverseLoopsInnerToOuter(node->inner)) {
       induction_range_.ReVisit(node->loop_info);
+      changed = true;
     }
     // Repeat simplifications in the loop-body until no more changes occur.
     // Note that since each simplification consists of eliminating code (without
@@ -1597,37 +1457,6 @@
       }
       return true;
     }
-  } else if (instruction->IsMin() || instruction->IsMax()) {
-    // Recognize saturation arithmetic.
-    if (VectorizeSaturationIdiom(node, instruction, generate_code, type, restrictions)) {
-      return true;
-    }
-    // Deal with vector restrictions.
-    HInstruction* opa = instruction->InputAt(0);
-    HInstruction* opb = instruction->InputAt(1);
-    HInstruction* r = opa;
-    HInstruction* s = opb;
-    bool is_unsigned = false;
-    if (HasVectorRestrictions(restrictions, kNoMinMax)) {
-      return false;
-    } else if (HasVectorRestrictions(restrictions, kNoHiBits) &&
-               !IsNarrowerOperands(opa, opb, type, &r, &s, &is_unsigned)) {
-      return false;  // reject, unless all operands are same-extension narrower
-    }
-    // Accept MIN/MAX(x, y) for vectorizable operands.
-    DCHECK(r != nullptr && s != nullptr);
-    if (generate_code && vector_mode_ != kVector) {  // de-idiom
-      r = opa;
-      s = opb;
-    }
-    if (VectorizeUse(node, r, generate_code, type, restrictions) &&
-        VectorizeUse(node, s, generate_code, type, restrictions)) {
-      if (generate_code) {
-        GenerateVecOp(
-            instruction, vector_map_->Get(r), vector_map_->Get(s), type, is_unsigned);
-      }
-      return true;
-    }
   }
   return false;
 }
@@ -1683,7 +1512,7 @@
           *restrictions |= kNoDiv;
           return TrySetVectorLength(4);
         case DataType::Type::kInt64:
-          *restrictions |= kNoDiv | kNoMul | kNoMinMax;
+          *restrictions |= kNoDiv | kNoMul;
           return TrySetVectorLength(2);
         case DataType::Type::kFloat32:
           *restrictions |= kNoReduction;
@@ -1713,13 +1542,13 @@
             *restrictions |= kNoDiv | kNoSAD;
             return TrySetVectorLength(4);
           case DataType::Type::kInt64:
-            *restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoMinMax | kNoSAD;
+            *restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoSAD;
             return TrySetVectorLength(2);
           case DataType::Type::kFloat32:
-            *restrictions |= kNoMinMax | kNoReduction;  // minmax: -0.0 vs +0.0
+            *restrictions |= kNoReduction;
             return TrySetVectorLength(4);
           case DataType::Type::kFloat64:
-            *restrictions |= kNoMinMax | kNoReduction;  // minmax: -0.0 vs +0.0
+            *restrictions |= kNoReduction;
             return TrySetVectorLength(2);
           default:
             break;
@@ -1732,11 +1561,11 @@
           case DataType::Type::kBool:
           case DataType::Type::kUint8:
           case DataType::Type::kInt8:
-            *restrictions |= kNoDiv | kNoSaturation;
+            *restrictions |= kNoDiv;
             return TrySetVectorLength(16);
           case DataType::Type::kUint16:
           case DataType::Type::kInt16:
-            *restrictions |= kNoDiv | kNoSaturation | kNoStringCharAt;
+            *restrictions |= kNoDiv | kNoStringCharAt;
             return TrySetVectorLength(8);
           case DataType::Type::kInt32:
             *restrictions |= kNoDiv;
@@ -1745,10 +1574,10 @@
             *restrictions |= kNoDiv;
             return TrySetVectorLength(2);
           case DataType::Type::kFloat32:
-            *restrictions |= kNoMinMax | kNoReduction;  // min/max(x, NaN)
+            *restrictions |= kNoReduction;
             return TrySetVectorLength(4);
           case DataType::Type::kFloat64:
-            *restrictions |= kNoMinMax | kNoReduction;  // min/max(x, NaN)
+            *restrictions |= kNoReduction;
             return TrySetVectorLength(2);
           default:
             break;
@@ -1761,11 +1590,11 @@
           case DataType::Type::kBool:
           case DataType::Type::kUint8:
           case DataType::Type::kInt8:
-            *restrictions |= kNoDiv | kNoSaturation;
+            *restrictions |= kNoDiv;
             return TrySetVectorLength(16);
           case DataType::Type::kUint16:
           case DataType::Type::kInt16:
-            *restrictions |= kNoDiv | kNoSaturation | kNoStringCharAt;
+            *restrictions |= kNoDiv | kNoStringCharAt;
             return TrySetVectorLength(8);
           case DataType::Type::kInt32:
             *restrictions |= kNoDiv;
@@ -1774,10 +1603,10 @@
             *restrictions |= kNoDiv;
             return TrySetVectorLength(2);
           case DataType::Type::kFloat32:
-            *restrictions |= kNoMinMax | kNoReduction;  // min/max(x, NaN)
+            *restrictions |= kNoReduction;
             return TrySetVectorLength(4);
           case DataType::Type::kFloat64:
-            *restrictions |= kNoMinMax | kNoReduction;  // min/max(x, NaN)
+            *restrictions |= kNoReduction;
             return TrySetVectorLength(2);
           default:
             break;
@@ -2002,8 +1831,7 @@
 void HLoopOptimization::GenerateVecOp(HInstruction* org,
                                       HInstruction* opa,
                                       HInstruction* opb,
-                                      DataType::Type type,
-                                      bool is_unsigned) {
+                                      DataType::Type type) {
   uint32_t dex_pc = org->GetDexPc();
   HInstruction* vector = nullptr;
   DataType::Type org_type = org->GetType();
@@ -2068,24 +1896,6 @@
       GENERATE_VEC(
         new (global_allocator_) HVecUShr(global_allocator_, opa, opb, type, vector_length_, dex_pc),
         new (global_allocator_) HUShr(org_type, opa, opb, dex_pc));
-    case HInstruction::kMin:
-      GENERATE_VEC(
-        new (global_allocator_) HVecMin(global_allocator_,
-                                        opa,
-                                        opb,
-                                        HVecOperation::ToProperType(type, is_unsigned),
-                                        vector_length_,
-                                        dex_pc),
-        new (global_allocator_) HMin(org_type, opa, opb, dex_pc));
-    case HInstruction::kMax:
-      GENERATE_VEC(
-        new (global_allocator_) HVecMax(global_allocator_,
-                                        opa,
-                                        opb,
-                                        HVecOperation::ToProperType(type, is_unsigned),
-                                        vector_length_,
-                                        dex_pc),
-        new (global_allocator_) HMax(org_type, opa, opb, dex_pc));
     case HInstruction::kAbs:
       DCHECK(opb == nullptr);
       GENERATE_VEC(
@@ -2104,79 +1914,6 @@
 // Vectorization idioms.
 //
 
-// Method recognizes single and double clipping saturation arithmetic.
-bool HLoopOptimization::VectorizeSaturationIdiom(LoopNode* node,
-                                                 HInstruction* instruction,
-                                                 bool generate_code,
-                                                 DataType::Type type,
-                                                 uint64_t restrictions) {
-  // Deal with vector restrictions.
-  if (HasVectorRestrictions(restrictions, kNoSaturation)) {
-    return false;
-  }
-  // Restrict type (generalize if one day we generalize allowed MIN/MAX integral types).
-  if (instruction->GetType() != DataType::Type::kInt32 &&
-      instruction->GetType() != DataType::Type::kInt64) {
-    return false;
-  }
-  // Clipped addition or subtraction on narrower operands? We will try both
-  // formats since, e.g., x+c can be interpreted as x+c and x-(-c), depending
-  // on what clipping values are used, to get most benefits.
-  int64_t lo = std::numeric_limits<int64_t>::min();
-  int64_t hi = std::numeric_limits<int64_t>::max();
-  HInstruction* clippee = FindClippee(instruction, &lo, &hi);
-  HInstruction* a = nullptr;
-  HInstruction* b = nullptr;
-  HInstruction* r = nullptr;
-  HInstruction* s = nullptr;
-  bool is_unsigned = false;
-  bool is_add = true;
-  int64_t c = 0;
-  // First try for saturated addition.
-  if (IsAddConst2(graph_, clippee, /*out*/ &a, /*out*/ &b, /*out*/ &c) && c == 0 &&
-      IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) &&
-      IsSaturatedAdd(r, s, type, lo, hi, is_unsigned)) {
-    is_add = true;
-  } else {
-    // Then try again for saturated subtraction.
-    a = b = r = s = nullptr;
-    if (IsSubConst2(graph_, clippee, /*out*/ &a, /*out*/ &b) &&
-        IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) &&
-        IsSaturatedSub(r, type, lo, hi, is_unsigned)) {
-      is_add = false;
-    } else {
-      return false;
-    }
-  }
-  // Accept saturation idiom for vectorizable operands.
-  DCHECK(r != nullptr && s != nullptr);
-  if (generate_code && vector_mode_ != kVector) {  // de-idiom
-    r = instruction->InputAt(0);
-    s = instruction->InputAt(1);
-    restrictions &= ~(kNoHiBits | kNoMinMax);  // allow narrow MIN/MAX in seq
-  }
-  if (VectorizeUse(node, r, generate_code, type, restrictions) &&
-      VectorizeUse(node, s, generate_code, type, restrictions)) {
-    if (generate_code) {
-      if (vector_mode_ == kVector) {
-        DataType::Type vtype = HVecOperation::ToProperType(type, is_unsigned);
-        HInstruction* op1 = vector_map_->Get(r);
-        HInstruction* op2 = vector_map_->Get(s);
-        vector_map_->Put(instruction, is_add
-          ? reinterpret_cast<HInstruction*>(new (global_allocator_) HVecSaturationAdd(
-              global_allocator_, op1, op2, vtype, vector_length_, kNoDexPc))
-          : reinterpret_cast<HInstruction*>(new (global_allocator_) HVecSaturationSub(
-              global_allocator_, op1, op2, vtype, vector_length_, kNoDexPc)));
-        MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom);
-      } else {
-        GenerateVecOp(instruction, vector_map_->Get(r), vector_map_->Get(s), type);
-      }
-    }
-    return true;
-  }
-  return false;
-}
-
 // Method recognizes the following idioms:
 //   rounding  halving add (a + b + 1) >> 1 for unsigned/signed operands a, b
 //   truncated halving add (a + b)     >> 1 for unsigned/signed operands a, b
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index f9a31a3..7807da1 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -43,7 +43,7 @@
                     OptimizingCompilerStats* stats,
                     const char* name = kLoopOptimizationPassName);
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kLoopOptimizationPassName = "loop_optimization";
 
@@ -78,12 +78,10 @@
     kNoSignedHAdd    = 1 << 5,   // no signed halving add
     kNoUnroundedHAdd = 1 << 6,   // no unrounded halving add
     kNoAbs           = 1 << 7,   // no absolute value
-    kNoMinMax        = 1 << 8,   // no min/max
-    kNoStringCharAt  = 1 << 9,   // no StringCharAt
-    kNoReduction     = 1 << 10,  // no reduction
-    kNoSAD           = 1 << 11,  // no sum of absolute differences (SAD)
-    kNoWideSAD       = 1 << 12,  // no sum of absolute differences (SAD) with operand widening
-    kNoSaturation    = 1 << 13,  // no saturation arithmetic
+    kNoStringCharAt  = 1 << 8,   // no StringCharAt
+    kNoReduction     = 1 << 9,   // no reduction
+    kNoSAD           = 1 << 10,  // no sum of absolute differences (SAD)
+    kNoWideSAD       = 1 << 11,  // no sum of absolute differences (SAD) with operand widening
   };
 
   /*
@@ -123,7 +121,7 @@
   // Loop setup and traversal.
   //
 
-  void LocalRun();
+  bool LocalRun();
   void AddLoop(HLoopInformation* loop_info);
   void RemoveLoop(LoopNode* node);
 
@@ -188,8 +186,7 @@
   void GenerateVecOp(HInstruction* org,
                      HInstruction* opa,
                      HInstruction* opb,
-                     DataType::Type type,
-                     bool is_unsigned = false);
+                     DataType::Type type);
 
   // Vectorization idioms.
   bool VectorizeSaturationIdiom(LoopNode* node,
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index f784f8f..7f78dc2 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1680,10 +1680,9 @@
 }
 
 bool HInstruction::Equals(const HInstruction* other) const {
-  if (!InstructionTypeEquals(other)) return false;
-  DCHECK_EQ(GetKind(), other->GetKind());
-  if (!InstructionDataEquals(other)) return false;
+  if (GetKind() != other->GetKind()) return false;
   if (GetType() != other->GetType()) return false;
+  if (!InstructionDataEquals(other)) return false;
   HConstInputsRef inputs = GetInputs();
   HConstInputsRef other_inputs = other->GetInputs();
   if (inputs.size() != other_inputs.size()) return false;
@@ -1698,7 +1697,7 @@
 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)
+    FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_CASE)
     default:
       os << "Unknown instruction kind " << static_cast<int>(rhs);
       break;
@@ -1952,6 +1951,11 @@
   return !GetInstructions().IsEmpty() && GetLastInstruction()->IsControlFlow();
 }
 
+bool HBasicBlock::EndsWithReturn() const {
+  return !GetInstructions().IsEmpty() &&
+      (GetLastInstruction()->IsReturn() || GetLastInstruction()->IsReturnVoid());
+}
+
 bool HBasicBlock::EndsWithIf() const {
   return !GetInstructions().IsEmpty() && GetLastInstruction()->IsIf();
 }
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 79d7330..09d9c57 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -41,6 +41,7 @@
 #include "intrinsics_enum.h"
 #include "locations.h"
 #include "mirror/class.h"
+#include "mirror/method_type.h"
 #include "offsets.h"
 #include "utils/intrusive_forward_list.h"
 
@@ -1284,6 +1285,7 @@
   void SetLifetimeEnd(size_t end) { lifetime_end_ = end; }
 
   bool EndsWithControlFlowInstruction() const;
+  bool EndsWithReturn() const;
   bool EndsWithIf() const;
   bool EndsWithTryBoundary() const;
   bool HasSinglePhi() const;
@@ -1382,6 +1384,8 @@
   M(LessThanOrEqual, Condition)                                         \
   M(LoadClass, Instruction)                                             \
   M(LoadException, Instruction)                                         \
+  M(LoadMethodHandle, Instruction)                                      \
+  M(LoadMethodType, Instruction)                                        \
   M(LoadString, Instruction)                                            \
   M(LongConstant, Constant)                                             \
   M(Max, Instruction)                                                   \
@@ -1525,9 +1529,6 @@
   H##type& operator=(const H##type&) = delete;                            \
   public:                                                                 \
   const char* DebugName() const OVERRIDE { return #type; }                \
-  bool InstructionTypeEquals(const HInstruction* other) const OVERRIDE {  \
-    return other->Is##type();                                             \
-  }                                                                       \
   HInstruction* Clone(ArenaAllocator* arena) const OVERRIDE {             \
     DCHECK(IsClonable());                                                 \
     return new (arena) H##type(*this->As##type());                        \
@@ -1537,10 +1538,7 @@
 #define DECLARE_ABSTRACT_INSTRUCTION(type)                              \
   private:                                                              \
   H##type& operator=(const H##type&) = delete;                          \
-  public:                                                               \
-  bool Is##type() const { return As##type() != nullptr; }               \
-  const H##type* As##type() const { return this; }                      \
-  H##type* As##type() { return this; }
+  public:
 
 #define DEFAULT_COPY_CONSTRUCTOR(type)                                  \
   explicit H##type(const H##type& other) = default;
@@ -1959,12 +1957,15 @@
  public:
 #define DECLARE_KIND(type, super) k##type,
   enum InstructionKind {
-    FOR_EACH_INSTRUCTION(DECLARE_KIND)
+    FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_KIND)
     kLastInstructionKind
   };
 #undef DECLARE_KIND
 
   HInstruction(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
+      : HInstruction(kind, DataType::Type::kVoid, side_effects, dex_pc) {}
+
+  HInstruction(InstructionKind kind, DataType::Type type, SideEffects side_effects, uint32_t dex_pc)
       : previous_(nullptr),
         next_(nullptr),
         block_(nullptr),
@@ -1979,6 +1980,7 @@
         side_effects_(side_effects),
         reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) {
     SetPackedField<InstructionKindField>(kind);
+    SetPackedField<TypeField>(type);
     SetPackedFlag<kFlagReferenceTypeIsExact>(ReferenceTypeInfo::CreateInvalid().IsExact());
   }
 
@@ -2036,7 +2038,9 @@
   virtual void Accept(HGraphVisitor* visitor) = 0;
   virtual const char* DebugName() const = 0;
 
-  virtual DataType::Type GetType() const { return DataType::Type::kVoid; }
+  DataType::Type GetType() const {
+    return TypeField::Decode(GetPackedFields());
+  }
 
   virtual bool NeedsEnvironment() const { return false; }
 
@@ -2228,19 +2232,17 @@
   void MoveBeforeFirstUserAndOutOfLoops();
 
 #define INSTRUCTION_TYPE_CHECK(type, super)                                    \
-  bool Is##type() const;                                                       \
+  bool Is##type() const;
+
+  FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
+#undef INSTRUCTION_TYPE_CHECK
+
+#define INSTRUCTION_TYPE_CAST(type, super)                                     \
   const H##type* As##type() const;                                             \
   H##type* As##type();
 
-  FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
-#undef INSTRUCTION_TYPE_CHECK
-
-#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_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
-#undef INSTRUCTION_TYPE_CHECK
+  FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CAST)
+#undef INSTRUCTION_TYPE_CAST
 
   // Return a clone of the instruction if it is clonable (shallow copy by default, custom copy
   // if a custom copy-constructor is provided for a particular type). If IsClonable() is false for
@@ -2266,11 +2268,6 @@
   //       meanings? split and rename?
   virtual bool CanBeMoved() const { return false; }
 
-  // Returns whether the two instructions are of the same kind.
-  virtual bool InstructionTypeEquals(const HInstruction* other ATTRIBUTE_UNUSED) const {
-    return false;
-  }
-
   // Returns whether any data encoded in the two instructions is equal.
   // This method does not look at the inputs. Both instructions must be
   // of the same type, otherwise the method has undefined behavior.
@@ -2342,13 +2339,18 @@
   static constexpr size_t kFieldInstructionKind = kFlagReferenceTypeIsExact + 1;
   static constexpr size_t kFieldInstructionKindSize =
       MinimumBitsToStore(static_cast<size_t>(InstructionKind::kLastInstructionKind - 1));
-  static constexpr size_t kNumberOfGenericPackedBits =
+  static constexpr size_t kFieldType =
       kFieldInstructionKind + kFieldInstructionKindSize;
+  static constexpr size_t kFieldTypeSize =
+      MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast));
+  static constexpr size_t kNumberOfGenericPackedBits = kFieldType + kFieldTypeSize;
   static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
 
   static_assert(kNumberOfGenericPackedBits <= kMaxNumberOfPackedBits,
                 "Too many generic packed fields");
 
+  using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
+
   const HUserRecord<HInstruction*> InputRecordAt(size_t i) const {
     return GetInputRecords()[i];
   }
@@ -2595,6 +2597,15 @@
                                 ArenaAllocKind kind)
       : HInstruction(inst_kind, side_effects, dex_pc),
         inputs_(number_of_inputs, allocator->Adapter(kind)) {}
+  HVariableInputSizeInstruction(InstructionKind inst_kind,
+                                DataType::Type type,
+                                SideEffects side_effects,
+                                uint32_t dex_pc,
+                                ArenaAllocator* allocator,
+                                size_t number_of_inputs,
+                                ArenaAllocKind kind)
+      : HInstruction(inst_kind, type, side_effects, dex_pc),
+        inputs_(number_of_inputs, allocator->Adapter(kind)) {}
 
   DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction);
 
@@ -2602,11 +2613,16 @@
 };
 
 template<size_t N>
-class HTemplateInstruction: public HInstruction {
+class HExpression : public HInstruction {
  public:
-  HTemplateInstruction<N>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
+  HExpression<N>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
       : HInstruction(kind, side_effects, dex_pc), inputs_() {}
-  virtual ~HTemplateInstruction() {}
+  HExpression<N>(InstructionKind kind,
+                 DataType::Type type,
+                 SideEffects side_effects,
+                 uint32_t dex_pc)
+      : HInstruction(kind, type, side_effects, dex_pc), inputs_() {}
+  virtual ~HExpression() {}
 
   using HInstruction::GetInputRecords;  // Keep the const version visible.
   ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
@@ -2614,7 +2630,7 @@
   }
 
  protected:
-  DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<N>);
+  DEFAULT_COPY_CONSTRUCTOR(Expression<N>);
 
  private:
   std::array<HUserRecord<HInstruction*>, N> inputs_;
@@ -2622,14 +2638,13 @@
   friend class SsaBuilder;
 };
 
-// HTemplateInstruction specialization for N=0.
+// HExpression specialization for N=0.
 template<>
-class HTemplateInstruction<0>: public HInstruction {
+class HExpression<0> : public HInstruction {
  public:
-  explicit HTemplateInstruction<0>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
-      : HInstruction(kind, side_effects, dex_pc) {}
+  using HInstruction::HInstruction;
 
-  virtual ~HTemplateInstruction() {}
+  virtual ~HExpression() {}
 
   using HInstruction::GetInputRecords;  // Keep the const version visible.
   ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
@@ -2637,46 +2652,18 @@
   }
 
  protected:
-  DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<0>);
+  DEFAULT_COPY_CONSTRUCTOR(Expression<0>);
 
  private:
   friend class SsaBuilder;
 };
 
-template<intptr_t N>
-class HExpression : public HTemplateInstruction<N> {
- public:
-  using HInstruction::InstructionKind;
-  HExpression<N>(InstructionKind kind,
-                 DataType::Type type,
-                 SideEffects side_effects,
-                 uint32_t dex_pc)
-      : HTemplateInstruction<N>(kind, side_effects, dex_pc) {
-    this->template SetPackedField<TypeField>(type);
-  }
-  virtual ~HExpression() {}
-
-  DataType::Type GetType() const OVERRIDE {
-    return TypeField::Decode(this->GetPackedFields());
-  }
-
- protected:
-  static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
-  static constexpr size_t kFieldTypeSize =
-      MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast));
-  static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize;
-  static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits,
-                "Too many packed fields.");
-  using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
-  DEFAULT_COPY_CONSTRUCTOR(Expression<N>);
-};
-
 // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow
 // instruction that branches to the exit block.
-class HReturnVoid FINAL : public HTemplateInstruction<0> {
+class HReturnVoid FINAL : public HExpression<0> {
  public:
   explicit HReturnVoid(uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(kReturnVoid, SideEffects::None(), dex_pc) {
+      : HExpression(kReturnVoid, SideEffects::None(), dex_pc) {
   }
 
   bool IsControlFlow() const OVERRIDE { return true; }
@@ -2689,10 +2676,10 @@
 
 // Represents dex's RETURN opcodes. A HReturn is a control flow
 // instruction that branches to the exit block.
-class HReturn FINAL : public HTemplateInstruction<1> {
+class HReturn FINAL : public HExpression<1> {
  public:
   explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(kReturn, SideEffects::None(), dex_pc) {
+      : HExpression(kReturn, SideEffects::None(), dex_pc) {
     SetRawInputAt(0, value);
   }
 
@@ -2713,13 +2700,13 @@
        uint32_t dex_pc = kNoDexPc)
       : HVariableInputSizeInstruction(
             kPhi,
+            ToPhiType(type),
             SideEffects::None(),
             dex_pc,
             allocator,
             number_of_inputs,
             kArenaAllocPhiInputs),
         reg_number_(reg_number) {
-    SetPackedField<TypeField>(ToPhiType(type));
     DCHECK_NE(GetType(), DataType::Type::kVoid);
     // Phis are constructed live and marked dead if conflicting or unused.
     // Individual steps of SsaBuilder should assume that if a phi has been
@@ -2737,7 +2724,6 @@
 
   bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
 
-  DataType::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
   void SetType(DataType::Type new_type) {
     // Make sure that only valid type changes occur. The following are allowed:
     //  (1) int  -> float/ref (primitive type propagation),
@@ -2796,14 +2782,10 @@
   DEFAULT_COPY_CONSTRUCTOR(Phi);
 
  private:
-  static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
-  static constexpr size_t kFieldTypeSize =
-      MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast));
-  static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize;
+  static constexpr size_t kFlagIsLive = HInstruction::kNumberOfGenericPackedBits;
   static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1;
   static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1;
   static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-  using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
 
   const uint32_t reg_number_;
 };
@@ -2811,10 +2793,10 @@
 // The exit instruction is the only instruction of the exit block.
 // Instructions aborting the method (HThrow and HReturn) must branch to the
 // exit block.
-class HExit FINAL : public HTemplateInstruction<0> {
+class HExit FINAL : public HExpression<0> {
  public:
   explicit HExit(uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(kExit, SideEffects::None(), dex_pc) {
+      : HExpression(kExit, SideEffects::None(), dex_pc) {
   }
 
   bool IsControlFlow() const OVERRIDE { return true; }
@@ -2826,10 +2808,10 @@
 };
 
 // Jumps from one block to another.
-class HGoto FINAL : public HTemplateInstruction<0> {
+class HGoto FINAL : public HExpression<0> {
  public:
   explicit HGoto(uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(kGoto, SideEffects::None(), dex_pc) {
+      : HExpression(kGoto, SideEffects::None(), dex_pc) {
   }
 
   bool IsClonable() const OVERRIDE { return true; }
@@ -3096,10 +3078,10 @@
 
 // Conditional branch. A block ending with an HIf instruction must have
 // two successors.
-class HIf FINAL : public HTemplateInstruction<1> {
+class HIf FINAL : public HExpression<1> {
  public:
   explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(kIf, SideEffects::None(), dex_pc) {
+      : HExpression(kIf, SideEffects::None(), dex_pc) {
     SetRawInputAt(0, input);
   }
 
@@ -3126,7 +3108,7 @@
 // non-exceptional control flow.
 // Normal-flow successor is stored at index zero, exception handlers under
 // higher indices in no particular order.
-class HTryBoundary FINAL : public HTemplateInstruction<0> {
+class HTryBoundary FINAL : public HExpression<0> {
  public:
   enum class BoundaryKind {
     kEntry,
@@ -3135,7 +3117,7 @@
   };
 
   explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(kTryBoundary, SideEffects::None(), dex_pc) {
+      : HExpression(kTryBoundary, SideEffects::None(), dex_pc) {
     SetPackedField<BoundaryKindField>(kind);
   }
 
@@ -3219,6 +3201,7 @@
               uint32_t dex_pc)
       : HVariableInputSizeInstruction(
             kDeoptimize,
+            guard->GetType(),
             SideEffects::CanTriggerGC(),
             dex_pc,
             allocator,
@@ -3242,10 +3225,6 @@
 
   DeoptimizationKind GetDeoptimizationKind() const { return GetPackedField<DeoptimizeKindField>(); }
 
-  DataType::Type GetType() const OVERRIDE {
-    return GuardsAnInput() ? GuardedInput()->GetType() : DataType::Type::kVoid;
-  }
-
   bool GuardsAnInput() const {
     return InputCount() == 2;
   }
@@ -3288,6 +3267,7 @@
   // with regard to other passes.
   HShouldDeoptimizeFlag(ArenaAllocator* allocator, uint32_t dex_pc)
       : HVariableInputSizeInstruction(kShouldDeoptimizeFlag,
+                                      DataType::Type::kInt32,
                                       SideEffects::None(),
                                       dex_pc,
                                       allocator,
@@ -3295,8 +3275,6 @@
                                       kArenaAllocCHA) {
   }
 
-  DataType::Type GetType() const OVERRIDE { return DataType::Type::kInt32; }
-
   // We do all CHA guard elimination/motion in a single pass, after which there is no
   // further guard elimination/motion since a guard might have been used for justification
   // of the elimination of another guard. Therefore, we pretend this guard cannot be moved
@@ -3360,7 +3338,7 @@
   DEFAULT_COPY_CONSTRUCTOR(ClassTableGet);
 
  private:
-  static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFieldTableKind = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldTableKindSize =
       MinimumBitsToStore(static_cast<size_t>(TableKind::kLast));
   static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize;
@@ -3375,13 +3353,13 @@
 // PackedSwitch (jump table). A block ending with a PackedSwitch instruction will
 // have one successor for each entry in the switch table, and the final successor
 // will be the block containing the next Dex opcode.
-class HPackedSwitch FINAL : public HTemplateInstruction<1> {
+class HPackedSwitch FINAL : public HExpression<1> {
  public:
   HPackedSwitch(int32_t start_value,
                 uint32_t num_entries,
                 HInstruction* input,
                 uint32_t dex_pc = kNoDexPc)
-    : HTemplateInstruction(kPackedSwitch, SideEffects::None(), dex_pc),
+    : HExpression(kPackedSwitch, SideEffects::None(), dex_pc),
       start_value_(start_value),
       num_entries_(num_entries) {
     SetRawInputAt(0, input);
@@ -3611,7 +3589,7 @@
 
  protected:
   // Needed if we merge a HCompare into a HCondition.
-  static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFieldComparisonBias = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldComparisonBiasSize =
       MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast));
   static constexpr size_t kNumberOfConditionPackedBits =
@@ -4131,7 +4109,7 @@
   DECLARE_INSTRUCTION(Compare);
 
  protected:
-  static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFieldComparisonBias = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldComparisonBiasSize =
       MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast));
   static constexpr size_t kNumberOfComparePackedBits =
@@ -4210,7 +4188,7 @@
   DEFAULT_COPY_CONSTRUCTOR(NewInstance);
 
  private:
-  static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFlagFinalizable = kNumberOfGenericPackedBits;
   static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1;
   static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits,
                 "Too many packed fields.");
@@ -4251,8 +4229,6 @@
   // inputs at the end of their list of inputs.
   uint32_t GetNumberOfArguments() const { return number_of_arguments_; }
 
-  DataType::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); }
-
   uint32_t GetDexMethodIndex() const { return dex_method_index_; }
 
   InvokeType GetInvokeType() const {
@@ -4305,16 +4281,11 @@
   static constexpr size_t kFieldInvokeType = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldInvokeTypeSize =
       MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType));
-  static constexpr size_t kFieldReturnType =
-      kFieldInvokeType + kFieldInvokeTypeSize;
-  static constexpr size_t kFieldReturnTypeSize =
-      MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast));
-  static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize;
+  static constexpr size_t kFlagCanThrow = kFieldInvokeType + kFieldInvokeTypeSize;
   static constexpr size_t kFlagAlwaysThrows = kFlagCanThrow + 1;
   static constexpr size_t kNumberOfInvokePackedBits = kFlagAlwaysThrows + 1;
   static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
   using InvokeTypeField = BitField<InvokeType, kFieldInvokeType, kFieldInvokeTypeSize>;
-  using ReturnTypeField = BitField<DataType::Type, kFieldReturnType, kFieldReturnTypeSize>;
 
   HInvoke(InstructionKind kind,
           ArenaAllocator* allocator,
@@ -4327,6 +4298,7 @@
           InvokeType invoke_type)
     : HVariableInputSizeInstruction(
           kind,
+          return_type,
           SideEffects::AllExceptGCDependency(),  // Assume write/read on all fields/arrays.
           dex_pc,
           allocator,
@@ -4337,7 +4309,6 @@
       dex_method_index_(dex_method_index),
       intrinsic_(Intrinsics::kNone),
       intrinsic_optimizations_(0) {
-    SetPackedField<ReturnTypeField>(return_type);
     SetPackedField<InvokeTypeField>(invoke_type);
     SetPackedFlag<kFlagCanThrow>(true);
   }
@@ -4550,7 +4521,7 @@
   }
 
   bool CanBeNull() const OVERRIDE {
-    return GetPackedField<ReturnTypeField>() == DataType::Type::kReference && !IsStringInit();
+    return GetType() == DataType::Type::kReference && !IsStringInit();
   }
 
   // Get the index of the special input, if any.
@@ -5146,8 +5117,6 @@
     SetRawInputAt(0, value);
   }
 
-  DataType::Type GetType() const OVERRIDE { return InputAt(0)->GetType(); }
-
   bool CanBeMoved() const OVERRIDE { return true; }
 
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
@@ -5500,7 +5469,7 @@
 
  private:
   // Whether or not the parameter value corresponds to 'this' argument.
-  static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFlagIsThis = kNumberOfGenericPackedBits;
   static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1;
   static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1;
   static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits,
@@ -5742,7 +5711,7 @@
   const FieldInfo field_info_;
 };
 
-class HInstanceFieldSet FINAL : public HTemplateInstruction<2> {
+class HInstanceFieldSet FINAL : public HExpression<2> {
  public:
   HInstanceFieldSet(HInstruction* object,
                     HInstruction* value,
@@ -5754,9 +5723,9 @@
                     uint16_t declaring_class_def_index,
                     const DexFile& dex_file,
                     uint32_t dex_pc)
-      : HTemplateInstruction(kInstanceFieldSet,
-                             SideEffects::FieldWriteOfType(field_type, is_volatile),
-                             dex_pc),
+      : HExpression(kInstanceFieldSet,
+                    SideEffects::FieldWriteOfType(field_type, is_volatile),
+                    dex_pc),
         field_info_(field,
                     field_offset,
                     field_type,
@@ -5882,13 +5851,13 @@
   // a particular HArrayGet is actually a String.charAt() by looking at the type
   // of the input but that requires holding the mutator lock, so we prefer to use
   // a flag, so that code generators don't need to do the locking.
-  static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFlagIsStringCharAt = kNumberOfGenericPackedBits;
   static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1;
   static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
                 "Too many packed fields.");
 };
 
-class HArraySet FINAL : public HTemplateInstruction<3> {
+class HArraySet FINAL : public HExpression<3> {
  public:
   HArraySet(HInstruction* array,
             HInstruction* index,
@@ -5910,7 +5879,7 @@
             DataType::Type expected_component_type,
             SideEffects side_effects,
             uint32_t dex_pc)
-      : HTemplateInstruction(kArraySet, side_effects, dex_pc) {
+      : HExpression(kArraySet, side_effects, dex_pc) {
     SetPackedField<ExpectedComponentTypeField>(expected_component_type);
     SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == DataType::Type::kReference);
     SetPackedFlag<kFlagValueCanBeNull>(true);
@@ -6039,7 +6008,7 @@
   // determine whether a particular HArrayLength is actually a String.length() by
   // looking at the type of the input but that requires holding the mutator lock, so
   // we prefer to use a flag, so that code generators don't need to do the locking.
-  static constexpr size_t kFlagIsStringLength = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFlagIsStringLength = kNumberOfGenericPackedBits;
   static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1;
   static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits,
                 "Too many packed fields.");
@@ -6080,13 +6049,13 @@
   DEFAULT_COPY_CONSTRUCTOR(BoundsCheck);
 
  private:
-  static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFlagIsStringCharAt = kNumberOfGenericPackedBits;
 };
 
-class HSuspendCheck FINAL : public HTemplateInstruction<0> {
+class HSuspendCheck FINAL : public HExpression<0> {
  public:
   explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc),
+      : HExpression(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc),
         slow_path_(nullptr) {
   }
 
@@ -6112,10 +6081,10 @@
 
 // Pseudo-instruction which provides the native debugger with mapping information.
 // It ensures that we can generate line number and local variables at this point.
-class HNativeDebugInfo : public HTemplateInstruction<0> {
+class HNativeDebugInfo : public HExpression<0> {
  public:
   explicit HNativeDebugInfo(uint32_t dex_pc)
-      : HTemplateInstruction<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) {
+      : HExpression<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) {
   }
 
   bool NeedsEnvironment() const OVERRIDE {
@@ -6174,7 +6143,10 @@
              bool is_referrers_class,
              uint32_t dex_pc,
              bool needs_access_check)
-      : HInstruction(kLoadClass, SideEffectsForArchRuntimeCalls(), dex_pc),
+      : HInstruction(kLoadClass,
+                     DataType::Type::kReference,
+                     SideEffectsForArchRuntimeCalls(),
+                     dex_pc),
         special_input_(HUserRecord<HInstruction*>(current_method)),
         type_index_(type_index),
         dex_file_(dex_file),
@@ -6285,10 +6257,6 @@
         &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u);
   }
 
-  DataType::Type GetType() const OVERRIDE {
-    return DataType::Type::kReference;
-  }
-
   Handle<mirror::Class> GetClass() const {
     return klass_;
   }
@@ -6399,7 +6367,10 @@
               dex::StringIndex string_index,
               const DexFile& dex_file,
               uint32_t dex_pc)
-      : HInstruction(kLoadString, SideEffectsForArchRuntimeCalls(), dex_pc),
+      : HInstruction(kLoadString,
+                     DataType::Type::kReference,
+                     SideEffectsForArchRuntimeCalls(),
+                     dex_pc),
         special_input_(HUserRecord<HInstruction*>(current_method)),
         string_index_(string_index),
         dex_file_(dex_file) {
@@ -6474,10 +6445,6 @@
         &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u);
   }
 
-  DataType::Type GetType() const OVERRIDE {
-    return DataType::Type::kReference;
-  }
-
   DECLARE_INSTRUCTION(LoadString);
 
  protected:
@@ -6535,6 +6502,94 @@
   special_input->AddUseAt(this, 0);
 }
 
+class HLoadMethodHandle FINAL : public HInstruction {
+ public:
+  HLoadMethodHandle(HCurrentMethod* current_method,
+                  uint16_t method_handle_idx,
+                  const DexFile& dex_file,
+                  uint32_t dex_pc)
+      : HInstruction(kLoadMethodHandle,
+                     DataType::Type::kReference,
+                     SideEffectsForArchRuntimeCalls(),
+                     dex_pc),
+        special_input_(HUserRecord<HInstruction*>(current_method)),
+        method_handle_idx_(method_handle_idx),
+        dex_file_(dex_file) {
+  }
+
+  using HInstruction::GetInputRecords;  // Keep the const version visible.
+  ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
+    return ArrayRef<HUserRecord<HInstruction*>>(
+        &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u);
+  }
+
+  bool IsClonable() const OVERRIDE { return true; }
+
+  uint16_t GetMethodHandleIndex() const { return method_handle_idx_; }
+
+  const DexFile& GetDexFile() const { return dex_file_; }
+
+  static SideEffects SideEffectsForArchRuntimeCalls() {
+    return SideEffects::CanTriggerGC();
+  }
+
+  DECLARE_INSTRUCTION(LoadMethodHandle);
+
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(LoadMethodHandle);
+
+ private:
+  // The special input is the HCurrentMethod for kRuntimeCall.
+  HUserRecord<HInstruction*> special_input_;
+
+  const uint16_t method_handle_idx_;
+  const DexFile& dex_file_;
+};
+
+class HLoadMethodType FINAL : public HInstruction {
+ public:
+  HLoadMethodType(HCurrentMethod* current_method,
+                  dex::ProtoIndex proto_index,
+                  const DexFile& dex_file,
+                  uint32_t dex_pc)
+      : HInstruction(kLoadMethodType,
+                     DataType::Type::kReference,
+                     SideEffectsForArchRuntimeCalls(),
+                     dex_pc),
+        special_input_(HUserRecord<HInstruction*>(current_method)),
+        proto_index_(proto_index),
+        dex_file_(dex_file) {
+  }
+
+  using HInstruction::GetInputRecords;  // Keep the const version visible.
+  ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
+    return ArrayRef<HUserRecord<HInstruction*>>(
+        &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u);
+  }
+
+  bool IsClonable() const OVERRIDE { return true; }
+
+  dex::ProtoIndex GetProtoIndex() const { return proto_index_; }
+
+  const DexFile& GetDexFile() const { return dex_file_; }
+
+  static SideEffects SideEffectsForArchRuntimeCalls() {
+    return SideEffects::CanTriggerGC();
+  }
+
+  DECLARE_INSTRUCTION(LoadMethodType);
+
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(LoadMethodType);
+
+ private:
+  // The special input is the HCurrentMethod for kRuntimeCall.
+  HUserRecord<HInstruction*> special_input_;
+
+  const dex::ProtoIndex proto_index_;
+  const DexFile& dex_file_;
+};
+
 /**
  * Performs an initialization check on its Class object input.
  */
@@ -6633,7 +6688,7 @@
   const FieldInfo field_info_;
 };
 
-class HStaticFieldSet FINAL : public HTemplateInstruction<2> {
+class HStaticFieldSet FINAL : public HExpression<2> {
  public:
   HStaticFieldSet(HInstruction* cls,
                   HInstruction* value,
@@ -6645,9 +6700,9 @@
                   uint16_t declaring_class_def_index,
                   const DexFile& dex_file,
                   uint32_t dex_pc)
-      : HTemplateInstruction(kStaticFieldSet,
-                             SideEffects::FieldWriteOfType(field_type, is_volatile),
-                             dex_pc),
+      : HExpression(kStaticFieldSet,
+                    SideEffects::FieldWriteOfType(field_type, is_volatile),
+                    dex_pc),
         field_info_(field,
                     field_offset,
                     field_type,
@@ -6714,16 +6769,14 @@
   const uint32_t field_index_;
 };
 
-class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> {
+class HUnresolvedInstanceFieldSet FINAL : public HExpression<2> {
  public:
   HUnresolvedInstanceFieldSet(HInstruction* obj,
                               HInstruction* value,
                               DataType::Type field_type,
                               uint32_t field_index,
                               uint32_t dex_pc)
-      : HTemplateInstruction(kUnresolvedInstanceFieldSet,
-                             SideEffects::AllExceptGCDependency(),
-                             dex_pc),
+      : HExpression(kUnresolvedInstanceFieldSet, SideEffects::AllExceptGCDependency(), dex_pc),
         field_index_(field_index) {
     SetPackedField<FieldTypeField>(field_type);
     DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType()));
@@ -6784,15 +6837,13 @@
   const uint32_t field_index_;
 };
 
-class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> {
+class HUnresolvedStaticFieldSet FINAL : public HExpression<1> {
  public:
   HUnresolvedStaticFieldSet(HInstruction* value,
                             DataType::Type field_type,
                             uint32_t field_index,
                             uint32_t dex_pc)
-      : HTemplateInstruction(kUnresolvedStaticFieldSet,
-                             SideEffects::AllExceptGCDependency(),
-                             dex_pc),
+      : HExpression(kUnresolvedStaticFieldSet, SideEffects::AllExceptGCDependency(), dex_pc),
         field_index_(field_index) {
     SetPackedField<FieldTypeField>(field_type);
     DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType()));
@@ -6841,10 +6892,10 @@
 
 // Implicit part of move-exception which clears thread-local exception storage.
 // Must not be removed because the runtime expects the TLS to get cleared.
-class HClearException FINAL : public HTemplateInstruction<0> {
+class HClearException FINAL : public HExpression<0> {
  public:
   explicit HClearException(uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(kClearException, SideEffects::AllWrites(), dex_pc) {
+      : HExpression(kClearException, SideEffects::AllWrites(), dex_pc) {
   }
 
   DECLARE_INSTRUCTION(ClearException);
@@ -6853,10 +6904,10 @@
   DEFAULT_COPY_CONSTRUCTOR(ClearException);
 };
 
-class HThrow FINAL : public HTemplateInstruction<1> {
+class HThrow FINAL : public HExpression<1> {
  public:
   HThrow(HInstruction* exception, uint32_t dex_pc)
-      : HTemplateInstruction(kThrow, SideEffects::CanTriggerGC(), dex_pc) {
+      : HExpression(kThrow, SideEffects::CanTriggerGC(), dex_pc) {
     SetRawInputAt(0, exception);
   }
 
@@ -6897,6 +6948,7 @@
 class HTypeCheckInstruction : public HVariableInputSizeInstruction {
  public:
   HTypeCheckInstruction(InstructionKind kind,
+                        DataType::Type type,
                         HInstruction* object,
                         HInstruction* target_class_or_null,
                         TypeCheckKind check_kind,
@@ -6908,6 +6960,7 @@
                         SideEffects side_effects)
       : HVariableInputSizeInstruction(
           kind,
+          type,
           side_effects,
           dex_pc,
           allocator,
@@ -7010,6 +7063,7 @@
               HIntConstant* bitstring_path_to_root,
               HIntConstant* bitstring_mask)
       : HTypeCheckInstruction(kInstanceOf,
+                              DataType::Type::kBool,
                               object,
                               target_class_or_null,
                               check_kind,
@@ -7020,8 +7074,6 @@
                               bitstring_mask,
                               SideEffectsForArchRuntimeCalls(check_kind)) {}
 
-  DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; }
-
   bool NeedsEnvironment() const OVERRIDE {
     return CanCallRuntime(GetTypeCheckKind());
   }
@@ -7074,7 +7126,7 @@
  private:
   // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this
   // is false then CanBeNull() cannot be true).
-  static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFlagUpperCanBeNull = kNumberOfGenericPackedBits;
   static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1;
   static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1;
   static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
@@ -7099,6 +7151,7 @@
              HIntConstant* bitstring_path_to_root,
              HIntConstant* bitstring_mask)
       : HTypeCheckInstruction(kCheckCast,
+                              DataType::Type::kVoid,
                               object,
                               target_class_or_null,
                               check_kind,
@@ -7148,13 +7201,12 @@
 };
 std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind);
 
-class HMemoryBarrier FINAL : public HTemplateInstruction<0> {
+class HMemoryBarrier FINAL : public HExpression<0> {
  public:
   explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(
-            kMemoryBarrier,
-            SideEffects::AllWritesAndReads(),  // Assume write/read on all fields/arrays.
-            dex_pc) {
+      : HExpression(kMemoryBarrier,
+                    SideEffects::AllWritesAndReads(),  // Assume write/read on all fields/arrays.
+                    dex_pc) {
     SetPackedField<BarrierKindField>(barrier_kind);
   }
 
@@ -7331,7 +7383,7 @@
   DEFAULT_COPY_CONSTRUCTOR(ConstructorFence);
 };
 
-class HMonitorOperation FINAL : public HTemplateInstruction<1> {
+class HMonitorOperation FINAL : public HExpression<1> {
  public:
   enum class OperationKind {
     kEnter,
@@ -7340,10 +7392,9 @@
   };
 
   HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
-    : HTemplateInstruction(
-          kMonitorOperation,
-          SideEffects::AllExceptGCDependency(),  // Assume write/read on all fields/arrays.
-          dex_pc) {
+    : HExpression(kMonitorOperation,
+                  SideEffects::AllExceptGCDependency(),  // Assume write/read on all fields/arrays.
+                  dex_pc) {
     SetPackedField<OperationKindField>(kind);
     SetRawInputAt(0, object);
   }
@@ -7493,10 +7544,10 @@
 
 static constexpr size_t kDefaultNumberOfMoves = 4;
 
-class HParallelMove FINAL : public HTemplateInstruction<0> {
+class HParallelMove FINAL : public HExpression<0> {
  public:
   explicit HParallelMove(ArenaAllocator* allocator, uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(kParallelMove, SideEffects::None(), dex_pc),
+      : HExpression(kParallelMove, SideEffects::None(), dex_pc),
         moves_(allocator->Adapter(kArenaAllocMoveOperands)) {
     moves_.reserve(kDefaultNumberOfMoves);
   }
@@ -7788,8 +7839,30 @@
   return instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern();
 }
 
+// Implement HInstruction::Is##type() for concrete instructions.
 #define INSTRUCTION_TYPE_CHECK(type, super)                                    \
-  inline bool HInstruction::Is##type() const { return GetKind() == k##type; }  \
+  inline bool HInstruction::Is##type() const { return GetKind() == k##type; }
+  FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
+#undef INSTRUCTION_TYPE_CHECK
+
+// Implement HInstruction::Is##type() for abstract instructions.
+#define INSTRUCTION_TYPE_CHECK_RESULT(type, super)                             \
+  std::is_base_of<BaseType, H##type>::value,
+#define INSTRUCTION_TYPE_CHECK(type, super)                                    \
+  inline bool HInstruction::Is##type() const {                                 \
+    DCHECK_LT(GetKind(), kLastInstructionKind);                                \
+    using BaseType = H##type;                                                  \
+    static constexpr bool results[] = {                                        \
+        FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK_RESULT)           \
+    };                                                                         \
+    return results[static_cast<size_t>(GetKind())];                            \
+  }
+
+  FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
+#undef INSTRUCTION_TYPE_CHECK
+#undef INSTRUCTION_TYPE_CHECK_RESULT
+
+#define INSTRUCTION_TYPE_CAST(type, super)                                     \
   inline const H##type* HInstruction::As##type() const {                       \
     return Is##type() ? down_cast<const H##type*>(this) : nullptr;             \
   }                                                                            \
@@ -7797,8 +7870,9 @@
     return Is##type() ? static_cast<H##type*>(this) : nullptr;                 \
   }
 
-  FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
-#undef INSTRUCTION_TYPE_CHECK
+  FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CAST)
+#undef INSTRUCTION_TYPE_CAST
+
 
 // Create space in `blocks` for adding `number_of_new_blocks` entries
 // starting at location `at`. Blocks after `at` are moved accordingly.
diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h
index d0e0fef..05b27a7 100644
--- a/compiler/optimizing/nodes_mips.h
+++ b/compiler/optimizing/nodes_mips.h
@@ -39,14 +39,14 @@
 };
 
 // Mips version of HPackedSwitch that holds a pointer to the base method address.
-class HMipsPackedSwitch FINAL : public HTemplateInstruction<2> {
+class HMipsPackedSwitch FINAL : public HExpression<2> {
  public:
   HMipsPackedSwitch(int32_t start_value,
                     int32_t num_entries,
                     HInstruction* input,
                     HMipsComputeBaseMethodAddress* method_base,
                     uint32_t dex_pc)
-    : HTemplateInstruction(kMipsPackedSwitch, SideEffects::None(), dex_pc),
+    : HExpression(kMipsPackedSwitch, SideEffects::None(), dex_pc),
       start_value_(start_value),
       num_entries_(num_entries) {
     SetRawInputAt(0, input);
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 1a484e1..c5e9a8d 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -79,13 +79,14 @@
                 size_t vector_length,
                 uint32_t dex_pc)
       : HVariableInputSizeInstruction(kind,
+                                      kSIMDType,
                                       side_effects,
                                       dex_pc,
                                       allocator,
                                       number_of_inputs,
                                       kArenaAllocVectorNode),
         vector_length_(vector_length) {
-    SetPackedField<TypeField>(packed_type);
+    SetPackedField<PackedTypeField>(packed_type);
     DCHECK_LT(1u, vector_length);
   }
 
@@ -99,14 +100,9 @@
     return vector_length_ * DataType::Size(GetPackedType());
   }
 
-  // Returns the type of the vector operation.
-  DataType::Type GetType() const OVERRIDE {
-    return kSIMDType;
-  }
-
   // Returns the true component type packed in a vector.
   DataType::Type GetPackedType() const {
-    return GetPackedField<TypeField>();
+    return GetPackedField<PackedTypeField>();
   }
 
   // Assumes vector nodes cannot be moved by default. Each concrete implementation
@@ -185,12 +181,12 @@
 
  protected:
   // Additional packed bits.
-  static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
-  static constexpr size_t kFieldTypeSize =
+  static constexpr size_t kFieldPackedType = HInstruction::kNumberOfGenericPackedBits;
+  static constexpr size_t kFieldPackedTypeSize =
       MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast));
-  static constexpr size_t kNumberOfVectorOpPackedBits = kFieldType + kFieldTypeSize;
+  static constexpr size_t kNumberOfVectorOpPackedBits = kFieldPackedType + kFieldPackedTypeSize;
   static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-  using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
+  using PackedTypeField = BitField<DataType::Type, kFieldPackedType, kFieldPackedTypeSize>;
 
   DEFAULT_COPY_CONSTRUCTOR(VecOperation);
 
@@ -358,11 +354,9 @@
     DCHECK(HasConsistentPackedTypes(input, packed_type));
     DCHECK_LT(index, vector_length);
     DCHECK_EQ(index, 0u);
-  }
-
-  // Yields a single component in the vector.
-  DataType::Type GetType() const OVERRIDE {
-    return GetPackedType();
+    // Yields a single component in the vector.
+    // Overrides the kSIMDType set by the VecOperation constructor.
+    SetPackedField<TypeField>(packed_type);
   }
 
   // An extract needs to stay in place, since SIMD registers are not
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index 4c32be7..d1e7f68 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -89,14 +89,14 @@
 };
 
 // X86 version of HPackedSwitch that holds a pointer to the base method address.
-class HX86PackedSwitch FINAL : public HTemplateInstruction<2> {
+class HX86PackedSwitch FINAL : public HExpression<2> {
  public:
   HX86PackedSwitch(int32_t start_value,
                    int32_t num_entries,
                    HInstruction* input,
                    HX86ComputeBaseMethodAddress* method_base,
                    uint32_t dex_pc)
-    : HTemplateInstruction(kX86PackedSwitch, SideEffects::None(), dex_pc),
+    : HExpression(kX86PackedSwitch, SideEffects::None(), dex_pc),
       start_value_(start_value),
       num_entries_(num_entries) {
     SetRawInputAt(0, input);
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 57db7a6..d37c43d 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -121,12 +121,15 @@
     case OptimizationPass::kX86MemoryOperandGeneration:
       return x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName;
 #endif
+    case OptimizationPass::kNone:
+      LOG(FATAL) << "kNone does not represent an actual pass";
+      UNREACHABLE();
   }
 }
 
-#define X(x) if (name == OptimizationPassName((x))) return (x)
+#define X(x) if (pass_name == OptimizationPassName((x))) return (x)
 
-OptimizationPass OptimizationPassByName(const std::string& name) {
+OptimizationPass OptimizationPassByName(const std::string& pass_name) {
   X(OptimizationPass::kBoundsCheckElimination);
   X(OptimizationPass::kCHAGuardOptimization);
   X(OptimizationPass::kCodeSinking);
@@ -160,7 +163,7 @@
   X(OptimizationPass::kPcRelativeFixupsX86);
   X(OptimizationPass::kX86MemoryOperandGeneration);
 #endif
-  LOG(FATAL) << "Cannot find optimization " << name;
+  LOG(FATAL) << "Cannot find optimization " << pass_name;
   UNREACHABLE();
 }
 
@@ -187,9 +190,9 @@
 
   // Loop over the requested optimizations.
   for (size_t i = 0; i < length; i++) {
-    OptimizationPass pass = definitions[i].first;
-    const char* alt_name = definitions[i].second;
-    const char* name = alt_name != nullptr
+    OptimizationPass pass = definitions[i].pass;
+    const char* alt_name = definitions[i].pass_name;
+    const char* pass_name = alt_name != nullptr
         ? alt_name
         : OptimizationPassName(pass);
     HOptimization* opt = nullptr;
@@ -199,47 +202,48 @@
       // Analysis passes (kept in most recent for subsequent passes).
       //
       case OptimizationPass::kSideEffectsAnalysis:
-        opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, name);
+        opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, pass_name);
         break;
       case OptimizationPass::kInductionVarAnalysis:
-        opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, name);
+        opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, pass_name);
         break;
       case OptimizationPass::kLoadStoreAnalysis:
-        opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, name);
+        opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, pass_name);
         break;
       //
       // Passes that need prior analysis.
       //
       case OptimizationPass::kGlobalValueNumbering:
         CHECK(most_recent_side_effects != nullptr);
-        opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, name);
+        opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, pass_name);
         break;
       case OptimizationPass::kInvariantCodeMotion:
         CHECK(most_recent_side_effects != nullptr);
-        opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, name);
+        opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, pass_name);
         break;
       case OptimizationPass::kLoopOptimization:
         CHECK(most_recent_induction != nullptr);
-        opt = new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats, name);
+        opt = new (allocator) HLoopOptimization(
+            graph, driver, most_recent_induction, stats, pass_name);
         break;
       case OptimizationPass::kBoundsCheckElimination:
         CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr);
         opt = new (allocator) BoundsCheckElimination(
-            graph, *most_recent_side_effects, most_recent_induction, name);
+            graph, *most_recent_side_effects, most_recent_induction, pass_name);
         break;
       case OptimizationPass::kLoadStoreElimination:
         CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr);
         opt = new (allocator) LoadStoreElimination(
-            graph, *most_recent_side_effects, *most_recent_lsa, stats, name);
+            graph, *most_recent_side_effects, *most_recent_lsa, stats, pass_name);
         break;
       //
       // Regular passes.
       //
       case OptimizationPass::kConstantFolding:
-        opt = new (allocator) HConstantFolding(graph, name);
+        opt = new (allocator) HConstantFolding(graph, pass_name);
         break;
       case OptimizationPass::kDeadCodeElimination:
-        opt = new (allocator) HDeadCodeElimination(graph, stats, name);
+        opt = new (allocator) HDeadCodeElimination(graph, stats, pass_name);
         break;
       case OptimizationPass::kInliner: {
         CodeItemDataAccessor accessor(*dex_compilation_unit.GetDexFile(),
@@ -256,33 +260,33 @@
                                        /* total_number_of_instructions */ 0,
                                        /* parent */ nullptr,
                                        /* depth */ 0,
-                                       name);
+                                       pass_name);
         break;
       }
       case OptimizationPass::kSharpening:
-        opt = new (allocator) HSharpening(graph, codegen, driver, name);
+        opt = new (allocator) HSharpening(graph, codegen, driver, pass_name);
         break;
       case OptimizationPass::kSelectGenerator:
-        opt = new (allocator) HSelectGenerator(graph, handles, stats, name);
+        opt = new (allocator) HSelectGenerator(graph, handles, stats, pass_name);
         break;
       case OptimizationPass::kInstructionSimplifier:
-        opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, name);
+        opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, pass_name);
         break;
       case OptimizationPass::kIntrinsicsRecognizer:
-        opt = new (allocator) IntrinsicsRecognizer(graph, stats, name);
+        opt = new (allocator) IntrinsicsRecognizer(graph, stats, pass_name);
         break;
       case OptimizationPass::kCHAGuardOptimization:
-        opt = new (allocator) CHAGuardOptimization(graph, name);
+        opt = new (allocator) CHAGuardOptimization(graph, pass_name);
         break;
       case OptimizationPass::kCodeSinking:
-        opt = new (allocator) CodeSinking(graph, stats, name);
+        opt = new (allocator) CodeSinking(graph, stats, pass_name);
         break;
       case OptimizationPass::kConstructorFenceRedundancyElimination:
-        opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, name);
+        opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, pass_name);
         break;
       case OptimizationPass::kScheduling:
         opt = new (allocator) HInstructionScheduling(
-            graph, driver->GetInstructionSet(), codegen, name);
+            graph, driver->GetInstructionSet(), codegen, pass_name);
         break;
       //
       // Arch-specific passes.
@@ -319,11 +323,14 @@
         opt = new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats);
         break;
 #endif
+      case OptimizationPass::kNone:
+        LOG(FATAL) << "kNone does not represent an actual pass";
+        UNREACHABLE();
     }  // switch
 
     // Add each next optimization to result vector.
     CHECK(opt != nullptr);
-    DCHECK_STREQ(name, opt->GetPassName());  // sanity
+    DCHECK_STREQ(pass_name, opt->GetPassName());  // sanity
     optimizations.push_back(opt);
   }
 
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index c170f15..88b283c 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -47,8 +47,9 @@
   // 'instruction_simplifier$before_codegen'.
   const char* GetPassName() const { return pass_name_; }
 
-  // Perform the analysis itself.
-  virtual void Run() = 0;
+  // Perform the pass or analysis. Returns false if no optimizations occurred or no useful
+  // information was computed (this is best effort, returning true is always ok).
+  virtual bool Run() = 0;
 
  protected:
   HGraph* const graph_;
@@ -101,21 +102,32 @@
 #if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
   kX86MemoryOperandGeneration,
 #endif
+  kNone,
+  kLast = kNone
 };
 
 // Lookup name of optimization pass.
 const char* OptimizationPassName(OptimizationPass pass);
 
 // Lookup optimization pass by name.
-OptimizationPass OptimizationPassByName(const std::string& name);
+OptimizationPass OptimizationPassByName(const std::string& pass_name);
 
 // Optimization definition consisting of an optimization pass
-// and an optional alternative name (nullptr denotes default).
-typedef std::pair<OptimizationPass, const char*> OptimizationDef;
+// an optional alternative name (nullptr denotes default), and
+// an optional pass dependence (kNone denotes no dependence).
+struct OptimizationDef {
+  OptimizationDef(OptimizationPass p, const char* pn, OptimizationPass d)
+      : pass(p), pass_name(pn), depends_on(d) {}
+  OptimizationPass pass;
+  const char* pass_name;
+  OptimizationPass depends_on;
+};
 
 // Helper method for optimization definition array entries.
-inline OptimizationDef OptDef(OptimizationPass pass, const char* name = nullptr) {
-  return std::make_pair(pass, name);
+inline OptimizationDef OptDef(OptimizationPass pass,
+                              const char* pass_name = nullptr,
+                              OptimizationPass depends_on = OptimizationPass::kNone) {
+  return OptimizationDef(pass, pass_name, depends_on);
 }
 
 // Helper method to construct series of optimization passes.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index cadefc3..c4977de 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -107,8 +107,9 @@
                CompilerDriver* compiler_driver,
                Mutex& dump_mutex)
       : graph_(graph),
+        last_seen_graph_size_(0),
         cached_method_name_(),
-        timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpTimings()),
+        timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpPassTimings()),
         timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true),
         disasm_info_(graph->GetAllocator()),
         visualizer_oss_(),
@@ -174,7 +175,7 @@
     visualizer_oss_.clear();
   }
 
-  void EndPass(const char* pass_name) REQUIRES(!visualizer_dump_mutex_) {
+  void EndPass(const char* pass_name, bool pass_change) REQUIRES(!visualizer_dump_mutex_) {
     // Pause timer first, then dump graph.
     if (timing_logger_enabled_) {
       timing_logger_.EndTiming();
@@ -188,7 +189,7 @@
     if (kIsDebugBuild) {
       if (!graph_in_bad_state_) {
         GraphChecker checker(graph_);
-        checker.Run();
+        last_seen_graph_size_ = checker.Run(pass_change, last_seen_graph_size_);
         if (!checker.IsValid()) {
           LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<GraphChecker>(checker);
         }
@@ -214,6 +215,7 @@
   }
 
   HGraph* const graph_;
+  size_t last_seen_graph_size_;
 
   std::string cached_method_name_;
 
@@ -241,16 +243,22 @@
  public:
   PassScope(const char *pass_name, PassObserver* pass_observer)
       : pass_name_(pass_name),
+        pass_change_(true),  // assume change
         pass_observer_(pass_observer) {
     pass_observer_->StartPass(pass_name_);
   }
 
+  void SetPassNotChanged() {
+    pass_change_ = false;
+  }
+
   ~PassScope() {
-    pass_observer_->EndPass(pass_name_);
+    pass_observer_->EndPass(pass_name_, pass_change_);
   }
 
  private:
   const char* const pass_name_;
+  bool pass_change_;
   PassObserver* const pass_observer_;
 };
 
@@ -294,7 +302,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
-  void RunOptimizations(HGraph* graph,
+  bool RunOptimizations(HGraph* graph,
                         CodeGenerator* codegen,
                         const DexCompilationUnit& dex_compilation_unit,
                         PassObserver* pass_observer,
@@ -313,21 +321,38 @@
         dex_compilation_unit,
         handles);
     DCHECK_EQ(length, optimizations.size());
-    // Run the optimization passes one by one.
+    // Run the optimization passes one by one. Any "depends_on" pass refers back to
+    // the most recent occurrence of that pass, skipped or executed.
+    std::bitset<static_cast<size_t>(OptimizationPass::kLast) + 1u> pass_changes;
+    pass_changes[static_cast<size_t>(OptimizationPass::kNone)] = true;
+    bool change = false;
     for (size_t i = 0; i < length; ++i) {
-      PassScope scope(optimizations[i]->GetPassName(), pass_observer);
-      optimizations[i]->Run();
+      if (pass_changes[static_cast<size_t>(definitions[i].depends_on)]) {
+        // Execute the pass and record whether it changed anything.
+        PassScope scope(optimizations[i]->GetPassName(), pass_observer);
+        bool pass_change = optimizations[i]->Run();
+        pass_changes[static_cast<size_t>(definitions[i].pass)] = pass_change;
+        if (pass_change) {
+          change = true;
+        } else {
+          scope.SetPassNotChanged();
+        }
+      } else {
+        // Skip the pass and record that nothing changed.
+        pass_changes[static_cast<size_t>(definitions[i].pass)] = false;
+      }
     }
+    return change;
   }
 
-  template <size_t length> void RunOptimizations(
+  template <size_t length> bool RunOptimizations(
       HGraph* graph,
       CodeGenerator* codegen,
       const DexCompilationUnit& dex_compilation_unit,
       PassObserver* pass_observer,
       VariableSizedHandleScope* handles,
       const OptimizationDef (&definitions)[length]) const {
-    RunOptimizations(
+    return RunOptimizations(
         graph, codegen, dex_compilation_unit, pass_observer, handles, definitions, length);
   }
 
@@ -366,13 +391,7 @@
                                      ArtMethod* method,
                                      VariableSizedHandleScope* handles) const;
 
-  void MaybeRunInliner(HGraph* graph,
-                       CodeGenerator* codegen,
-                       const DexCompilationUnit& dex_compilation_unit,
-                       PassObserver* pass_observer,
-                       VariableSizedHandleScope* handles) const;
-
-  void RunArchOptimizations(HGraph* graph,
+  bool RunArchOptimizations(HGraph* graph,
                             CodeGenerator* codegen,
                             const DexCompilationUnit& dex_compilation_unit,
                             PassObserver* pass_observer,
@@ -435,28 +454,7 @@
       || instruction_set == InstructionSet::kX86_64;
 }
 
-void OptimizingCompiler::MaybeRunInliner(HGraph* graph,
-                                         CodeGenerator* codegen,
-                                         const DexCompilationUnit& dex_compilation_unit,
-                                         PassObserver* pass_observer,
-                                         VariableSizedHandleScope* handles) const {
-  const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions();
-  bool should_inline = (compiler_options.GetInlineMaxCodeUnits() > 0);
-  if (!should_inline) {
-    return;
-  }
-  OptimizationDef optimizations[] = {
-    OptDef(OptimizationPass::kInliner)
-  };
-  RunOptimizations(graph,
-                   codegen,
-                   dex_compilation_unit,
-                   pass_observer,
-                   handles,
-                   optimizations);
-}
-
-void OptimizingCompiler::RunArchOptimizations(HGraph* graph,
+bool OptimizingCompiler::RunArchOptimizations(HGraph* graph,
                                               CodeGenerator* codegen,
                                               const DexCompilationUnit& dex_compilation_unit,
                                               PassObserver* pass_observer,
@@ -471,13 +469,12 @@
         OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"),
         OptDef(OptimizationPass::kScheduling)
       };
-      RunOptimizations(graph,
-                       codegen,
-                       dex_compilation_unit,
-                       pass_observer,
-                       handles,
-                       arm_optimizations);
-      break;
+      return RunOptimizations(graph,
+                              codegen,
+                              dex_compilation_unit,
+                              pass_observer,
+                              handles,
+                              arm_optimizations);
     }
 #endif
 #ifdef ART_ENABLE_CODEGEN_arm64
@@ -488,13 +485,12 @@
         OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"),
         OptDef(OptimizationPass::kScheduling)
       };
-      RunOptimizations(graph,
-                       codegen,
-                       dex_compilation_unit,
-                       pass_observer,
-                       handles,
-                       arm64_optimizations);
-      break;
+      return RunOptimizations(graph,
+                              codegen,
+                              dex_compilation_unit,
+                              pass_observer,
+                              handles,
+                              arm64_optimizations);
     }
 #endif
 #ifdef ART_ENABLE_CODEGEN_mips
@@ -505,13 +501,12 @@
         OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"),
         OptDef(OptimizationPass::kPcRelativeFixupsMips)
       };
-      RunOptimizations(graph,
-                       codegen,
-                       dex_compilation_unit,
-                       pass_observer,
-                       handles,
-                       mips_optimizations);
-      break;
+      return RunOptimizations(graph,
+                              codegen,
+                              dex_compilation_unit,
+                              pass_observer,
+                              handles,
+                              mips_optimizations);
     }
 #endif
 #ifdef ART_ENABLE_CODEGEN_mips64
@@ -520,13 +515,12 @@
         OptDef(OptimizationPass::kSideEffectsAnalysis),
         OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch")
       };
-      RunOptimizations(graph,
-                       codegen,
-                       dex_compilation_unit,
-                       pass_observer,
-                       handles,
-                       mips64_optimizations);
-      break;
+      return RunOptimizations(graph,
+                              codegen,
+                              dex_compilation_unit,
+                              pass_observer,
+                              handles,
+                              mips64_optimizations);
     }
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86
@@ -537,13 +531,12 @@
         OptDef(OptimizationPass::kPcRelativeFixupsX86),
         OptDef(OptimizationPass::kX86MemoryOperandGeneration)
       };
-      RunOptimizations(graph,
-                       codegen,
-                       dex_compilation_unit,
-                       pass_observer,
-                       handles,
-                       x86_optimizations);
-      break;
+      return RunOptimizations(graph,
+                              codegen,
+                              dex_compilation_unit,
+                              pass_observer,
+                              handles,
+                              x86_optimizations);
     }
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86_64
@@ -553,17 +546,16 @@
         OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"),
         OptDef(OptimizationPass::kX86MemoryOperandGeneration)
       };
-      RunOptimizations(graph,
-                       codegen,
-                       dex_compilation_unit,
-                       pass_observer,
-                       handles,
-                       x86_64_optimizations);
-      break;
+      return RunOptimizations(graph,
+                              codegen,
+                              dex_compilation_unit,
+                              pass_observer,
+                              handles,
+                              x86_64_optimizations);
     }
 #endif
     default:
-      break;
+      return false;
   }
 }
 
@@ -610,6 +602,7 @@
   if (pass_names != nullptr) {
     // If passes were defined on command-line, build the optimization
     // passes and run these instead of the built-in optimizations.
+    // TODO: a way to define depends_on via command-line?
     const size_t length = pass_names->size();
     std::vector<OptimizationDef> optimizations;
     for (const std::string& pass_name : *pass_names) {
@@ -626,47 +619,64 @@
     return;
   }
 
-  OptimizationDef optimizations1[] = {
+  OptimizationDef optimizations[] = {
+    // Initial optimizations.
     OptDef(OptimizationPass::kIntrinsicsRecognizer),
     OptDef(OptimizationPass::kSharpening),
     OptDef(OptimizationPass::kConstantFolding),
     OptDef(OptimizationPass::kInstructionSimplifier),
-    OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial")
-  };
-  RunOptimizations(graph,
-                   codegen,
-                   dex_compilation_unit,
-                   pass_observer,
-                   handles,
-                   optimizations1);
-
-  MaybeRunInliner(graph, codegen, dex_compilation_unit, pass_observer, handles);
-
-  OptimizationDef optimizations2[] = {
-    OptDef(OptimizationPass::kSideEffectsAnalysis,   "side_effects$before_gvn"),
+    OptDef(OptimizationPass::kDeadCodeElimination,
+           "dead_code_elimination$initial"),
+    // Inlining.
+    OptDef(OptimizationPass::kInliner),
+    // Simplification (only if inlining occurred).
+    OptDef(OptimizationPass::kConstantFolding,
+           "constant_folding$after_inlining",
+           OptimizationPass::kInliner),
+    OptDef(OptimizationPass::kInstructionSimplifier,
+           "instruction_simplifier$after_inlining",
+           OptimizationPass::kInliner),
+    OptDef(OptimizationPass::kDeadCodeElimination,
+           "dead_code_elimination$after_inlining",
+           OptimizationPass::kInliner),
+    // GVN.
+    OptDef(OptimizationPass::kSideEffectsAnalysis,
+           "side_effects$before_gvn"),
     OptDef(OptimizationPass::kGlobalValueNumbering),
+    // Simplification (TODO: only if GVN occurred).
     OptDef(OptimizationPass::kSelectGenerator),
-    OptDef(OptimizationPass::kConstantFolding,       "constant_folding$after_inlining"),
-    OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_inlining"),
-    OptDef(OptimizationPass::kDeadCodeElimination,   "dead_code_elimination$after_inlining"),
-    OptDef(OptimizationPass::kSideEffectsAnalysis,   "side_effects$before_licm"),
+    OptDef(OptimizationPass::kConstantFolding,
+           "constant_folding$after_gvn"),
+    OptDef(OptimizationPass::kInstructionSimplifier,
+           "instruction_simplifier$after_gvn"),
+    OptDef(OptimizationPass::kDeadCodeElimination,
+           "dead_code_elimination$after_gvn"),
+    // High-level optimizations.
+    OptDef(OptimizationPass::kSideEffectsAnalysis,
+           "side_effects$before_licm"),
     OptDef(OptimizationPass::kInvariantCodeMotion),
     OptDef(OptimizationPass::kInductionVarAnalysis),
     OptDef(OptimizationPass::kBoundsCheckElimination),
     OptDef(OptimizationPass::kLoopOptimization),
-    // Evaluates code generated by dynamic bce.
-    OptDef(OptimizationPass::kConstantFolding,       "constant_folding$after_bce"),
-    OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_bce"),
-    OptDef(OptimizationPass::kSideEffectsAnalysis,   "side_effects$before_lse"),
+    // Simplification.
+    OptDef(OptimizationPass::kConstantFolding,
+           "constant_folding$after_bce"),
+    OptDef(OptimizationPass::kInstructionSimplifier,
+           "instruction_simplifier$after_bce"),
+    // Other high-level optimizations.
+    OptDef(OptimizationPass::kSideEffectsAnalysis,
+           "side_effects$before_lse"),
     OptDef(OptimizationPass::kLoadStoreAnalysis),
     OptDef(OptimizationPass::kLoadStoreElimination),
     OptDef(OptimizationPass::kCHAGuardOptimization),
-    OptDef(OptimizationPass::kDeadCodeElimination,   "dead_code_elimination$final"),
+    OptDef(OptimizationPass::kDeadCodeElimination,
+           "dead_code_elimination$final"),
     OptDef(OptimizationPass::kCodeSinking),
     // The codegen has a few assumptions that only the instruction simplifier
     // can satisfy. For example, the code generator does not expect to see a
     // HTypeConversion from a type to the same type.
-    OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$before_codegen"),
+    OptDef(OptimizationPass::kInstructionSimplifier,
+           "instruction_simplifier$before_codegen"),
     // Eliminate constructor fences after code sinking to avoid
     // complicated sinking logic to split a fence with many inputs.
     OptDef(OptimizationPass::kConstructorFenceRedundancyElimination)
@@ -676,7 +686,7 @@
                    dex_compilation_unit,
                    pass_observer,
                    handles,
-                   optimizations2);
+                   optimizations);
 
   RunArchOptimizations(graph, codegen, dex_compilation_unit, pass_observer, handles);
 }
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index d8cea30..6ee9c70 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -17,8 +17,8 @@
 #ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
 #define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
 
+#include "base/globals.h"
 #include "base/mutex.h"
-#include "globals.h"
 
 namespace art {
 
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 9a26f2f..f246228 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -50,6 +50,7 @@
   kNotCompiledThrowCatchLoop,
   kNotCompiledAmbiguousArrayOp,
   kNotCompiledHugeMethod,
+  kNotCompiledIrreducibleAndStringInit,
   kNotCompiledLargeMethodNoBranches,
   kNotCompiledMalformedOpcode,
   kNotCompiledNoCodegen,
diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc
index 0102254..f18ecc1 100644
--- a/compiler/optimizing/pc_relative_fixups_mips.cc
+++ b/compiler/optimizing/pc_relative_fixups_mips.cc
@@ -128,20 +128,21 @@
   HMipsComputeBaseMethodAddress* base_;
 };
 
-void PcRelativeFixups::Run() {
+bool PcRelativeFixups::Run() {
   CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen_);
   if (mips_codegen->GetInstructionSetFeatures().IsR6()) {
     // Do nothing for R6 because it has PC-relative addressing.
-    return;
+    return false;
   }
   if (graph_->HasIrreducibleLoops()) {
     // Do not run this optimization, as irreducible loops do not work with an instruction
     // that can be live-in at the irreducible loop header.
-    return;
+    return false;
   }
   PCRelativeHandlerVisitor visitor(graph_, codegen_);
   visitor.VisitInsertionOrder();
   visitor.MoveBaseIfNeeded();
+  return true;
 }
 
 }  // namespace mips
diff --git a/compiler/optimizing/pc_relative_fixups_mips.h b/compiler/optimizing/pc_relative_fixups_mips.h
index ec2c711..6dd1ee0 100644
--- a/compiler/optimizing/pc_relative_fixups_mips.h
+++ b/compiler/optimizing/pc_relative_fixups_mips.h
@@ -34,7 +34,7 @@
 
   static constexpr const char* kPcRelativeFixupsMipsPassName = "pc_relative_fixups_mips";
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
  private:
   CodeGenerator* codegen_;
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index 647336b..9049457 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -256,10 +256,11 @@
   HX86ComputeBaseMethodAddress* base_;
 };
 
-void PcRelativeFixups::Run() {
+bool PcRelativeFixups::Run() {
   PCRelativeHandlerVisitor visitor(graph_, codegen_);
   visitor.VisitInsertionOrder();
   visitor.MoveBaseIfNeeded();
+  return true;
 }
 
 }  // namespace x86
diff --git a/compiler/optimizing/pc_relative_fixups_x86.h b/compiler/optimizing/pc_relative_fixups_x86.h
index 72fa71e..db56b7f 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.h
+++ b/compiler/optimizing/pc_relative_fixups_x86.h
@@ -34,7 +34,7 @@
 
   static constexpr const char* kPcRelativeFixupsX86PassName  = "pc_relative_fixups_x86";
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
  private:
   CodeGenerator* codegen_;
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 5973339..831bccc 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -17,7 +17,7 @@
 #include "prepare_for_register_allocation.h"
 
 #include "dex/dex_file_types.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "optimizing_compiler_stats.h"
 #include "well_known_classes.h"
 
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 4030883..ecfa790 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -59,6 +59,18 @@
   return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_class_handle_);
 }
 
+ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodHandleClassHandle() {
+  return GetRootHandle(handles_,
+                       ClassLinker::kJavaLangInvokeMethodHandleImpl,
+                       &method_handle_class_handle_);
+}
+
+ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodTypeClassHandle() {
+  return GetRootHandle(handles_,
+                       ClassLinker::kJavaLangInvokeMethodType,
+                       &method_type_class_handle_);
+}
+
 ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetStringClassHandle() {
   return GetRootHandle(handles_, ClassLinker::kJavaLangString, &string_class_handle_);
 }
@@ -89,6 +101,8 @@
   void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
   void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE;
   void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
+  void VisitLoadMethodHandle(HLoadMethodHandle* instr) OVERRIDE;
+  void VisitLoadMethodType(HLoadMethodType* instr) OVERRIDE;
   void VisitLoadString(HLoadString* instr) OVERRIDE;
   void VisitLoadException(HLoadException* instr) OVERRIDE;
   void VisitNewArray(HNewArray* instr) OVERRIDE;
@@ -348,7 +362,7 @@
   }
 }
 
-void ReferenceTypePropagation::Run() {
+bool ReferenceTypePropagation::Run() {
   RTPVisitor visitor(graph_, class_loader_, hint_dex_cache_, &handle_cache_, is_first_run_);
 
   // To properly propagate type info we need to visit in the dominator-based order.
@@ -360,6 +374,7 @@
 
   visitor.ProcessWorklist();
   ValidateTypes();
+  return true;
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitBasicBlock(HBasicBlock* block) {
@@ -667,6 +682,17 @@
   instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());
 }
 
+void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodHandle(HLoadMethodHandle* instr) {
+  instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
+      handle_cache_->GetMethodHandleClassHandle(),
+      /* is_exact */ true));
+}
+
+void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodType(HLoadMethodType* instr) {
+  instr->SetReferenceTypeInfo(
+      ReferenceTypeInfo::Create(handle_cache_->GetMethodTypeClassHandle(), /* is_exact */ true));
+}
+
 void ReferenceTypePropagation::RTPVisitor::VisitLoadString(HLoadString* instr) {
   instr->SetReferenceTypeInfo(
       ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true));
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index fd4dad2..d36d592 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -40,7 +40,7 @@
   // Visit a single instruction.
   void Visit(HInstruction* instruction);
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   // Returns true if klass is admissible to the propagation: non-null and resolved.
   // For an array type, we also check if the component type is admissible.
@@ -75,6 +75,8 @@
 
     ReferenceTypeInfo::TypeHandle GetObjectClassHandle();
     ReferenceTypeInfo::TypeHandle GetClassClassHandle();
+    ReferenceTypeInfo::TypeHandle GetMethodHandleClassHandle();
+    ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle();
     ReferenceTypeInfo::TypeHandle GetStringClassHandle();
     ReferenceTypeInfo::TypeHandle GetThrowableClassHandle();
 
@@ -83,6 +85,8 @@
 
     ReferenceTypeInfo::TypeHandle object_class_handle_;
     ReferenceTypeInfo::TypeHandle class_class_handle_;
+    ReferenceTypeInfo::TypeHandle method_handle_class_handle_;
+    ReferenceTypeInfo::TypeHandle method_type_class_handle_;
     ReferenceTypeInfo::TypeHandle string_class_handle_;
     ReferenceTypeInfo::TypeHandle throwable_class_handle_;
   };
diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc
index bca538f..588ea03 100644
--- a/compiler/optimizing/scheduler.cc
+++ b/compiler/optimizing/scheduler.cc
@@ -70,19 +70,19 @@
   return false;
 }
 
-size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const {
+size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* instruction) const {
   DCHECK(heap_location_collector_ != nullptr);
-  size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(array, index);
+  size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(instruction);
   // This array access should be analyzed and added to HeapLocationCollector before.
   DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound);
   return heap_loc;
 }
 
-bool SchedulingGraph::ArrayAccessMayAlias(const HInstruction* node,
-                                          const HInstruction* other) const {
+bool SchedulingGraph::ArrayAccessMayAlias(HInstruction* node,
+                                          HInstruction* other) const {
   DCHECK(heap_location_collector_ != nullptr);
-  size_t node_heap_loc = ArrayAccessHeapLocation(node->InputAt(0), node->InputAt(1));
-  size_t other_heap_loc = ArrayAccessHeapLocation(other->InputAt(0), other->InputAt(1));
+  size_t node_heap_loc = ArrayAccessHeapLocation(node);
+  size_t other_heap_loc = ArrayAccessHeapLocation(other);
 
   // For example: arr[0] and arr[0]
   if (node_heap_loc == other_heap_loc) {
@@ -194,8 +194,8 @@
   return true;
 }
 
-bool SchedulingGraph::HasMemoryDependency(const HInstruction* node,
-                                          const HInstruction* other) const {
+bool SchedulingGraph::HasMemoryDependency(HInstruction* node,
+                                          HInstruction* other) const {
   if (!MayHaveReorderingDependency(node->GetSideEffects(), other->GetSideEffects())) {
     return false;
   }
@@ -264,8 +264,8 @@
 
 // Check whether `node` depends on `other`, taking into account `SideEffect`
 // information and `CanThrow` information.
-bool SchedulingGraph::HasSideEffectDependency(const HInstruction* node,
-                                              const HInstruction* other) const {
+bool SchedulingGraph::HasSideEffectDependency(HInstruction* node,
+                                              HInstruction* other) const {
   if (HasMemoryDependency(node, other)) {
     return true;
   }
@@ -774,7 +774,7 @@
       instr->IsSuspendCheck();
 }
 
-void HInstructionScheduling::Run(bool only_optimize_loop_blocks,
+bool HInstructionScheduling::Run(bool only_optimize_loop_blocks,
                                  bool schedule_randomly) {
 #if defined(ART_ENABLE_CODEGEN_arm64) || defined(ART_ENABLE_CODEGEN_arm)
   // Phase-local allocator that allocates scheduler internal data structures like
@@ -814,6 +814,7 @@
     default:
       break;
   }
+  return true;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h
index dfa077f..8e98f19 100644
--- a/compiler/optimizing/scheduler.h
+++ b/compiler/optimizing/scheduler.h
@@ -310,12 +310,12 @@
   void AddOtherDependency(SchedulingNode* node, SchedulingNode* dependency) {
     AddDependency(node, dependency, /*is_data_dependency*/false);
   }
-  bool HasMemoryDependency(const HInstruction* node, const HInstruction* other) const;
+  bool HasMemoryDependency(HInstruction* node, HInstruction* other) const;
   bool HasExceptionDependency(const HInstruction* node, const HInstruction* other) const;
-  bool HasSideEffectDependency(const HInstruction* node, const HInstruction* other) const;
-  bool ArrayAccessMayAlias(const HInstruction* node, const HInstruction* other) const;
+  bool HasSideEffectDependency(HInstruction* node, HInstruction* other) const;
+  bool ArrayAccessMayAlias(HInstruction* node, HInstruction* other) const;
   bool FieldAccessMayAlias(const HInstruction* node, const HInstruction* other) const;
-  size_t ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const;
+  size_t ArrayAccessHeapLocation(HInstruction* instruction) const;
   size_t FieldAccessHeapLocation(HInstruction* obj, const FieldInfo* field) const;
 
   // Add dependencies nodes for the given `HInstruction`: inputs, environments, and side-effects.
@@ -508,10 +508,11 @@
         codegen_(cg),
         instruction_set_(instruction_set) {}
 
-  void Run() {
-    Run(/*only_optimize_loop_blocks*/ true, /*schedule_randomly*/ false);
+  bool Run() OVERRIDE {
+    return Run(/*only_optimize_loop_blocks*/ true, /*schedule_randomly*/ false);
   }
-  void Run(bool only_optimize_loop_blocks, bool schedule_randomly);
+
+  bool Run(bool only_optimize_loop_blocks, bool schedule_randomly);
 
   static constexpr const char* kInstructionSchedulingPassName = "scheduler";
 
diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h
index f71cb5b..4f394d5 100644
--- a/compiler/optimizing/scheduler_arm64.h
+++ b/compiler/optimizing/scheduler_arm64.h
@@ -68,12 +68,10 @@
   M(ArrayGet             , unused)                   \
   M(ArrayLength          , unused)                   \
   M(ArraySet             , unused)                   \
-  M(BinaryOperation      , unused)                   \
   M(BoundsCheck          , unused)                   \
   M(Div                  , unused)                   \
   M(InstanceFieldGet     , unused)                   \
   M(InstanceOf           , unused)                   \
-  M(Invoke               , unused)                   \
   M(LoadString           , unused)                   \
   M(Mul                  , unused)                   \
   M(NewArray             , unused)                   \
@@ -108,6 +106,10 @@
   M(VecLoad              , unused)                   \
   M(VecStore             , unused)
 
+#define FOR_EACH_SCHEDULED_ABSTRACT_INSTRUCTION(M)   \
+  M(BinaryOperation      , unused)                   \
+  M(Invoke               , unused)
+
 #define FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(M) \
   M(BitwiseNegatedRight, unused)                 \
   M(MultiplyAccumulate, unused)                  \
@@ -119,6 +121,7 @@
   void Visit##type(H##type* instruction) OVERRIDE;
 
   FOR_EACH_SCHEDULED_COMMON_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_SCHEDULED_ABSTRACT_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION)
 
diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc
index fb15fc8..d4cae72 100644
--- a/compiler/optimizing/scheduler_test.cc
+++ b/compiler/optimizing/scheduler_test.cc
@@ -296,38 +296,38 @@
     size_t loc2 = HeapLocationCollector::kHeapLocationNotFound;
 
     // Test side effect dependency: array[0] and array[1]
-    loc1 = heap_location_collector.GetArrayHeapLocation(arr, c0);
-    loc2 = heap_location_collector.GetArrayHeapLocation(arr, c1);
+    loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0);
+    loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_1);
     ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
     ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_1, arr_set_0));
 
     // Test side effect dependency based on LSA analysis: array[i] and array[j]
-    loc1 = heap_location_collector.GetArrayHeapLocation(arr, i);
-    loc2 = heap_location_collector.GetArrayHeapLocation(arr, j);
+    loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
+    loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_j);
     ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
     ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i));
 
     // Test side effect dependency based on LSA analysis: array[i] and array[i+0]
-    loc1 = heap_location_collector.GetArrayHeapLocation(arr, i);
-    loc2 = heap_location_collector.GetArrayHeapLocation(arr, add0);
+    loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
+    loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_add0);
     ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
     ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_add0, arr_set_i));
 
     // Test side effect dependency based on LSA analysis: array[i] and array[i-0]
-    loc1 = heap_location_collector.GetArrayHeapLocation(arr, i);
-    loc2 = heap_location_collector.GetArrayHeapLocation(arr, sub0);
+    loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
+    loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_sub0);
     ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
     ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub0, arr_set_i));
 
     // Test side effect dependency based on LSA analysis: array[i] and array[i+1]
-    loc1 = heap_location_collector.GetArrayHeapLocation(arr, i);
-    loc2 = heap_location_collector.GetArrayHeapLocation(arr, add1);
+    loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
+    loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_add1);
     ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
     ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_add1, arr_set_i));
 
     // Test side effect dependency based on LSA analysis: array[i+1] and array[i-1]
-    loc1 = heap_location_collector.GetArrayHeapLocation(arr, add1);
-    loc2 = heap_location_collector.GetArrayHeapLocation(arr, sub1);
+    loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_add1);
+    loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_sub1);
     ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
     ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub1, arr_set_add1));
 
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index f9acf5a..0d0f7cc 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -90,7 +90,8 @@
   return select_phi;
 }
 
-void HSelectGenerator::Run() {
+bool HSelectGenerator::Run() {
+  bool didSelect = false;
   // Select cache with local allocator.
   ScopedArenaAllocator allocator(graph_->GetArenaStack());
   ScopedArenaSafeMap<HInstruction*, HSelect*> cache(
@@ -211,7 +212,9 @@
     // entry block. Any following blocks would have had the join block
     // as a dominator, and `MergeWith` handles changing that to the
     // entry block.
+    didSelect = true;
   }
+  return didSelect;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h
index bda57fd..d24d226 100644
--- a/compiler/optimizing/select_generator.h
+++ b/compiler/optimizing/select_generator.h
@@ -68,7 +68,7 @@
                    OptimizingCompilerStats* stats,
                    const char* name = kSelectGeneratorPassName);
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kSelectGeneratorPassName = "select_generator";
 
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 70b4576..6541043 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -36,7 +36,7 @@
 
 namespace art {
 
-void HSharpening::Run() {
+bool HSharpening::Run() {
   // We don't care about the order of the blocks here.
   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
@@ -51,6 +51,7 @@
       //       because we know the type better when inlining.
     }
   }
+  return true;
 }
 
 static bool IsInBootImage(ArtMethod* method) {
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index fa3e948..9ccbcaf 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -40,7 +40,7 @@
         codegen_(codegen),
         compiler_driver_(compiler_driver) { }
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kSharpeningPassName = "sharpening";
 
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
index 6d82e8e..ba97b43 100644
--- a/compiler/optimizing/side_effects_analysis.cc
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -18,7 +18,7 @@
 
 namespace art {
 
-void SideEffectsAnalysis::Run() {
+bool SideEffectsAnalysis::Run() {
   // Inlining might have created more blocks, so we need to increase the size
   // if needed.
   block_effects_.resize(graph_->GetBlocks().size());
@@ -69,6 +69,7 @@
     }
   }
   has_run_ = true;
+  return true;
 }
 
 SideEffects SideEffectsAnalysis::GetLoopEffects(HBasicBlock* block) const {
diff --git a/compiler/optimizing/side_effects_analysis.h b/compiler/optimizing/side_effects_analysis.h
index c0f81a9..56a01e6 100644
--- a/compiler/optimizing/side_effects_analysis.h
+++ b/compiler/optimizing/side_effects_analysis.h
@@ -37,7 +37,7 @@
   SideEffects GetBlockEffects(HBasicBlock* block) const;
 
   // Compute side effects of individual blocks and loops.
-  void Run();
+  bool Run();
 
   bool HasRun() const { return has_run_; }
 
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index cb27ded..5370f43 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -23,9 +23,10 @@
 
 namespace art {
 
-void SsaDeadPhiElimination::Run() {
+bool SsaDeadPhiElimination::Run() {
   MarkDeadPhis();
   EliminateDeadPhis();
+  return true;
 }
 
 void SsaDeadPhiElimination::MarkDeadPhis() {
@@ -122,7 +123,7 @@
   }
 }
 
-void SsaRedundantPhiElimination::Run() {
+bool SsaRedundantPhiElimination::Run() {
   // Use local allocator for allocating memory used by this optimization.
   ScopedArenaAllocator allocator(graph_->GetArenaStack());
 
@@ -255,6 +256,7 @@
       current->GetBlock()->RemovePhi(current);
     }
   }
+  return true;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/ssa_phi_elimination.h b/compiler/optimizing/ssa_phi_elimination.h
index 11d5837..ee859e8 100644
--- a/compiler/optimizing/ssa_phi_elimination.h
+++ b/compiler/optimizing/ssa_phi_elimination.h
@@ -31,7 +31,7 @@
   explicit SsaDeadPhiElimination(HGraph* graph)
       : HOptimization(graph, kSsaDeadPhiEliminationPassName) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   void MarkDeadPhis();
   void EliminateDeadPhis();
@@ -53,7 +53,7 @@
   explicit SsaRedundantPhiElimination(HGraph* graph)
       : HOptimization(graph, kSsaRedundantPhiEliminationPassName) {}
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kSsaRedundantPhiEliminationPassName = "redundant_phi_elimination";
 
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 7010e3f..aa28c8b 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -51,15 +51,7 @@
   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_++;
-  }
 
-  // Note: dex_pc can be kNoDexPc for native method intrinsics.
-  if (dex_pc != dex::kDexNoIndex && (dex_pc_max_ == dex::kDexNoIndex || dex_pc_max_ < dex_pc)) {
-    dex_pc_max_ = dex_pc;
-  }
-  register_mask_max_ = std::max(register_mask_max_, register_mask);
   current_dex_register_ = 0;
 }
 
@@ -146,51 +138,6 @@
   current_inline_info_ = InlineInfoEntry();
 }
 
-CodeOffset StackMapStream::ComputeMaxNativePcCodeOffset() const {
-  CodeOffset max_native_pc_offset;
-  for (const StackMapEntry& entry : stack_maps_) {
-    max_native_pc_offset = std::max(max_native_pc_offset, entry.native_pc_code_offset);
-  }
-  return max_native_pc_offset;
-}
-
-size_t StackMapStream::PrepareForFillIn() {
-  CodeInfoEncoding encoding;
-  encoding.dex_register_map.num_entries = 0;  // TODO: Remove this field.
-  encoding.dex_register_map.num_bytes = ComputeDexRegisterMapsSize();
-  encoding.location_catalog.num_entries = location_catalog_entries_.size();
-  encoding.location_catalog.num_bytes = ComputeDexRegisterLocationCatalogSize();
-  encoding.inline_info.num_entries = inline_infos_.size();
-  // Must be done before calling ComputeInlineInfoEncoding since ComputeInlineInfoEncoding requires
-  // dex_method_index_idx to be filled in.
-  PrepareMethodIndices();
-  ComputeInlineInfoEncoding(&encoding.inline_info.encoding,
-                            encoding.dex_register_map.num_bytes);
-  CodeOffset max_native_pc_offset = ComputeMaxNativePcCodeOffset();
-  // Prepare the CodeInfo variable-sized encoding.
-  encoding.stack_mask.encoding.num_bits = stack_mask_max_ + 1;  // Need room for max element too.
-  encoding.stack_mask.num_entries = PrepareStackMasks(encoding.stack_mask.encoding.num_bits);
-  encoding.register_mask.encoding.num_bits = MinimumBitsToStore(register_mask_max_);
-  encoding.register_mask.num_entries = PrepareRegisterMasks();
-  encoding.stack_map.num_entries = stack_maps_.size();
-  encoding.stack_map.encoding.SetFromSizes(
-      // The stack map contains compressed native PC offsets.
-      max_native_pc_offset.CompressedValue(),
-      dex_pc_max_,
-      encoding.dex_register_map.num_bytes,
-      encoding.inline_info.num_entries,
-      encoding.register_mask.num_entries,
-      encoding.stack_mask.num_entries);
-  ComputeInvokeInfoEncoding(&encoding);
-  DCHECK_EQ(code_info_encoding_.size(), 0u);
-  encoding.Compress(&code_info_encoding_);
-  encoding.ComputeTableOffsets();
-  // Compute table offsets so we can get the non header size.
-  DCHECK_EQ(encoding.HeaderSize(), code_info_encoding_.size());
-  needed_size_ = code_info_encoding_.size() + encoding.NonHeaderSize();
-  return needed_size_;
-}
-
 size_t StackMapStream::ComputeDexRegisterLocationCatalogSize() const {
   size_t size = DexRegisterLocationCatalog::kFixedSize;
   for (const DexRegisterLocation& dex_register_location : location_catalog_entries_) {
@@ -204,6 +151,10 @@
   if (num_dex_registers == 0u) {
     return 0u;  // No register map will be emitted.
   }
+  size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits();
+  if (live_dex_registers_mask->NumSetBits() == 0) {
+    return 0u;  // No register map will be emitted.
+  }
   DCHECK(live_dex_registers_mask != nullptr);
 
   // Size of the map in bytes.
@@ -211,7 +162,6 @@
   // Add the live bit mask for the Dex register liveness.
   size += DexRegisterMap::GetLiveBitMaskSize(num_dex_registers);
   // Compute the size of the set of live Dex register entries.
-  size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits();
   size_t map_entries_size_in_bits =
       DexRegisterMap::SingleEntrySizeInBits(catalog_size) * number_of_live_dex_registers;
   size_t map_entries_size_in_bytes =
@@ -220,86 +170,6 @@
   return size;
 }
 
-size_t StackMapStream::ComputeDexRegisterMapsSize() const {
-  size_t size = 0;
-  for (const DexRegisterMapEntry& entry : dex_register_entries_) {
-    size += entry.ComputeSize(location_catalog_entries_.size());
-  }
-  return size;
-}
-
-void StackMapStream::ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding) {
-  DCHECK(encoding != nullptr);
-  uint32_t native_pc_max = 0;
-  uint16_t method_index_max = 0;
-  size_t invoke_infos_count = 0;
-  size_t invoke_type_max = 0;
-  for (const StackMapEntry& entry : stack_maps_) {
-    if (entry.dex_method_index != dex::kDexNoIndex) {
-      native_pc_max = std::max(native_pc_max, entry.native_pc_code_offset.CompressedValue());
-      method_index_max = std::max(method_index_max, static_cast<uint16_t>(entry.dex_method_index));
-      invoke_type_max = std::max(invoke_type_max, static_cast<size_t>(entry.invoke_type));
-      ++invoke_infos_count;
-    }
-  }
-  encoding->invoke_info.num_entries = invoke_infos_count;
-  encoding->invoke_info.encoding.SetFromSizes(native_pc_max, invoke_type_max, method_index_max);
-}
-
-void StackMapStream::ComputeInlineInfoEncoding(InlineInfoEncoding* encoding,
-                                               size_t dex_register_maps_bytes) {
-  uint32_t method_index_max = 0;
-  uint32_t dex_pc_max = dex::kDexNoIndex;
-  uint32_t extra_data_max = 0;
-
-  uint32_t inline_info_index = 0;
-  for (const StackMapEntry& entry : stack_maps_) {
-    for (size_t j = 0; j < entry.inlining_depth; ++j) {
-      InlineInfoEntry inline_entry = inline_infos_[inline_info_index++];
-      if (inline_entry.method == nullptr) {
-        method_index_max = std::max(method_index_max, inline_entry.dex_method_index_idx);
-        extra_data_max = std::max(extra_data_max, 1u);
-      } else {
-        method_index_max = std::max(
-            method_index_max, High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
-        extra_data_max = std::max(
-            extra_data_max, Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
-      }
-      if (inline_entry.dex_pc != dex::kDexNoIndex &&
-          (dex_pc_max == dex::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) {
-        dex_pc_max = inline_entry.dex_pc;
-      }
-    }
-  }
-  DCHECK_EQ(inline_info_index, inline_infos_.size());
-
-  encoding->SetFromSizes(method_index_max, dex_pc_max, extra_data_max, dex_register_maps_bytes);
-}
-
-size_t StackMapStream::MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry,
-                                               size_t* current_offset,
-                                               MemoryRegion dex_register_locations_region) {
-  DCHECK(current_offset != nullptr);
-  if ((entry.num_dex_registers == 0) || (entry.live_dex_registers_mask->NumSetBits() == 0)) {
-    // No dex register map needed.
-    return StackMap::kNoDexRegisterMap;
-  }
-  if (entry.offset == DexRegisterMapEntry::kOffsetUnassigned) {
-    // Not already copied, need to copy and and assign an offset.
-    entry.offset = *current_offset;
-    const size_t entry_size = entry.ComputeSize(location_catalog_entries_.size());
-    DexRegisterMap dex_register_map(
-        dex_register_locations_region.Subregion(entry.offset, entry_size));
-    *current_offset += entry_size;
-    // Fill in the map since it was just added.
-    FillInDexRegisterMap(dex_register_map,
-                         entry.num_dex_registers,
-                         *entry.live_dex_registers_mask,
-                         entry.locations_start_index);
-  }
-  return entry.offset;
-}
-
 void StackMapStream::FillInMethodInfo(MemoryRegion region) {
   {
     MethodInfo info(region.begin(), method_indices_.size());
@@ -318,30 +188,64 @@
   }
 }
 
-void StackMapStream::FillInCodeInfo(MemoryRegion region) {
-  DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry";
-  DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before FillIn";
+template<typename Vector>
+static MemoryRegion EncodeMemoryRegion(Vector* out, size_t* bit_offset, uint32_t bit_length) {
+  uint32_t byte_length = BitsToBytesRoundUp(bit_length);
+  EncodeVarintBits(out, bit_offset, byte_length);
+  *bit_offset = RoundUp(*bit_offset, kBitsPerByte);
+  out->resize(out->size() + byte_length);
+  MemoryRegion region(out->data() + *bit_offset / kBitsPerByte, byte_length);
+  *bit_offset += kBitsPerByte * byte_length;
+  return region;
+}
 
-  DCHECK_EQ(region.size(), needed_size_);
+template<uint32_t NumColumns>
+using ScopedBitTableBuilder = BitTableBuilder<NumColumns, ScopedArenaAllocatorAdapter<uint32_t>>;
 
-  // Note that the memory region does not have to be zeroed when we JIT code
-  // because we do not use the arena allocator there.
+size_t StackMapStream::PrepareForFillIn() {
+  size_t bit_offset = 0;
+  out_.clear();
 
-  // Write the CodeInfo header.
-  region.CopyFrom(0, MemoryRegion(code_info_encoding_.data(), code_info_encoding_.size()));
+  // Decide the offsets of dex register map entries, but do not write them out yet.
+  // Needs to be done first as it modifies the stack map entry.
+  size_t dex_register_map_bytes = 0;
+  for (DexRegisterMapEntry& entry : dex_register_entries_) {
+    size_t size = entry.ComputeSize(location_catalog_entries_.size());
+    entry.offset = size == 0 ? DexRegisterMapEntry::kOffsetUnassigned : dex_register_map_bytes;
+    dex_register_map_bytes += size;
+  }
 
-  CodeInfo code_info(region);
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  DCHECK_EQ(encoding.stack_map.num_entries, stack_maps_.size());
+  // Must be done before calling ComputeInlineInfoEncoding since ComputeInlineInfoEncoding requires
+  // dex_method_index_idx to be filled in.
+  PrepareMethodIndices();
 
-  MemoryRegion dex_register_locations_region = region.Subregion(
-      encoding.dex_register_map.byte_offset,
-      encoding.dex_register_map.num_bytes);
+  // Dedup stack masks. Needs to be done first as it modifies the stack map entry.
+  size_t stack_mask_bits = stack_mask_max_ + 1;  // Need room for max element too.
+  size_t num_stack_masks = PrepareStackMasks(stack_mask_bits);
 
-  // Set the Dex register location catalog.
-  MemoryRegion dex_register_location_catalog_region = region.Subregion(
-      encoding.location_catalog.byte_offset,
-      encoding.location_catalog.num_bytes);
+  // Dedup register masks. Needs to be done first as it modifies the stack map entry.
+  size_t num_register_masks = PrepareRegisterMasks();
+
+  // Write dex register maps.
+  MemoryRegion dex_register_map_region =
+      EncodeMemoryRegion(&out_, &bit_offset, dex_register_map_bytes * kBitsPerByte);
+  for (DexRegisterMapEntry& entry : dex_register_entries_) {
+    size_t entry_size = entry.ComputeSize(location_catalog_entries_.size());
+    if (entry_size != 0) {
+      DexRegisterMap dex_register_map(
+          dex_register_map_region.Subregion(entry.offset, entry_size));
+      FillInDexRegisterMap(dex_register_map,
+                           entry.num_dex_registers,
+                           *entry.live_dex_registers_mask,
+                           entry.locations_start_index);
+    }
+  }
+
+  // Write dex register catalog.
+  EncodeVarintBits(&out_, &bit_offset, location_catalog_entries_.size());
+  size_t location_catalog_bytes = ComputeDexRegisterLocationCatalogSize();
+  MemoryRegion dex_register_location_catalog_region =
+      EncodeMemoryRegion(&out_, &bit_offset, location_catalog_bytes * kBitsPerByte);
   DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region);
   // Offset in `dex_register_location_catalog` where to store the next
   // register location.
@@ -353,93 +257,87 @@
   // Ensure we reached the end of the Dex registers location_catalog.
   DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size());
 
-  ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false, kArenaAllocStackMapStream);
-  uintptr_t next_dex_register_map_offset = 0;
-  uintptr_t next_inline_info_index = 0;
-  size_t invoke_info_idx = 0;
-  for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) {
-    StackMap stack_map = code_info.GetStackMapAt(i, encoding);
-    StackMapEntry entry = stack_maps_[i];
-
-    stack_map.SetDexPc(encoding.stack_map.encoding, entry.dex_pc);
-    stack_map.SetNativePcCodeOffset(encoding.stack_map.encoding, entry.native_pc_code_offset);
-    stack_map.SetRegisterMaskIndex(encoding.stack_map.encoding, entry.register_mask_index);
-    stack_map.SetStackMaskIndex(encoding.stack_map.encoding, entry.stack_mask_index);
-
-    size_t offset = MaybeCopyDexRegisterMap(dex_register_entries_[entry.dex_register_map_index],
-                                            &next_dex_register_map_offset,
-                                            dex_register_locations_region);
-    stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, offset);
-
+  // Write stack maps.
+  ScopedArenaAllocatorAdapter<void> adapter = allocator_->Adapter(kArenaAllocStackMapStream);
+  ScopedBitTableBuilder<StackMap::Field::kCount> stack_map_builder((adapter));
+  ScopedBitTableBuilder<InvokeInfo::Field::kCount> invoke_info_builder((adapter));
+  ScopedBitTableBuilder<InlineInfo::Field::kCount> inline_info_builder((adapter));
+  for (const StackMapEntry& entry : stack_maps_) {
     if (entry.dex_method_index != dex::kDexNoIndex) {
-      InvokeInfo invoke_info(code_info.GetInvokeInfo(encoding, invoke_info_idx));
-      invoke_info.SetNativePcCodeOffset(encoding.invoke_info.encoding, entry.native_pc_code_offset);
-      invoke_info.SetInvokeType(encoding.invoke_info.encoding, entry.invoke_type);
-      invoke_info.SetMethodIndexIdx(encoding.invoke_info.encoding, entry.dex_method_index_idx);
-      ++invoke_info_idx;
+      invoke_info_builder.AddRow(
+          entry.native_pc_code_offset.CompressedValue(),
+          entry.invoke_type,
+          entry.dex_method_index_idx);
     }
 
     // Set the inlining info.
-    if (entry.inlining_depth != 0) {
-      InlineInfo inline_info = code_info.GetInlineInfo(next_inline_info_index, encoding);
-
-      // Fill in the index.
-      stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, next_inline_info_index);
-      DCHECK_EQ(next_inline_info_index, entry.inline_infos_start_index);
-      next_inline_info_index += entry.inlining_depth;
-
-      inline_info.SetDepth(encoding.inline_info.encoding, entry.inlining_depth);
-      DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size());
-
-      for (size_t depth = 0; depth < entry.inlining_depth; ++depth) {
-        InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index];
-        if (inline_entry.method != nullptr) {
-          inline_info.SetMethodIndexIdxAtDepth(
-              encoding.inline_info.encoding,
-              depth,
-              High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
-          inline_info.SetExtraDataAtDepth(
-              encoding.inline_info.encoding,
-              depth,
-              Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
-        } else {
-          inline_info.SetMethodIndexIdxAtDepth(encoding.inline_info.encoding,
-                                               depth,
-                                               inline_entry.dex_method_index_idx);
-          inline_info.SetExtraDataAtDepth(encoding.inline_info.encoding, depth, 1);
-        }
-        inline_info.SetDexPcAtDepth(encoding.inline_info.encoding, depth, inline_entry.dex_pc);
-        size_t dex_register_map_offset = MaybeCopyDexRegisterMap(
-            dex_register_entries_[inline_entry.dex_register_map_index],
-            &next_dex_register_map_offset,
-            dex_register_locations_region);
-        inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding,
-                                                   depth,
-                                                   dex_register_map_offset);
+    uint32_t inline_info_index = StackMap::kNoValue;
+    DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size());
+    for (size_t depth = 0; depth < entry.inlining_depth; ++depth) {
+      InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index];
+      uint32_t method_index_idx = inline_entry.dex_method_index_idx;
+      uint32_t extra_data = 1;
+      if (inline_entry.method != nullptr) {
+        method_index_idx = High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method));
+        extra_data = Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method));
       }
-    } else if (encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
-      stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, StackMap::kNoInlineInfo);
-    }
-  }
-
-  // Write stack masks table.
-  const size_t stack_mask_bits = encoding.stack_mask.encoding.BitSize();
-  if (stack_mask_bits > 0) {
-    size_t stack_mask_bytes = RoundUp(stack_mask_bits, kBitsPerByte) / kBitsPerByte;
-    for (size_t i = 0; i < encoding.stack_mask.num_entries; ++i) {
-      MemoryRegion source(&stack_masks_[i * stack_mask_bytes], stack_mask_bytes);
-      BitMemoryRegion stack_mask = code_info.GetStackMask(i, encoding);
-      for (size_t bit_index = 0; bit_index < stack_mask_bits; ++bit_index) {
-        stack_mask.StoreBit(bit_index, source.LoadBit(bit_index));
+      uint32_t index = inline_info_builder.AddRow(
+          (depth == entry.inlining_depth - 1) ? InlineInfo::kLast : InlineInfo::kMore,
+          method_index_idx,
+          inline_entry.dex_pc,
+          extra_data,
+          dex_register_entries_[inline_entry.dex_register_map_index].offset);
+      if (depth == 0) {
+        inline_info_index = index;
       }
     }
+    stack_map_builder.AddRow(
+        entry.native_pc_code_offset.CompressedValue(),
+        entry.dex_pc,
+        dex_register_entries_[entry.dex_register_map_index].offset,
+        inline_info_index,
+        entry.register_mask_index,
+        entry.stack_mask_index);
   }
+  stack_map_builder.Encode(&out_, &bit_offset);
+  invoke_info_builder.Encode(&out_, &bit_offset);
+  inline_info_builder.Encode(&out_, &bit_offset);
 
   // Write register masks table.
-  for (size_t i = 0; i < encoding.register_mask.num_entries; ++i) {
-    BitMemoryRegion register_mask = code_info.GetRegisterMask(i, encoding);
-    register_mask.StoreBits(0, register_masks_[i], encoding.register_mask.encoding.BitSize());
+  ScopedBitTableBuilder<1> register_mask_builder((adapter));
+  for (size_t i = 0; i < num_register_masks; ++i) {
+    register_mask_builder.AddRow(register_masks_[i]);
   }
+  register_mask_builder.Encode(&out_, &bit_offset);
+
+  // Write stack masks table.
+  EncodeVarintBits(&out_, &bit_offset, stack_mask_bits);
+  out_.resize(BitsToBytesRoundUp(bit_offset + stack_mask_bits * num_stack_masks));
+  BitMemoryRegion stack_mask_region(MemoryRegion(out_.data(), out_.size()),
+                                    bit_offset,
+                                    stack_mask_bits * num_stack_masks);
+  if (stack_mask_bits > 0) {
+    for (size_t i = 0; i < num_stack_masks; ++i) {
+      size_t stack_mask_bytes = BitsToBytesRoundUp(stack_mask_bits);
+      BitMemoryRegion src(MemoryRegion(&stack_masks_[i * stack_mask_bytes], stack_mask_bytes));
+      BitMemoryRegion dst = stack_mask_region.Subregion(i * stack_mask_bits, stack_mask_bits);
+      for (size_t bit_index = 0; bit_index < stack_mask_bits; bit_index += BitSizeOf<uint32_t>()) {
+        size_t num_bits = std::min<size_t>(stack_mask_bits - bit_index, BitSizeOf<uint32_t>());
+        dst.StoreBits(bit_index, src.LoadBits(bit_index, num_bits), num_bits);
+      }
+    }
+  }
+
+  return UnsignedLeb128Size(out_.size()) +  out_.size();
+}
+
+void StackMapStream::FillInCodeInfo(MemoryRegion region) {
+  DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry";
+  DCHECK_NE(0u, out_.size()) << "PrepareForFillIn not called before FillIn";
+  DCHECK_EQ(region.size(), UnsignedLeb128Size(out_.size()) +  out_.size());
+
+  uint8_t* ptr = EncodeUnsignedLeb128(region.begin(), out_.size());
+  region.CopyFromVector(ptr - region.begin(), out_);
 
   // Verify all written data in debug build.
   if (kIsDebugBuild) {
@@ -527,7 +425,6 @@
                                          size_t num_dex_registers,
                                          BitVector* live_dex_registers_mask,
                                          size_t dex_register_locations_index) const {
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
   for (size_t reg = 0; reg < num_dex_registers; reg++) {
     // Find the location we tried to encode.
     DexRegisterLocation expected = DexRegisterLocation::None();
@@ -542,7 +439,7 @@
     } else {
       DCHECK(dex_register_map.IsDexRegisterLive(reg));
       DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation(
-          reg, num_dex_registers, code_info, encoding);
+          reg, num_dex_registers, code_info);
       DCHECK_EQ(expected.GetKind(), seen.GetKind());
       DCHECK_EQ(expected.GetValue(), seen.GetValue());
     }
@@ -600,8 +497,9 @@
   for (StackMapEntry& stack_map : stack_maps_) {
     size_t index = dedup.size();
     MemoryRegion stack_mask(stack_masks_.data() + index * byte_entry_size, byte_entry_size);
+    BitMemoryRegion stack_mask_bits(stack_mask);
     for (size_t i = 0; i < entry_size_in_bits; i++) {
-      stack_mask.StoreBit(i, stack_map.sp_mask != nullptr && stack_map.sp_mask->IsBitSet(i));
+      stack_mask_bits.StoreBit(i, stack_map.sp_mask != nullptr && stack_map.sp_mask->IsBitSet(i));
     }
     stack_map.stack_mask_index = dedup.emplace(stack_mask, index).first->second;
   }
@@ -611,23 +509,23 @@
 // Check that all StackMapStream inputs are correctly encoded by trying to read them back.
 void StackMapStream::CheckCodeInfo(MemoryRegion region) const {
   CodeInfo code_info(region);
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  DCHECK_EQ(code_info.GetNumberOfStackMaps(encoding), stack_maps_.size());
+  DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size());
+  DCHECK_EQ(code_info.GetNumberOfStackMaskBits(), static_cast<uint32_t>(stack_mask_max_ + 1));
+  DCHECK_EQ(code_info.GetNumberOfLocationCatalogEntries(), location_catalog_entries_.size());
   size_t invoke_info_index = 0;
   for (size_t s = 0; s < stack_maps_.size(); ++s) {
-    const StackMap stack_map = code_info.GetStackMapAt(s, encoding);
-    const StackMapEncoding& stack_map_encoding = encoding.stack_map.encoding;
+    const StackMap stack_map = code_info.GetStackMapAt(s);
     StackMapEntry entry = stack_maps_[s];
 
     // Check main stack map fields.
-    DCHECK_EQ(stack_map.GetNativePcOffset(stack_map_encoding, instruction_set_),
+    DCHECK_EQ(stack_map.GetNativePcOffset(instruction_set_),
               entry.native_pc_code_offset.Uint32Value(instruction_set_));
-    DCHECK_EQ(stack_map.GetDexPc(stack_map_encoding), entry.dex_pc);
-    DCHECK_EQ(stack_map.GetRegisterMaskIndex(stack_map_encoding), entry.register_mask_index);
-    DCHECK_EQ(code_info.GetRegisterMaskOf(encoding, stack_map), entry.register_mask);
-    const size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits(encoding);
-    DCHECK_EQ(stack_map.GetStackMaskIndex(stack_map_encoding), entry.stack_mask_index);
-    BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map);
+    DCHECK_EQ(stack_map.GetDexPc(), entry.dex_pc);
+    DCHECK_EQ(stack_map.GetRegisterMaskIndex(), entry.register_mask_index);
+    DCHECK_EQ(code_info.GetRegisterMaskOf(stack_map), entry.register_mask);
+    const size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits();
+    DCHECK_EQ(stack_map.GetStackMaskIndex(), entry.stack_mask_index);
+    BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map);
     if (entry.sp_mask != nullptr) {
       DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits());
       for (size_t b = 0; b < num_stack_mask_bits; b++) {
@@ -639,38 +537,36 @@
       }
     }
     if (entry.dex_method_index != dex::kDexNoIndex) {
-      InvokeInfo invoke_info = code_info.GetInvokeInfo(encoding, invoke_info_index);
-      DCHECK_EQ(invoke_info.GetNativePcOffset(encoding.invoke_info.encoding, instruction_set_),
+      InvokeInfo invoke_info = code_info.GetInvokeInfo(invoke_info_index);
+      DCHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_),
                 entry.native_pc_code_offset.Uint32Value(instruction_set_));
-      DCHECK_EQ(invoke_info.GetInvokeType(encoding.invoke_info.encoding), entry.invoke_type);
-      DCHECK_EQ(invoke_info.GetMethodIndexIdx(encoding.invoke_info.encoding),
-                entry.dex_method_index_idx);
+      DCHECK_EQ(invoke_info.GetInvokeType(), entry.invoke_type);
+      DCHECK_EQ(invoke_info.GetMethodIndexIdx(), entry.dex_method_index_idx);
       invoke_info_index++;
     }
     CheckDexRegisterMap(code_info,
                         code_info.GetDexRegisterMapOf(
-                            stack_map, encoding, entry.dex_register_entry.num_dex_registers),
+                            stack_map, entry.dex_register_entry.num_dex_registers),
                         entry.dex_register_entry.num_dex_registers,
                         entry.dex_register_entry.live_dex_registers_mask,
                         entry.dex_register_entry.locations_start_index);
 
     // Check inline info.
-    DCHECK_EQ(stack_map.HasInlineInfo(stack_map_encoding), (entry.inlining_depth != 0));
+    DCHECK_EQ(stack_map.HasInlineInfo(), (entry.inlining_depth != 0));
     if (entry.inlining_depth != 0) {
-      InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
-      DCHECK_EQ(inline_info.GetDepth(encoding.inline_info.encoding), entry.inlining_depth);
+      InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
+      DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth);
       for (size_t d = 0; d < entry.inlining_depth; ++d) {
         size_t inline_info_index = entry.inline_infos_start_index + d;
         DCHECK_LT(inline_info_index, inline_infos_.size());
         InlineInfoEntry inline_entry = inline_infos_[inline_info_index];
-        DCHECK_EQ(inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, d),
-                  inline_entry.dex_pc);
-        if (inline_info.EncodesArtMethodAtDepth(encoding.inline_info.encoding, d)) {
-          DCHECK_EQ(inline_info.GetArtMethodAtDepth(encoding.inline_info.encoding, d),
+        DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc);
+        if (inline_info.EncodesArtMethodAtDepth(d)) {
+          DCHECK_EQ(inline_info.GetArtMethodAtDepth(d),
                     inline_entry.method);
         } else {
           const size_t method_index_idx =
-              inline_info.GetMethodIndexIdxAtDepth(encoding.inline_info.encoding, d);
+              inline_info.GetMethodIndexIdxAtDepth(d);
           DCHECK_EQ(method_index_idx, inline_entry.dex_method_index_idx);
           DCHECK_EQ(method_indices_[method_index_idx], inline_entry.method_index);
         }
@@ -679,7 +575,6 @@
                             code_info.GetDexRegisterMapAtDepth(
                                 d,
                                 inline_info,
-                                encoding,
                                 inline_entry.dex_register_entry.num_dex_registers),
                             inline_entry.dex_register_entry.num_dex_registers,
                             inline_entry.dex_register_entry.live_dex_registers_mask,
@@ -690,7 +585,7 @@
 }
 
 size_t StackMapStream::ComputeMethodInfoSize() const {
-  DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before " << __FUNCTION__;
+  DCHECK_NE(0u, out_.size()) << "PrepareForFillIn not called before " << __FUNCTION__;
   return MethodInfo::ComputeSize(method_indices_.size());
 }
 
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 268e9bd..ea97cf6 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -73,36 +73,32 @@
         method_indices_(allocator->Adapter(kArenaAllocStackMapStream)),
         dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)),
         stack_mask_max_(-1),
-        dex_pc_max_(kNoDexPc),
-        register_mask_max_(0),
-        number_of_stack_maps_with_inline_info_(0),
+        out_(allocator->Adapter(kArenaAllocStackMapStream)),
         dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(),
                                            allocator->Adapter(kArenaAllocStackMapStream)),
         current_entry_(),
         current_inline_info_(),
-        code_info_encoding_(allocator->Adapter(kArenaAllocStackMapStream)),
-        needed_size_(0),
         current_dex_register_(0),
         in_inline_frame_(false) {
     stack_maps_.reserve(10);
+    out_.reserve(64);
     location_catalog_entries_.reserve(4);
     dex_register_locations_.reserve(10 * 4);
     inline_infos_.reserve(2);
-    code_info_encoding_.reserve(16);
   }
 
   // A dex register map entry for a single stack map entry, contains what registers are live as
   // well as indices into the location catalog.
   class DexRegisterMapEntry {
    public:
-    static const size_t kOffsetUnassigned = -1;
+    static const uint32_t kOffsetUnassigned = -1;
 
     BitVector* live_dex_registers_mask;
     uint32_t num_dex_registers;
     size_t locations_start_index;
     // Computed fields
     size_t hash = 0;
-    size_t offset = kOffsetUnassigned;
+    uint32_t offset = kOffsetUnassigned;
 
     size_t ComputeSize(size_t catalog_size) const;
   };
@@ -113,7 +109,7 @@
     CodeOffset native_pc_code_offset;
     uint32_t register_mask;
     BitVector* sp_mask;
-    uint8_t inlining_depth;
+    uint32_t inlining_depth;
     size_t inline_infos_start_index;
     uint32_t stack_mask_index;
     uint32_t register_mask_index;
@@ -174,11 +170,6 @@
 
  private:
   size_t ComputeDexRegisterLocationCatalogSize() const;
-  size_t ComputeDexRegisterMapsSize() const;
-  void ComputeInlineInfoEncoding(InlineInfoEncoding* encoding,
-                                 size_t dex_register_maps_bytes);
-
-  CodeOffset ComputeMaxNativePcCodeOffset() const;
 
   // Returns the number of unique stack masks.
   size_t PrepareStackMasks(size_t entry_size_in_bits);
@@ -197,24 +188,11 @@
   bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const;
 
   // Fill in the corresponding entries of a register map.
-  void ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding);
-
-  // Returns the index of an entry with the same dex register map as the current_entry,
-  // or kNoSameDexMapFound if no such entry exists.
-  size_t FindEntryWithTheSameDexMap();
-  bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const;
-
-  // Fill in the corresponding entries of a register map.
   void FillInDexRegisterMap(DexRegisterMap dex_register_map,
                             uint32_t num_dex_registers,
                             const BitVector& live_dex_registers_mask,
                             uint32_t start_index_in_dex_register_locations) const;
 
-  // Returns the offset for the dex register inside of the dex register location region. See FillIn.
-  // Only copies the dex register map if the offset for the entry is not already assigned.
-  size_t MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry,
-                                 size_t* current_offset,
-                                 MemoryRegion dex_register_locations_region);
   void CheckDexRegisterMap(const CodeInfo& code_info,
                            const DexRegisterMap& dex_register_map,
                            size_t num_dex_registers,
@@ -244,21 +222,16 @@
   ScopedArenaVector<uint32_t> method_indices_;
   ScopedArenaVector<DexRegisterMapEntry> dex_register_entries_;
   int stack_mask_max_;
-  uint32_t dex_pc_max_;
-  uint32_t register_mask_max_;
-  size_t number_of_stack_maps_with_inline_info_;
+
+  ScopedArenaVector<uint8_t> out_;
 
   ScopedArenaSafeMap<uint32_t, ScopedArenaVector<uint32_t>> dex_map_hash_to_stack_map_indices_;
 
   StackMapEntry current_entry_;
   InlineInfoEntry current_inline_info_;
-  ScopedArenaVector<uint8_t> code_info_encoding_;
-  size_t needed_size_;
   uint32_t current_dex_register_;
   bool in_inline_frame_;
 
-  static constexpr uint32_t kNoSameDexMapFound = -1;
-
   DISALLOW_COPY_AND_ASSIGN(StackMapStream);
 };
 
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index e36c592..9db7588 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -29,14 +29,13 @@
 // to the given bit vector. Returns true if they are same.
 static bool CheckStackMask(
     const CodeInfo& code_info,
-    const CodeInfoEncoding& encoding,
     const StackMap& stack_map,
     const BitVector& bit_vector) {
-  BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map);
-  if (bit_vector.GetNumberOfBits() > encoding.stack_mask.encoding.BitSize()) {
+  BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map);
+  if (bit_vector.GetNumberOfBits() > code_info.GetNumberOfStackMaskBits()) {
     return false;
   }
-  for (size_t i = 0; i < encoding.stack_mask.encoding.BitSize(); ++i) {
+  for (size_t i = 0; i < code_info.GetNumberOfStackMaskBits(); ++i) {
     if (stack_mask.LoadBit(i) != bit_vector.IsBitSet(i)) {
       return false;
     }
@@ -65,30 +64,29 @@
   stream.FillInCodeInfo(region);
 
   CodeInfo code_info(region);
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding));
+  ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
 
-  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
+  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries();
   ASSERT_EQ(2u, number_of_catalog_entries);
-  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
   // The Dex register location catalog contains:
   // - one 1-byte short Dex register location, and
   // - one 5-byte large Dex register location.
   size_t expected_location_catalog_size = 1u + 5u;
   ASSERT_EQ(expected_location_catalog_size, location_catalog.Size());
 
-  StackMap stack_map = code_info.GetStackMapAt(0, encoding);
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
-  ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding));
-  ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
-  ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map));
+  StackMap stack_map = code_info.GetStackMapAt(0);
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
+  ASSERT_EQ(0u, stack_map.GetDexPc());
+  ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA));
+  ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map));
 
-  ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask));
+  ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask));
 
-  ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
+  ASSERT_TRUE(stack_map.HasDexRegisterMap());
   DexRegisterMap dex_register_map =
-      code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
+      code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
   ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
   ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
   ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -99,16 +97,16 @@
   ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
 
   ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind(
-                0, number_of_dex_registers, code_info, encoding));
+                0, number_of_dex_registers, code_info));
   ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(
-                1, number_of_dex_registers, code_info, encoding));
+                1, number_of_dex_registers, code_info));
   ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind(
-                0, number_of_dex_registers, code_info, encoding));
+                0, number_of_dex_registers, code_info));
   ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(
-                1, number_of_dex_registers, code_info, encoding));
+                1, number_of_dex_registers, code_info));
   ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(
-                0, number_of_dex_registers, code_info, encoding));
-  ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding));
+                0, number_of_dex_registers, code_info));
+  ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info));
 
   size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
       0, number_of_dex_registers, number_of_catalog_entries);
@@ -125,7 +123,7 @@
   ASSERT_EQ(0, location0.GetValue());
   ASSERT_EQ(-2, location1.GetValue());
 
-  ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
+  ASSERT_FALSE(stack_map.HasInlineInfo());
 }
 
 TEST(StackMapTest, Test2) {
@@ -179,12 +177,11 @@
   stream.FillInCodeInfo(region);
 
   CodeInfo code_info(region);
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  ASSERT_EQ(4u, code_info.GetNumberOfStackMaps(encoding));
+  ASSERT_EQ(4u, code_info.GetNumberOfStackMaps());
 
-  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
+  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries();
   ASSERT_EQ(7u, number_of_catalog_entries);
-  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
   // The Dex register location catalog contains:
   // - six 1-byte short Dex register locations, and
   // - one 5-byte large Dex register location.
@@ -193,18 +190,18 @@
 
   // First stack map.
   {
-    StackMap stack_map = code_info.GetStackMapAt(0, encoding);
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
-    ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding));
-    ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
-    ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map));
+    StackMap stack_map = code_info.GetStackMapAt(0);
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
+    ASSERT_EQ(0u, stack_map.GetDexPc());
+    ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA));
+    ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map));
 
-    ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1));
+    ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1));
 
-    ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
+    ASSERT_TRUE(stack_map.HasDexRegisterMap());
     DexRegisterMap dex_register_map =
-        code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
+        code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
     ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
     ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
     ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -215,16 +212,16 @@
     ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
 
     ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
     ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(
-                  0, number_of_dex_registers, code_info, encoding));
-    ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
+    ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info));
 
     size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
         0, number_of_dex_registers, number_of_catalog_entries);
@@ -241,29 +238,29 @@
     ASSERT_EQ(0, location0.GetValue());
     ASSERT_EQ(-2, location1.GetValue());
 
-    ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
-    InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
-    ASSERT_EQ(2u, inline_info.GetDepth(encoding.inline_info.encoding));
-    ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, 0));
-    ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, 1));
-    ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0));
-    ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1));
+    ASSERT_TRUE(stack_map.HasInlineInfo());
+    InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
+    ASSERT_EQ(2u, inline_info.GetDepth());
+    ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(0));
+    ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(1));
+    ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(0));
+    ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(1));
   }
 
   // Second stack map.
   {
-    StackMap stack_map = code_info.GetStackMapAt(1, encoding);
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u, encoding)));
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u, encoding)));
-    ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map.encoding));
-    ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
-    ASSERT_EQ(0xFFu, code_info.GetRegisterMaskOf(encoding, stack_map));
+    StackMap stack_map = code_info.GetStackMapAt(1);
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u)));
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u)));
+    ASSERT_EQ(1u, stack_map.GetDexPc());
+    ASSERT_EQ(128u, stack_map.GetNativePcOffset(kRuntimeISA));
+    ASSERT_EQ(0xFFu, code_info.GetRegisterMaskOf(stack_map));
 
-    ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask2));
+    ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask2));
 
-    ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
+    ASSERT_TRUE(stack_map.HasDexRegisterMap());
     DexRegisterMap dex_register_map =
-        code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
+        code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
     ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
     ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
     ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -274,17 +271,17 @@
     ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
 
     ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
     ASSERT_EQ(18, dex_register_map.GetMachineRegister(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(3, dex_register_map.GetMachineRegister(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
 
     size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
         0, number_of_dex_registers, number_of_catalog_entries);
@@ -301,23 +298,23 @@
     ASSERT_EQ(18, location0.GetValue());
     ASSERT_EQ(3, location1.GetValue());
 
-    ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
+    ASSERT_FALSE(stack_map.HasInlineInfo());
   }
 
   // Third stack map.
   {
-    StackMap stack_map = code_info.GetStackMapAt(2, encoding);
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(2u, encoding)));
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u, encoding)));
-    ASSERT_EQ(2u, stack_map.GetDexPc(encoding.stack_map.encoding));
-    ASSERT_EQ(192u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
-    ASSERT_EQ(0xABu, code_info.GetRegisterMaskOf(encoding, stack_map));
+    StackMap stack_map = code_info.GetStackMapAt(2);
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(2u)));
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u)));
+    ASSERT_EQ(2u, stack_map.GetDexPc());
+    ASSERT_EQ(192u, stack_map.GetNativePcOffset(kRuntimeISA));
+    ASSERT_EQ(0xABu, code_info.GetRegisterMaskOf(stack_map));
 
-    ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask3));
+    ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask3));
 
-    ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
+    ASSERT_TRUE(stack_map.HasDexRegisterMap());
     DexRegisterMap dex_register_map =
-        code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
+        code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
     ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
     ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
     ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -328,17 +325,17 @@
     ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
 
     ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationKind(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationInternalKind(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
     ASSERT_EQ(6, dex_register_map.GetMachineRegister(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(8, dex_register_map.GetMachineRegister(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
 
     size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
         0, number_of_dex_registers, number_of_catalog_entries);
@@ -355,23 +352,23 @@
     ASSERT_EQ(6, location0.GetValue());
     ASSERT_EQ(8, location1.GetValue());
 
-    ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
+    ASSERT_FALSE(stack_map.HasInlineInfo());
   }
 
   // Fourth stack map.
   {
-    StackMap stack_map = code_info.GetStackMapAt(3, encoding);
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(3u, encoding)));
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u, encoding)));
-    ASSERT_EQ(3u, stack_map.GetDexPc(encoding.stack_map.encoding));
-    ASSERT_EQ(256u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
-    ASSERT_EQ(0xCDu, code_info.GetRegisterMaskOf(encoding, stack_map));
+    StackMap stack_map = code_info.GetStackMapAt(3);
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(3u)));
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u)));
+    ASSERT_EQ(3u, stack_map.GetDexPc());
+    ASSERT_EQ(256u, stack_map.GetNativePcOffset(kRuntimeISA));
+    ASSERT_EQ(0xCDu, code_info.GetRegisterMaskOf(stack_map));
 
-    ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask4));
+    ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask4));
 
-    ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
+    ASSERT_TRUE(stack_map.HasDexRegisterMap());
     DexRegisterMap dex_register_map =
-        code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
+        code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
     ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
     ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
     ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -382,17 +379,17 @@
     ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
 
     ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationKind(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationInternalKind(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
     ASSERT_EQ(3, dex_register_map.GetMachineRegister(
-                  0, number_of_dex_registers, code_info, encoding));
+                  0, number_of_dex_registers, code_info));
     ASSERT_EQ(1, dex_register_map.GetMachineRegister(
-                  1, number_of_dex_registers, code_info, encoding));
+                  1, number_of_dex_registers, code_info));
 
     size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
         0, number_of_dex_registers, number_of_catalog_entries);
@@ -409,7 +406,7 @@
     ASSERT_EQ(3, location0.GetValue());
     ASSERT_EQ(1, location1.GetValue());
 
-    ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
+    ASSERT_FALSE(stack_map.HasInlineInfo());
   }
 }
 
@@ -440,12 +437,11 @@
   stream.FillInCodeInfo(region);
 
   CodeInfo code_info(region);
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding));
+  ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
 
-  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
+  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries();
   ASSERT_EQ(2u, number_of_catalog_entries);
-  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
   // The Dex register location catalog contains:
   // - one 1-byte short Dex register locations, and
   // - one 5-byte large Dex register location.
@@ -454,17 +450,17 @@
 
   // First stack map.
   {
-    StackMap stack_map = code_info.GetStackMapAt(0, encoding);
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
-    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
-    ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding));
-    ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
-    ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map));
+    StackMap stack_map = code_info.GetStackMapAt(0);
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
+    ASSERT_EQ(0u, stack_map.GetDexPc());
+    ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA));
+    ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map));
 
-    ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1));
+    ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1));
 
-    ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
-    DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers));
+    ASSERT_TRUE(stack_map.HasDexRegisterMap());
+    DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers));
     ASSERT_TRUE(map.IsDexRegisterLive(0));
     ASSERT_TRUE(map.IsDexRegisterLive(1));
     ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -474,15 +470,15 @@
     size_t expected_map_size = 1u + 1u;
     ASSERT_EQ(expected_map_size, map.Size());
 
-    ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info, encoding));
+    ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kConstant,
-              map.GetLocationKind(1, number_of_dex_registers, code_info, encoding));
+              map.GetLocationKind(1, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kInStack,
-              map.GetLocationInternalKind(0, number_of_dex_registers, code_info, encoding));
+              map.GetLocationInternalKind(0, number_of_dex_registers, code_info));
     ASSERT_EQ(Kind::kConstantLargeValue,
-              map.GetLocationInternalKind(1, number_of_dex_registers, code_info, encoding));
-    ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info, encoding));
-    ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info, encoding));
+              map.GetLocationInternalKind(1, number_of_dex_registers, code_info));
+    ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info));
+    ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info));
 
     const size_t index0 =
         map.GetLocationCatalogEntryIndex(0, number_of_dex_registers, number_of_catalog_entries);
@@ -501,10 +497,10 @@
 
     // Test that the inline info dex register map deduplicated to the same offset as the stack map
     // one.
-    ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
-    InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
-    EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, 0),
-              stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding));
+    ASSERT_TRUE(stack_map.HasInlineInfo());
+    InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
+    EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(0),
+              stack_map.GetDexRegisterMapOffset());
   }
 }
 
@@ -527,27 +523,26 @@
   stream.FillInCodeInfo(region);
 
   CodeInfo code_info(region);
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding));
+  ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
 
-  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
+  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries();
   ASSERT_EQ(1u, number_of_catalog_entries);
-  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
   // The Dex register location catalog contains:
   // - one 5-byte large Dex register location.
   size_t expected_location_catalog_size = 5u;
   ASSERT_EQ(expected_location_catalog_size, location_catalog.Size());
 
-  StackMap stack_map = code_info.GetStackMapAt(0, encoding);
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
-  ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding));
-  ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
-  ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map));
+  StackMap stack_map = code_info.GetStackMapAt(0);
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
+  ASSERT_EQ(0u, stack_map.GetDexPc());
+  ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA));
+  ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map));
 
-  ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
+  ASSERT_TRUE(stack_map.HasDexRegisterMap());
   DexRegisterMap dex_register_map =
-      code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
+      code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
   ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0));
   ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
   ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -558,14 +553,14 @@
   ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
 
   ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationKind(
-                0, number_of_dex_registers, code_info, encoding));
+                0, number_of_dex_registers, code_info));
   ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(
-                1, number_of_dex_registers, code_info, encoding));
+                1, number_of_dex_registers, code_info));
   ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationInternalKind(
-                0, number_of_dex_registers, code_info, encoding));
+                0, number_of_dex_registers, code_info));
   ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(
-                1, number_of_dex_registers, code_info, encoding));
-  ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding));
+                1, number_of_dex_registers, code_info));
+  ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info));
 
   size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
       0, number_of_dex_registers, number_of_catalog_entries);
@@ -582,7 +577,7 @@
   ASSERT_EQ(0, location0.GetValue());
   ASSERT_EQ(-2, location1.GetValue());
 
-  ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
+  ASSERT_FALSE(stack_map.HasInlineInfo());
 }
 
 // Generate a stack map whose dex register offset is
@@ -620,11 +615,10 @@
   stream.FillInCodeInfo(region);
 
   CodeInfo code_info(region);
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
   // The location catalog contains two entries (DexRegisterLocation(kConstant, 0)
   // and DexRegisterLocation(kConstant, 1)), therefore the location catalog index
   // has a size of 1 bit.
-  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
+  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries();
   ASSERT_EQ(2u, number_of_catalog_entries);
   ASSERT_EQ(1u, DexRegisterMap::SingleEntrySizeInBits(number_of_catalog_entries));
 
@@ -635,21 +629,21 @@
   //   locations (that is, 127 bytes of data).
   // Hence it has a size of 255 bytes, and therefore...
   ASSERT_EQ(128u, DexRegisterMap::GetLiveBitMaskSize(number_of_dex_registers));
-  StackMap stack_map0 = code_info.GetStackMapAt(0, encoding);
+  StackMap stack_map0 = code_info.GetStackMapAt(0);
   DexRegisterMap dex_register_map0 =
-      code_info.GetDexRegisterMapOf(stack_map0, encoding, number_of_dex_registers);
+      code_info.GetDexRegisterMapOf(stack_map0, number_of_dex_registers);
   ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_dex_registers,
                                                                number_of_catalog_entries));
   ASSERT_EQ(255u, dex_register_map0.Size());
 
-  StackMap stack_map1 = code_info.GetStackMapAt(1, encoding);
-  ASSERT_TRUE(stack_map1.HasDexRegisterMap(encoding.stack_map.encoding));
+  StackMap stack_map1 = code_info.GetStackMapAt(1);
+  ASSERT_TRUE(stack_map1.HasDexRegisterMap());
   // ...the offset of the second Dex register map (relative to the
   // beginning of the Dex register maps region) is 255 (i.e.,
   // kNoDexRegisterMapSmallEncoding).
-  ASSERT_NE(stack_map1.GetDexRegisterMapOffset(encoding.stack_map.encoding),
-            StackMap::kNoDexRegisterMap);
-  ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(encoding.stack_map.encoding), 0xFFu);
+  ASSERT_NE(stack_map1.GetDexRegisterMapOffset(),
+            StackMap::kNoValue);
+  ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(), 0xFFu);
 }
 
 TEST(StackMapTest, TestShareDexRegisterMap) {
@@ -682,33 +676,32 @@
   stream.FillInCodeInfo(region);
 
   CodeInfo ci(region);
-  CodeInfoEncoding encoding = ci.ExtractEncoding();
 
   // Verify first stack map.
-  StackMap sm0 = ci.GetStackMapAt(0, encoding);
-  DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, number_of_dex_registers);
-  ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci, encoding));
-  ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci, encoding));
+  StackMap sm0 = ci.GetStackMapAt(0);
+  DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers);
+  ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci));
+  ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci));
 
   // Verify second stack map.
-  StackMap sm1 = ci.GetStackMapAt(1, encoding);
-  DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, encoding, number_of_dex_registers);
-  ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci, encoding));
-  ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci, encoding));
+  StackMap sm1 = ci.GetStackMapAt(1);
+  DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers);
+  ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci));
+  ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci));
 
   // Verify third stack map.
-  StackMap sm2 = ci.GetStackMapAt(2, encoding);
-  DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, encoding, number_of_dex_registers);
-  ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci, encoding));
-  ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci, encoding));
+  StackMap sm2 = ci.GetStackMapAt(2);
+  DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers);
+  ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci));
+  ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci));
 
   // Verify dex register map offsets.
-  ASSERT_EQ(sm0.GetDexRegisterMapOffset(encoding.stack_map.encoding),
-            sm1.GetDexRegisterMapOffset(encoding.stack_map.encoding));
-  ASSERT_NE(sm0.GetDexRegisterMapOffset(encoding.stack_map.encoding),
-            sm2.GetDexRegisterMapOffset(encoding.stack_map.encoding));
-  ASSERT_NE(sm1.GetDexRegisterMapOffset(encoding.stack_map.encoding),
-            sm2.GetDexRegisterMapOffset(encoding.stack_map.encoding));
+  ASSERT_EQ(sm0.GetDexRegisterMapOffset(),
+            sm1.GetDexRegisterMapOffset());
+  ASSERT_NE(sm0.GetDexRegisterMapOffset(),
+            sm2.GetDexRegisterMapOffset());
+  ASSERT_NE(sm1.GetDexRegisterMapOffset(),
+            sm2.GetDexRegisterMapOffset());
 }
 
 TEST(StackMapTest, TestNoDexRegisterMap) {
@@ -732,33 +725,32 @@
   stream.FillInCodeInfo(region);
 
   CodeInfo code_info(region);
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  ASSERT_EQ(2u, code_info.GetNumberOfStackMaps(encoding));
+  ASSERT_EQ(2u, code_info.GetNumberOfStackMaps());
 
-  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
+  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries();
   ASSERT_EQ(0u, number_of_catalog_entries);
-  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
   ASSERT_EQ(0u, location_catalog.Size());
 
-  StackMap stack_map = code_info.GetStackMapAt(0, encoding);
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
-  ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding));
-  ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
-  ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map));
+  StackMap stack_map = code_info.GetStackMapAt(0);
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
+  ASSERT_EQ(0u, stack_map.GetDexPc());
+  ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA));
+  ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map));
 
-  ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
-  ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
+  ASSERT_FALSE(stack_map.HasDexRegisterMap());
+  ASSERT_FALSE(stack_map.HasInlineInfo());
 
-  stack_map = code_info.GetStackMapAt(1, encoding);
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1, encoding)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68, encoding)));
-  ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map.encoding));
-  ASSERT_EQ(68u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
-  ASSERT_EQ(0x4u, code_info.GetRegisterMaskOf(encoding, stack_map));
+  stack_map = code_info.GetStackMapAt(1);
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68)));
+  ASSERT_EQ(1u, stack_map.GetDexPc());
+  ASSERT_EQ(68u, stack_map.GetNativePcOffset(kRuntimeISA));
+  ASSERT_EQ(0x4u, code_info.GetRegisterMaskOf(stack_map));
 
-  ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
-  ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
+  ASSERT_FALSE(stack_map.HasDexRegisterMap());
+  ASSERT_FALSE(stack_map.HasInlineInfo());
 }
 
 TEST(StackMapTest, InlineTest) {
@@ -835,100 +827,99 @@
   stream.FillInCodeInfo(region);
 
   CodeInfo ci(region);
-  CodeInfoEncoding encoding = ci.ExtractEncoding();
 
   {
     // Verify first stack map.
-    StackMap sm0 = ci.GetStackMapAt(0, encoding);
+    StackMap sm0 = ci.GetStackMapAt(0);
 
-    DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, 2);
-    ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding));
-    ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding));
+    DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, 2);
+    ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci));
+    ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci));
 
-    InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding);
-    ASSERT_EQ(2u, if0.GetDepth(encoding.inline_info.encoding));
-    ASSERT_EQ(2u, if0.GetDexPcAtDepth(encoding.inline_info.encoding, 0));
-    ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0));
-    ASSERT_EQ(3u, if0.GetDexPcAtDepth(encoding.inline_info.encoding, 1));
-    ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1));
+    InlineInfo if0 = ci.GetInlineInfoOf(sm0);
+    ASSERT_EQ(2u, if0.GetDepth());
+    ASSERT_EQ(2u, if0.GetDexPcAtDepth(0));
+    ASSERT_TRUE(if0.EncodesArtMethodAtDepth(0));
+    ASSERT_EQ(3u, if0.GetDexPcAtDepth(1));
+    ASSERT_TRUE(if0.EncodesArtMethodAtDepth(1));
 
-    DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, encoding, 1);
-    ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
+    DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, 1);
+    ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci));
 
-    DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, encoding, 3);
-    ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding));
-    ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci, encoding));
-    ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci, encoding));
+    DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, 3);
+    ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci));
+    ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci));
+    ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci));
   }
 
   {
     // Verify second stack map.
-    StackMap sm1 = ci.GetStackMapAt(1, encoding);
+    StackMap sm1 = ci.GetStackMapAt(1);
 
-    DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, encoding, 2);
-    ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding));
-    ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding));
+    DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, 2);
+    ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci));
+    ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci));
 
-    InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding);
-    ASSERT_EQ(3u, if1.GetDepth(encoding.inline_info.encoding));
-    ASSERT_EQ(2u, if1.GetDexPcAtDepth(encoding.inline_info.encoding, 0));
-    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0));
-    ASSERT_EQ(3u, if1.GetDexPcAtDepth(encoding.inline_info.encoding, 1));
-    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1));
-    ASSERT_EQ(5u, if1.GetDexPcAtDepth(encoding.inline_info.encoding, 2));
-    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 2));
+    InlineInfo if1 = ci.GetInlineInfoOf(sm1);
+    ASSERT_EQ(3u, if1.GetDepth());
+    ASSERT_EQ(2u, if1.GetDexPcAtDepth(0));
+    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(0));
+    ASSERT_EQ(3u, if1.GetDexPcAtDepth(1));
+    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(1));
+    ASSERT_EQ(5u, if1.GetDexPcAtDepth(2));
+    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(2));
 
-    DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, encoding, 1);
-    ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
+    DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, 1);
+    ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci));
 
-    DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, encoding, 3);
-    ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding));
-    ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci, encoding));
-    ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci, encoding));
+    DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, 3);
+    ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci));
+    ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci));
+    ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci));
 
-    ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(encoding.inline_info.encoding, 2));
+    ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(2));
   }
 
   {
     // Verify third stack map.
-    StackMap sm2 = ci.GetStackMapAt(2, encoding);
+    StackMap sm2 = ci.GetStackMapAt(2);
 
-    DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, encoding, 2);
+    DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, 2);
     ASSERT_FALSE(dex_registers0.IsDexRegisterLive(0));
-    ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding));
-    ASSERT_FALSE(sm2.HasInlineInfo(encoding.stack_map.encoding));
+    ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci));
+    ASSERT_FALSE(sm2.HasInlineInfo());
   }
 
   {
     // Verify fourth stack map.
-    StackMap sm3 = ci.GetStackMapAt(3, encoding);
+    StackMap sm3 = ci.GetStackMapAt(3);
 
-    DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, encoding, 2);
-    ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding));
-    ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding));
+    DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, 2);
+    ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci));
+    ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci));
 
-    InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding);
-    ASSERT_EQ(3u, if2.GetDepth(encoding.inline_info.encoding));
-    ASSERT_EQ(2u, if2.GetDexPcAtDepth(encoding.inline_info.encoding, 0));
-    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0));
-    ASSERT_EQ(5u, if2.GetDexPcAtDepth(encoding.inline_info.encoding, 1));
-    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1));
-    ASSERT_EQ(10u, if2.GetDexPcAtDepth(encoding.inline_info.encoding, 2));
-    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 2));
+    InlineInfo if2 = ci.GetInlineInfoOf(sm3);
+    ASSERT_EQ(3u, if2.GetDepth());
+    ASSERT_EQ(2u, if2.GetDexPcAtDepth(0));
+    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(0));
+    ASSERT_EQ(5u, if2.GetDexPcAtDepth(1));
+    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(1));
+    ASSERT_EQ(10u, if2.GetDexPcAtDepth(2));
+    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(2));
 
-    ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(encoding.inline_info.encoding, 0));
+    ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(0));
 
-    DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, encoding, 1);
-    ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci, encoding));
+    DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, 1);
+    ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci));
 
-    DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, encoding, 2);
+    DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, 2);
     ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0));
-    ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci, encoding));
+    ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci));
   }
 }
 
 TEST(StackMapTest, CodeOffsetTest) {
-  // Test minimum alignments, encoding, and decoding.
+  // Test minimum alignments, and decoding.
   CodeOffset offset_thumb2 =
       CodeOffset::FromOffset(kThumb2InstructionAlignment, InstructionSet::kThumb2);
   CodeOffset offset_arm64 =
@@ -969,13 +960,12 @@
   stream.FillInCodeInfo(region);
 
   CodeInfo code_info(region);
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  ASSERT_EQ(2u, code_info.GetNumberOfStackMaps(encoding));
+  ASSERT_EQ(2u, code_info.GetNumberOfStackMaps());
 
-  StackMap stack_map1 = code_info.GetStackMapForNativePcOffset(4, encoding);
-  StackMap stack_map2 = code_info.GetStackMapForNativePcOffset(8, encoding);
-  EXPECT_EQ(stack_map1.GetStackMaskIndex(encoding.stack_map.encoding),
-            stack_map2.GetStackMaskIndex(encoding.stack_map.encoding));
+  StackMap stack_map1 = code_info.GetStackMapForNativePcOffset(4);
+  StackMap stack_map2 = code_info.GetStackMapForNativePcOffset(8);
+  EXPECT_EQ(stack_map1.GetStackMaskIndex(),
+            stack_map2.GetStackMaskIndex());
 }
 
 TEST(StackMapTest, TestInvokeInfo) {
@@ -1007,26 +997,25 @@
 
   CodeInfo code_info(code_info_region);
   MethodInfo method_info(method_info_region.begin());
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  ASSERT_EQ(3u, code_info.GetNumberOfStackMaps(encoding));
+  ASSERT_EQ(3u, code_info.GetNumberOfStackMaps());
 
-  InvokeInfo invoke1(code_info.GetInvokeInfoForNativePcOffset(4, encoding));
-  InvokeInfo invoke2(code_info.GetInvokeInfoForNativePcOffset(8, encoding));
-  InvokeInfo invoke3(code_info.GetInvokeInfoForNativePcOffset(16, encoding));
-  InvokeInfo invoke_invalid(code_info.GetInvokeInfoForNativePcOffset(12, encoding));
+  InvokeInfo invoke1(code_info.GetInvokeInfoForNativePcOffset(4));
+  InvokeInfo invoke2(code_info.GetInvokeInfoForNativePcOffset(8));
+  InvokeInfo invoke3(code_info.GetInvokeInfoForNativePcOffset(16));
+  InvokeInfo invoke_invalid(code_info.GetInvokeInfoForNativePcOffset(12));
   EXPECT_FALSE(invoke_invalid.IsValid());  // No entry for that index.
   EXPECT_TRUE(invoke1.IsValid());
   EXPECT_TRUE(invoke2.IsValid());
   EXPECT_TRUE(invoke3.IsValid());
-  EXPECT_EQ(invoke1.GetInvokeType(encoding.invoke_info.encoding), kSuper);
-  EXPECT_EQ(invoke1.GetMethodIndex(encoding.invoke_info.encoding, method_info), 1u);
-  EXPECT_EQ(invoke1.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 4u);
-  EXPECT_EQ(invoke2.GetInvokeType(encoding.invoke_info.encoding), kStatic);
-  EXPECT_EQ(invoke2.GetMethodIndex(encoding.invoke_info.encoding, method_info), 3u);
-  EXPECT_EQ(invoke2.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 8u);
-  EXPECT_EQ(invoke3.GetInvokeType(encoding.invoke_info.encoding), kDirect);
-  EXPECT_EQ(invoke3.GetMethodIndex(encoding.invoke_info.encoding, method_info), 65535u);
-  EXPECT_EQ(invoke3.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 16u);
+  EXPECT_EQ(invoke1.GetInvokeType(), kSuper);
+  EXPECT_EQ(invoke1.GetMethodIndex(method_info), 1u);
+  EXPECT_EQ(invoke1.GetNativePcOffset(kRuntimeISA), 4u);
+  EXPECT_EQ(invoke2.GetInvokeType(), kStatic);
+  EXPECT_EQ(invoke2.GetMethodIndex(method_info), 3u);
+  EXPECT_EQ(invoke2.GetNativePcOffset(kRuntimeISA), 8u);
+  EXPECT_EQ(invoke3.GetInvokeType(), kDirect);
+  EXPECT_EQ(invoke3.GetMethodIndex(method_info), 65535u);
+  EXPECT_EQ(invoke3.GetNativePcOffset(kRuntimeISA), 16u);
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/x86_memory_gen.cc b/compiler/optimizing/x86_memory_gen.cc
index 0271850..f0069c0 100644
--- a/compiler/optimizing/x86_memory_gen.cc
+++ b/compiler/optimizing/x86_memory_gen.cc
@@ -76,9 +76,10 @@
       do_implicit_null_checks_(codegen->GetCompilerOptions().GetImplicitNullChecks()) {
 }
 
-void X86MemoryOperandGeneration::Run() {
+bool X86MemoryOperandGeneration::Run() {
   MemoryOperandVisitor visitor(graph_, do_implicit_null_checks_);
   visitor.VisitInsertionOrder();
+  return true;
 }
 
 }  // namespace x86
diff --git a/compiler/optimizing/x86_memory_gen.h b/compiler/optimizing/x86_memory_gen.h
index 5f15d9f..b254000 100644
--- a/compiler/optimizing/x86_memory_gen.h
+++ b/compiler/optimizing/x86_memory_gen.h
@@ -31,7 +31,7 @@
                              CodeGenerator* codegen,
                              OptimizingCompilerStats* stats);
 
-  void Run() OVERRIDE;
+  bool Run() OVERRIDE;
 
   static constexpr const char* kX86MemoryOperandGenerationPassName =
           "x86_memory_operand_generation";
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index 57360e7..26aa434 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -18,7 +18,7 @@
 
 #include "base/arena_allocator.h"
 #include "base/malloc_arena_pool.h"
-#include "jni_env_ext.h"
+#include "jni/jni_env_ext.h"
 
 #ifdef ART_ENABLE_CODEGEN_arm
 #include "utils/arm/assembler_arm_vixl.h"
diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h
index 66252be..3e316c8 100644
--- a/compiler/utils/arm/constants_arm.h
+++ b/compiler/utils/arm/constants_arm.h
@@ -25,7 +25,7 @@
 
 #include "arch/arm/registers_arm.h"
 #include "base/casts.h"
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 namespace arm {
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 065c3de..2c428fa 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -37,6 +37,29 @@
 #define ___   asm_.GetVIXLAssembler()->
 #endif
 
+vixl::aarch32::Register AsVIXLRegister(ArmManagedRegister reg) {
+  CHECK(reg.IsCoreRegister());
+  return vixl::aarch32::Register(reg.RegId());
+}
+
+static inline vixl::aarch32::SRegister AsVIXLSRegister(ArmManagedRegister reg) {
+  CHECK(reg.IsSRegister());
+  return vixl::aarch32::SRegister(reg.RegId() - kNumberOfCoreRegIds);
+}
+
+static inline vixl::aarch32::DRegister AsVIXLDRegister(ArmManagedRegister reg) {
+  CHECK(reg.IsDRegister());
+  return vixl::aarch32::DRegister(reg.RegId() - kNumberOfCoreRegIds - kNumberOfSRegIds);
+}
+
+static inline vixl::aarch32::Register AsVIXLRegisterPairLow(ArmManagedRegister reg) {
+  return vixl::aarch32::Register(reg.AsRegisterPairLow());
+}
+
+static inline vixl::aarch32::Register AsVIXLRegisterPairHigh(ArmManagedRegister reg) {
+  return vixl::aarch32::Register(reg.AsRegisterPairHigh());
+}
+
 void ArmVIXLJNIMacroAssembler::FinalizeCode() {
   for (const std::unique_ptr<
       ArmVIXLJNIMacroAssembler::ArmException>& exception : exception_blocks_) {
@@ -60,7 +83,7 @@
                                           ArrayRef<const ManagedRegister> callee_save_regs,
                                           const ManagedRegisterEntrySpills& entry_spills) {
   CHECK_ALIGNED(frame_size, kStackAlignment);
-  CHECK(r0.Is(method_reg.AsArm().AsVIXLRegister()));
+  CHECK(r0.Is(AsVIXLRegister(method_reg.AsArm())));
 
   // Push callee saves and link register.
   RegList core_spill_mask = 1 << LR;
@@ -104,13 +127,13 @@
       ManagedRegisterSpill spill = entry_spills.at(i);
       offset += spill.getSize();
     } else if (reg.IsCoreRegister()) {
-      asm_.StoreToOffset(kStoreWord, reg.AsVIXLRegister(), sp, offset);
+      asm_.StoreToOffset(kStoreWord, AsVIXLRegister(reg), sp, offset);
       offset += 4;
     } else if (reg.IsSRegister()) {
-      asm_.StoreSToOffset(reg.AsVIXLSRegister(), sp, offset);
+      asm_.StoreSToOffset(AsVIXLSRegister(reg), sp, offset);
       offset += 4;
     } else if (reg.IsDRegister()) {
-      asm_.StoreDToOffset(reg.AsVIXLDRegister(), sp, offset);
+      asm_.StoreDToOffset(AsVIXLDRegister(reg), sp, offset);
       offset += 8;
     }
   }
@@ -208,76 +231,71 @@
   } else if (src.IsCoreRegister()) {
     CHECK_EQ(4u, size);
     UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-    temps.Exclude(src.AsVIXLRegister());
-    asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
+    temps.Exclude(AsVIXLRegister(src));
+    asm_.StoreToOffset(kStoreWord, AsVIXLRegister(src), sp, dest.Int32Value());
   } else if (src.IsRegisterPair()) {
     CHECK_EQ(8u, size);
-    asm_.StoreToOffset(kStoreWord, src.AsVIXLRegisterPairLow(),  sp, dest.Int32Value());
-    asm_.StoreToOffset(kStoreWord, src.AsVIXLRegisterPairHigh(), sp, dest.Int32Value() + 4);
+    asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairLow(src),  sp, dest.Int32Value());
+    asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairHigh(src), sp, dest.Int32Value() + 4);
   } else if (src.IsSRegister()) {
     CHECK_EQ(4u, size);
-    asm_.StoreSToOffset(src.AsVIXLSRegister(), sp, dest.Int32Value());
+    asm_.StoreSToOffset(AsVIXLSRegister(src), sp, dest.Int32Value());
   } else {
     CHECK_EQ(8u, size);
     CHECK(src.IsDRegister()) << src;
-    asm_.StoreDToOffset(src.AsVIXLDRegister(), sp, dest.Int32Value());
+    asm_.StoreDToOffset(AsVIXLDRegister(src), sp, dest.Int32Value());
   }
 }
 
 void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
-  ArmManagedRegister src = msrc.AsArm();
-  CHECK(src.IsCoreRegister()) << src;
+  vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(src.AsVIXLRegister());
-  asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
+  temps.Exclude(src);
+  asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value());
 }
 
 void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
-  ArmManagedRegister src = msrc.AsArm();
-  CHECK(src.IsCoreRegister()) << src;
+  vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(src.AsVIXLRegister());
-  asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
+  temps.Exclude(src);
+  asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value());
 }
 
 void ArmVIXLJNIMacroAssembler::StoreSpanning(FrameOffset dest,
                                              ManagedRegister msrc,
                                              FrameOffset in_off,
                                              ManagedRegister mscratch) {
-  ArmManagedRegister src = msrc.AsArm();
-  ArmManagedRegister scratch = mscratch.AsArm();
-  asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
+  vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm());
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
+  asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(scratch.AsVIXLRegister());
-  asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, in_off.Int32Value());
-  asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value() + 4);
+  temps.Exclude(scratch);
+  asm_.LoadFromOffset(kLoadWord, scratch, sp, in_off.Int32Value());
+  asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4);
 }
 
 void ArmVIXLJNIMacroAssembler::CopyRef(FrameOffset dest,
                                        FrameOffset src,
                                        ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(scratch.AsVIXLRegister());
-  asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, src.Int32Value());
-  asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value());
+  temps.Exclude(scratch);
+  asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value());
+  asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
 }
 
-void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister dest,
-                                       ManagedRegister base,
+void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister mdest,
+                                       ManagedRegister mbase,
                                        MemberOffset offs,
                                        bool unpoison_reference) {
-  ArmManagedRegister dst = dest.AsArm();
-  CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
+  vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm());
+  vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(dst.AsVIXLRegister(), base.AsArm().AsVIXLRegister());
-  asm_.LoadFromOffset(kLoadWord,
-                      dst.AsVIXLRegister(),
-                      base.AsArm().AsVIXLRegister(),
-                      offs.Int32Value());
+  temps.Exclude(dest, base);
+  asm_.LoadFromOffset(kLoadWord, dest, base, offs.Int32Value());
 
   if (unpoison_reference) {
-    asm_.MaybeUnpoisonHeapReference(dst.AsVIXLRegister());
+    asm_.MaybeUnpoisonHeapReference(dest);
   }
 }
 
@@ -294,13 +312,12 @@
 
 void ArmVIXLJNIMacroAssembler::StoreImmediateToFrame(FrameOffset dest,
                                                      uint32_t imm,
-                                                     ManagedRegister scratch) {
-  ArmManagedRegister mscratch = scratch.AsArm();
-  CHECK(mscratch.IsCoreRegister()) << mscratch;
+                                                     ManagedRegister mscratch) {
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(mscratch.AsVIXLRegister());
-  asm_.LoadImmediate(mscratch.AsVIXLRegister(), imm);
-  asm_.StoreToOffset(kStoreWord, mscratch.AsVIXLRegister(), sp, dest.Int32Value());
+  temps.Exclude(scratch);
+  asm_.LoadImmediate(scratch, imm);
+  asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
 }
 
 void ArmVIXLJNIMacroAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) {
@@ -313,23 +330,21 @@
   return Load(m_dst.AsArm(), tr, src.Int32Value(), size);
 }
 
-void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) {
-  ArmManagedRegister dst = m_dst.AsArm();
-  CHECK(dst.IsCoreRegister()) << dst;
+void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) {
+  vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(dst.AsVIXLRegister());
-  asm_.LoadFromOffset(kLoadWord, dst.AsVIXLRegister(), tr, offs.Int32Value());
+  temps.Exclude(dest);
+  asm_.LoadFromOffset(kLoadWord, dest, tr, offs.Int32Value());
 }
 
 void ArmVIXLJNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
                                                     ThreadOffset32 thr_offs,
                                                     ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(scratch.AsVIXLRegister());
-  asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value());
-  asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, fr_offs.Int32Value());
+  temps.Exclude(scratch);
+  asm_.LoadFromOffset(kLoadWord, scratch, tr, thr_offs.Int32Value());
+  asm_.StoreToOffset(kStoreWord, scratch, sp, fr_offs.Int32Value());
 }
 
 void ArmVIXLJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIBUTE_UNUSED,
@@ -341,12 +356,11 @@
 void ArmVIXLJNIMacroAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
                                                         FrameOffset fr_offs,
                                                         ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(scratch.AsVIXLRegister());
-  asm_.AddConstant(scratch.AsVIXLRegister(), sp, fr_offs.Int32Value());
-  asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value());
+  temps.Exclude(scratch);
+  asm_.AddConstant(scratch, sp, fr_offs.Int32Value());
+  asm_.StoreToOffset(kStoreWord, scratch, tr, thr_offs.Int32Value());
 }
 
 void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
@@ -363,43 +377,43 @@
   UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm";
 }
 
-void ArmVIXLJNIMacroAssembler::Move(ManagedRegister m_dst,
-                                    ManagedRegister m_src,
+void ArmVIXLJNIMacroAssembler::Move(ManagedRegister mdst,
+                                    ManagedRegister msrc,
                                     size_t size  ATTRIBUTE_UNUSED) {
-  ArmManagedRegister dst = m_dst.AsArm();
-  ArmManagedRegister src = m_src.AsArm();
+  ArmManagedRegister dst = mdst.AsArm();
+  ArmManagedRegister src = msrc.AsArm();
   if (!dst.Equals(src)) {
     if (dst.IsCoreRegister()) {
       CHECK(src.IsCoreRegister()) << src;
       UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-      temps.Exclude(dst.AsVIXLRegister());
-      ___ Mov(dst.AsVIXLRegister(), src.AsVIXLRegister());
+      temps.Exclude(AsVIXLRegister(dst));
+      ___ Mov(AsVIXLRegister(dst), AsVIXLRegister(src));
     } else if (dst.IsDRegister()) {
       if (src.IsDRegister()) {
-        ___ Vmov(F64, dst.AsVIXLDRegister(), src.AsVIXLDRegister());
+        ___ Vmov(F64, AsVIXLDRegister(dst), AsVIXLDRegister(src));
       } else {
         // VMOV Dn, Rlo, Rhi (Dn = {Rlo, Rhi})
         CHECK(src.IsRegisterPair()) << src;
-        ___ Vmov(dst.AsVIXLDRegister(), src.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairHigh());
+        ___ Vmov(AsVIXLDRegister(dst), AsVIXLRegisterPairLow(src), AsVIXLRegisterPairHigh(src));
       }
     } else if (dst.IsSRegister()) {
       if (src.IsSRegister()) {
-        ___ Vmov(F32, dst.AsVIXLSRegister(), src.AsVIXLSRegister());
+        ___ Vmov(F32, AsVIXLSRegister(dst), AsVIXLSRegister(src));
       } else {
         // VMOV Sn, Rn  (Sn = Rn)
         CHECK(src.IsCoreRegister()) << src;
-        ___ Vmov(dst.AsVIXLSRegister(), src.AsVIXLRegister());
+        ___ Vmov(AsVIXLSRegister(dst), AsVIXLRegister(src));
       }
     } else {
       CHECK(dst.IsRegisterPair()) << dst;
       CHECK(src.IsRegisterPair()) << src;
       // Ensure that the first move doesn't clobber the input of the second.
       if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) {
-        ___ Mov(dst.AsVIXLRegisterPairLow(),  src.AsVIXLRegisterPairLow());
-        ___ Mov(dst.AsVIXLRegisterPairHigh(), src.AsVIXLRegisterPairHigh());
+        ___ Mov(AsVIXLRegisterPairLow(dst),  AsVIXLRegisterPairLow(src));
+        ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src));
       } else {
-        ___ Mov(dst.AsVIXLRegisterPairHigh(), src.AsVIXLRegisterPairHigh());
-        ___ Mov(dst.AsVIXLRegisterPairLow(),  src.AsVIXLRegisterPairLow());
+        ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src));
+        ___ Mov(AsVIXLRegisterPairLow(dst),  AsVIXLRegisterPairLow(src));
       }
     }
   }
@@ -407,21 +421,20 @@
 
 void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest,
                                     FrameOffset src,
-                                    ManagedRegister scratch,
+                                    ManagedRegister mscratch,
                                     size_t size) {
-  ArmManagedRegister temp = scratch.AsArm();
-  CHECK(temp.IsCoreRegister()) << temp;
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
   CHECK(size == 4 || size == 8) << size;
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(temp.AsVIXLRegister());
+  temps.Exclude(scratch);
   if (size == 4) {
-    asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value());
-    asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value());
+    asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value());
+    asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
   } else if (size == 8) {
-    asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value());
-    asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value());
-    asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value() + 4);
-    asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value() + 4);
+    asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value());
+    asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
+    asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value() + 4);
+    asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4);
   }
 }
 
@@ -471,48 +484,44 @@
                                                       FrameOffset handle_scope_offset,
                                                       ManagedRegister min_reg,
                                                       bool null_allowed) {
-  ArmManagedRegister out_reg = mout_reg.AsArm();
-  ArmManagedRegister in_reg = min_reg.AsArm();
-  CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
-  CHECK(out_reg.IsCoreRegister()) << out_reg;
+  vixl::aarch32::Register out_reg = AsVIXLRegister(mout_reg.AsArm());
+  vixl::aarch32::Register in_reg =
+      min_reg.AsArm().IsNoRegister() ? vixl::aarch32::Register() : AsVIXLRegister(min_reg.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(out_reg.AsVIXLRegister());
+  temps.Exclude(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()) {
-      asm_.LoadFromOffset(kLoadWord,
-                          out_reg.AsVIXLRegister(),
-                          sp,
-                          handle_scope_offset.Int32Value());
+    if (!in_reg.IsValid()) {
+      asm_.LoadFromOffset(kLoadWord, out_reg, sp, handle_scope_offset.Int32Value());
       in_reg = out_reg;
     }
 
-    temps.Exclude(in_reg.AsVIXLRegister());
-    ___ Cmp(in_reg.AsVIXLRegister(), 0);
+    temps.Exclude(in_reg);
+    ___ Cmp(in_reg, 0);
 
     if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) {
-      if (!out_reg.Equals(in_reg)) {
+      if (!out_reg.Is(in_reg)) {
         ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
                                  3 * vixl32::kMaxInstructionSizeInBytes,
                                  CodeBufferCheckScope::kMaximumSize);
         ___ it(eq, 0xc);
-        ___ mov(eq, out_reg.AsVIXLRegister(), 0);
-        asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
+        ___ mov(eq, out_reg, 0);
+        asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne);
       } else {
         ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
                                  2 * vixl32::kMaxInstructionSizeInBytes,
                                  CodeBufferCheckScope::kMaximumSize);
         ___ it(ne, 0x8);
-        asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
+        asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne);
       }
     } else {
       // TODO: Implement this (old arm assembler would have crashed here).
       UNIMPLEMENTED(FATAL);
     }
   } else {
-    asm_.AddConstant(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value());
+    asm_.AddConstant(out_reg, sp, handle_scope_offset.Int32Value());
   }
 }
 
@@ -520,31 +529,30 @@
                                                       FrameOffset handle_scope_offset,
                                                       ManagedRegister mscratch,
                                                       bool null_allowed) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(scratch.AsVIXLRegister());
+  temps.Exclude(scratch);
   if (null_allowed) {
-    asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value());
+    asm_.LoadFromOffset(kLoadWord, scratch, 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(scratch.AsVIXLRegister(), 0);
+    ___ Cmp(scratch, 0);
 
     if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) {
       ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
                                2 * vixl32::kMaxInstructionSizeInBytes,
                                CodeBufferCheckScope::kMaximumSize);
       ___ it(ne, 0x8);
-      asm_.AddConstantInIt(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
+      asm_.AddConstantInIt(scratch, sp, handle_scope_offset.Int32Value(), ne);
     } else {
       // TODO: Implement this (old arm assembler would have crashed here).
       UNIMPLEMENTED(FATAL);
     }
   } else {
-    asm_.AddConstant(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value());
+    asm_.AddConstant(scratch, sp, handle_scope_offset.Int32Value());
   }
-  asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, out_off.Int32Value());
+  asm_.StoreToOffset(kStoreWord, scratch, sp, out_off.Int32Value());
 }
 
 void ArmVIXLJNIMacroAssembler::LoadReferenceFromHandleScope(
@@ -566,32 +574,23 @@
 void ArmVIXLJNIMacroAssembler::Call(ManagedRegister mbase,
                                     Offset offset,
                                     ManagedRegister mscratch) {
-  ArmManagedRegister base = mbase.AsArm();
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(base.IsCoreRegister()) << base;
-  CHECK(scratch.IsCoreRegister()) << scratch;
+  vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm());
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(scratch.AsVIXLRegister());
-  asm_.LoadFromOffset(kLoadWord,
-                      scratch.AsVIXLRegister(),
-                      base.AsVIXLRegister(),
-                      offset.Int32Value());
-  ___ Blx(scratch.AsVIXLRegister());
+  temps.Exclude(scratch);
+  asm_.LoadFromOffset(kLoadWord, scratch, base, offset.Int32Value());
+  ___ Blx(scratch);
   // TODO: place reference map on call.
 }
 
 void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(scratch.AsVIXLRegister());
+  temps.Exclude(scratch);
   // Call *(*(SP + base) + offset)
-  asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, base.Int32Value());
-  asm_.LoadFromOffset(kLoadWord,
-                      scratch.AsVIXLRegister(),
-                      scratch.AsVIXLRegister(),
-                      offset.Int32Value());
-  ___ Blx(scratch.AsVIXLRegister());
+  asm_.LoadFromOffset(kLoadWord, scratch, sp, base.Int32Value());
+  asm_.LoadFromOffset(kLoadWord, scratch, scratch, offset.Int32Value());
+  ___ Blx(scratch);
   // TODO: place reference map on call
 }
 
@@ -602,8 +601,8 @@
 
 void ArmVIXLJNIMacroAssembler::GetCurrentThread(ManagedRegister mtr) {
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(mtr.AsArm().AsVIXLRegister());
-  ___ Mov(mtr.AsArm().AsVIXLRegister(), tr);
+  temps.Exclude(AsVIXLRegister(mtr.AsArm()));
+  ___ Mov(AsVIXLRegister(mtr.AsArm()), tr);
 }
 
 void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset,
@@ -611,19 +610,19 @@
   asm_.StoreToOffset(kStoreWord, tr, sp, dest_offset.Int32Value());
 }
 
-void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) {
+void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
   CHECK_ALIGNED(stack_adjust, kStackAlignment);
-  ArmManagedRegister scratch = m_scratch.AsArm();
+  vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(scratch.AsVIXLRegister());
+  temps.Exclude(scratch);
   exception_blocks_.emplace_back(
-      new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust));
+      new ArmVIXLJNIMacroAssembler::ArmException(mscratch.AsArm(), stack_adjust));
   asm_.LoadFromOffset(kLoadWord,
-                      scratch.AsVIXLRegister(),
+                      scratch,
                       tr,
                       Thread::ExceptionOffset<kArmPointerSize>().Int32Value());
 
-  ___ Cmp(scratch.AsVIXLRegister(), 0);
+  ___ Cmp(scratch, 0);
   vixl32::Label* label = exception_blocks_.back()->Entry();
   ___ BPreferNear(ne, label);
   // TODO: think about using CBNZ here.
@@ -640,19 +639,18 @@
 
 void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label,
                                     JNIMacroUnaryCondition condition,
-                                    ManagedRegister test) {
+                                    ManagedRegister mtest) {
   CHECK(label != nullptr);
 
+  vixl::aarch32::Register test = AsVIXLRegister(mtest.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(test.AsArm().AsVIXLRegister());
+  temps.Exclude(test);
   switch (condition) {
     case JNIMacroUnaryCondition::kZero:
-      ___ CompareAndBranchIfZero(test.AsArm().AsVIXLRegister(),
-                                 ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+      ___ CompareAndBranchIfZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
       break;
     case JNIMacroUnaryCondition::kNotZero:
-      ___ CompareAndBranchIfNonZero(test.AsArm().AsVIXLRegister(),
-                                    ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+      ___ CompareAndBranchIfNonZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
       break;
     default:
       LOG(FATAL) << "Not implemented unary condition: " << static_cast<int>(condition);
@@ -672,12 +670,13 @@
     DecreaseFrameSize(exception->stack_adjust_);
   }
 
+  vixl::aarch32::Register scratch = AsVIXLRegister(exception->scratch_);
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(exception->scratch_.AsVIXLRegister());
+  temps.Exclude(scratch);
   // Pass exception object as argument.
   // Don't care about preserving r0 as this won't return.
-  ___ Mov(r0, exception->scratch_.AsVIXLRegister());
-  temps.Include(exception->scratch_.AsVIXLRegister());
+  ___ Mov(r0, scratch);
+  temps.Include(scratch);
   // TODO: check that exception->scratch_ is dead by this point.
   vixl32::Register temp = temps.Acquire();
   ___ Ldr(temp,
@@ -698,26 +697,27 @@
   if (dest.IsNoRegister()) {
     CHECK_EQ(0u, size) << dest;
   } else if (dest.IsCoreRegister()) {
-    CHECK(!dest.AsVIXLRegister().Is(sp)) << dest;
+    vixl::aarch32::Register dst = AsVIXLRegister(dest);
+    CHECK(!dst.Is(sp)) << dest;
 
     UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-    temps.Exclude(dest.AsVIXLRegister());
+    temps.Exclude(dst);
 
     if (size == 1u) {
-      ___ Ldrb(dest.AsVIXLRegister(), MemOperand(base, offset));
+      ___ Ldrb(dst, MemOperand(base, offset));
     } else {
       CHECK_EQ(4u, size) << dest;
-      ___ Ldr(dest.AsVIXLRegister(), MemOperand(base, offset));
+      ___ Ldr(dst, MemOperand(base, offset));
     }
   } else if (dest.IsRegisterPair()) {
     CHECK_EQ(8u, size) << dest;
-    ___ Ldr(dest.AsVIXLRegisterPairLow(),  MemOperand(base, offset));
-    ___ Ldr(dest.AsVIXLRegisterPairHigh(), MemOperand(base, offset + 4));
+    ___ Ldr(AsVIXLRegisterPairLow(dest),  MemOperand(base, offset));
+    ___ Ldr(AsVIXLRegisterPairHigh(dest), MemOperand(base, offset + 4));
   } else if (dest.IsSRegister()) {
-    ___ Vldr(dest.AsVIXLSRegister(), MemOperand(base, offset));
+    ___ Vldr(AsVIXLSRegister(dest), MemOperand(base, offset));
   } else {
     CHECK(dest.IsDRegister()) << dest;
-    ___ Vldr(dest.AsVIXLDRegister(), MemOperand(base, offset));
+    ___ Vldr(AsVIXLDRegister(dest), MemOperand(base, offset));
   }
 }
 
diff --git a/compiler/utils/arm/managed_register_arm.cc b/compiler/utils/arm/managed_register_arm.cc
index 1fdc110..deff658 100644
--- a/compiler/utils/arm/managed_register_arm.cc
+++ b/compiler/utils/arm/managed_register_arm.cc
@@ -16,7 +16,7 @@
 
 #include "managed_register_arm.h"
 
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 namespace arm {
diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h
index 26f23b2..e42572d 100644
--- a/compiler/utils/arm/managed_register_arm.h
+++ b/compiler/utils/arm/managed_register_arm.h
@@ -20,15 +20,8 @@
 #include <android-base/logging.h>
 
 #include "constants_arm.h"
-#include "debug/dwarf/register.h"
 #include "utils/managed_register.h"
 
-// TODO(VIXL): Make VIXL compile with -Wshadow.
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wshadow"
-#include "aarch32/macro-assembler-aarch32.h"
-#pragma GCC diagnostic pop
-
 namespace art {
 namespace arm {
 
@@ -97,31 +90,16 @@
     return static_cast<Register>(id_);
   }
 
-  vixl::aarch32::Register AsVIXLRegister() const {
-    CHECK(IsCoreRegister());
-    return vixl::aarch32::Register(id_);
-  }
-
   constexpr SRegister AsSRegister() const {
     CHECK(IsSRegister());
     return static_cast<SRegister>(id_ - kNumberOfCoreRegIds);
   }
 
-  vixl::aarch32::SRegister AsVIXLSRegister() const {
-    CHECK(IsSRegister());
-    return vixl::aarch32::SRegister(id_ - kNumberOfCoreRegIds);
-  }
-
   constexpr DRegister AsDRegister() const {
     CHECK(IsDRegister());
     return static_cast<DRegister>(id_ - kNumberOfCoreRegIds - kNumberOfSRegIds);
   }
 
-  vixl::aarch32::DRegister AsVIXLDRegister() const {
-    CHECK(IsDRegister());
-    return vixl::aarch32::DRegister(id_ - kNumberOfCoreRegIds - kNumberOfSRegIds);
-  }
-
   constexpr SRegister AsOverlappingDRegisterLow() const {
     CHECK(IsOverlappingDRegister());
     DRegister d_reg = AsDRegister();
@@ -150,20 +128,12 @@
     return FromRegId(AllocIdLow()).AsCoreRegister();
   }
 
-  vixl::aarch32::Register AsVIXLRegisterPairLow() const {
-    return vixl::aarch32::Register(AsRegisterPairLow());
-  }
-
   constexpr Register AsRegisterPairHigh() const {
     CHECK(IsRegisterPair());
     // Appropriate mapping of register ids allows to use AllocIdHigh().
     return FromRegId(AllocIdHigh()).AsCoreRegister();
   }
 
-  vixl::aarch32::Register AsVIXLRegisterPairHigh() const {
-    return vixl::aarch32::Register(AsRegisterPairHigh());
-  }
-
   constexpr bool IsCoreRegister() const {
     CHECK(IsValidManagedRegister());
     return (0 <= id_) && (id_ < kNumberOfCoreRegIds);
@@ -255,16 +225,16 @@
     return FromDRegister(static_cast<DRegister>(r));
   }
 
- private:
-  constexpr bool IsValidManagedRegister() const {
-    return (0 <= id_) && (id_ < kNumberOfRegIds);
-  }
-
   int RegId() const {
     CHECK(!IsNoRegister());
     return id_;
   }
 
+ private:
+  constexpr bool IsValidManagedRegister() const {
+    return (0 <= id_) && (id_ < kNumberOfRegIds);
+  }
+
   int AllocId() const {
     CHECK(IsValidManagedRegister() &&
            !IsOverlappingDRegister() && !IsRegisterPair());
diff --git a/compiler/utils/arm/managed_register_arm_test.cc b/compiler/utils/arm/managed_register_arm_test.cc
index 43b0b51..6f440a7 100644
--- a/compiler/utils/arm/managed_register_arm_test.cc
+++ b/compiler/utils/arm/managed_register_arm_test.cc
@@ -15,7 +15,7 @@
  */
 
 #include "managed_register_arm.h"
-#include "globals.h"
+#include "base/globals.h"
 #include "gtest/gtest.h"
 
 namespace art {
diff --git a/compiler/utils/arm64/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc
index 47924bf..5632265 100644
--- a/compiler/utils/arm64/managed_register_arm64.cc
+++ b/compiler/utils/arm64/managed_register_arm64.cc
@@ -15,7 +15,7 @@
  */
 
 #include "managed_register_arm64.h"
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 namespace arm64 {
diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h
index 9ce7ec9..0513890 100644
--- a/compiler/utils/arm64/managed_register_arm64.h
+++ b/compiler/utils/arm64/managed_register_arm64.h
@@ -20,7 +20,6 @@
 #include <android-base/logging.h>
 
 #include "arch/arm64/registers_arm64.h"
-#include "debug/dwarf/register.h"
 #include "utils/managed_register.h"
 
 namespace art {
diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc
index 2a79313..d151ac9 100644
--- a/compiler/utils/arm64/managed_register_arm64_test.cc
+++ b/compiler/utils/arm64/managed_register_arm64_test.cc
@@ -17,7 +17,7 @@
 #include "managed_register_arm64.h"
 
 #include "assembler_arm64.h"
-#include "globals.h"
+#include "base/globals.h"
 #include "gtest/gtest.h"
 
 namespace art {
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index 421c1b6..d1d2a3d 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -20,8 +20,8 @@
 #include <vector>
 
 #include "base/casts.h"
+#include "base/globals.h"
 #include "base/memory_region.h"
-#include "globals.h"
 
 namespace art {
 
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 674dc9a..19c405e 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -153,7 +153,7 @@
   " 21c:	f8d9 8034 	ldr.w	r8, [r9, #52]	; 0x34\n",
   " 220:	4770      	bx	lr\n",
   " 222:	4660      	mov	r0, ip\n",
-  " 224:	f8d9 c2c4 	ldr.w	ip, [r9, #708]	; 0x2c4\n",
+  " 224:	f8d9 c2cc 	ldr.w	ip, [r9, #716]	; 0x2cc\n",
   " 228:	47e0      	blx	ip\n",
   nullptr
 };
diff --git a/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc
index 0c34aa4..5f405f3 100644
--- a/compiler/utils/jni_macro_assembler.cc
+++ b/compiler/utils/jni_macro_assembler.cc
@@ -38,8 +38,8 @@
 #include "x86_64/jni_macro_assembler_x86_64.h"
 #endif
 #include "base/casts.h"
+#include "base/globals.h"
 #include "base/memory_region.h"
-#include "globals.h"
 
 namespace art {
 
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index c6ce62b..af3d7a0 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -24,10 +24,10 @@
 #include "arch/mips/instruction_set_features_mips.h"
 #include "base/arena_containers.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/stl_util_identity.h"
 #include "constants_mips.h"
-#include "globals.h"
 #include "heap_poisoning.h"
 #include "managed_register_mips.h"
 #include "offsets.h"
diff --git a/compiler/utils/mips/assembler_mips32r5_test.cc b/compiler/utils/mips/assembler_mips32r5_test.cc
index 9a69ffd..0f85892 100644
--- a/compiler/utils/mips/assembler_mips32r5_test.cc
+++ b/compiler/utils/mips/assembler_mips32r5_test.cc
@@ -45,6 +45,16 @@
                         uint32_t,
                         mips::VectorRegister> Base;
 
+  // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<>
+  // and reimplement it without the verification against `assembly_string`. b/73903608
+  void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED,
+                 const std::string& test_name ATTRIBUTE_UNUSED) {
+    GetAssembler()->FinalizeCode();
+    std::vector<uint8_t> data(GetAssembler()->CodeSize());
+    MemoryRegion code(data.data(), data.size());
+    GetAssembler()->FinalizeInstructions(code);
+  }
+
   AssemblerMIPS32r5Test() :
     instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r5", nullptr)) {
   }
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index 691c33f..3d876ca 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -45,6 +45,16 @@
                         uint32_t,
                         mips::VectorRegister> Base;
 
+  // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<>
+  // and reimplement it without the verification against `assembly_string`. b/73903608
+  void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED,
+                 const std::string& test_name ATTRIBUTE_UNUSED) {
+    GetAssembler()->FinalizeCode();
+    std::vector<uint8_t> data(GetAssembler()->CodeSize());
+    MemoryRegion code(data.data(), data.size());
+    GetAssembler()->FinalizeInstructions(code);
+  }
+
   AssemblerMIPS32r6Test() :
     instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r6", nullptr)) {
   }
diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc
index b027d3a..f94d074 100644
--- a/compiler/utils/mips/assembler_mips_test.cc
+++ b/compiler/utils/mips/assembler_mips_test.cc
@@ -43,6 +43,16 @@
                         mips::FRegister,
                         uint32_t> Base;
 
+  // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<>
+  // and reimplement it without the verification against `assembly_string`. b/73903608
+  void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED,
+                 const std::string& test_name ATTRIBUTE_UNUSED) {
+    GetAssembler()->FinalizeCode();
+    std::vector<uint8_t> data(GetAssembler()->CodeSize());
+    MemoryRegion code(data.data(), data.size());
+    GetAssembler()->FinalizeInstructions(code);
+  }
+
  protected:
   // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
   std::string GetArchitectureString() OVERRIDE {
diff --git a/compiler/utils/mips/constants_mips.h b/compiler/utils/mips/constants_mips.h
index 016c0db..07d8b7d 100644
--- a/compiler/utils/mips/constants_mips.h
+++ b/compiler/utils/mips/constants_mips.h
@@ -22,8 +22,8 @@
 #include <android-base/logging.h>
 
 #include "arch/mips/registers_mips.h"
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 namespace art {
 namespace mips {
diff --git a/compiler/utils/mips/managed_register_mips.cc b/compiler/utils/mips/managed_register_mips.cc
index 5a8c048..9b3ed79 100644
--- a/compiler/utils/mips/managed_register_mips.cc
+++ b/compiler/utils/mips/managed_register_mips.cc
@@ -16,7 +16,7 @@
 
 #include "managed_register_mips.h"
 
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 namespace mips {
diff --git a/compiler/utils/mips/managed_register_mips.h b/compiler/utils/mips/managed_register_mips.h
index 66204e7..18d5821 100644
--- a/compiler/utils/mips/managed_register_mips.h
+++ b/compiler/utils/mips/managed_register_mips.h
@@ -18,7 +18,6 @@
 #define ART_COMPILER_UTILS_MIPS_MANAGED_REGISTER_MIPS_H_
 
 #include "constants_mips.h"
-#include "debug/dwarf/register.h"
 #include "utils/managed_register.h"
 
 namespace art {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index 542dbaf..19f23b7 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -24,10 +24,10 @@
 #include "arch/mips64/instruction_set_features_mips64.h"
 #include "base/arena_containers.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/stl_util_identity.h"
 #include "constants_mips64.h"
-#include "globals.h"
 #include "heap_poisoning.h"
 #include "managed_register_mips64.h"
 #include "offsets.h"
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index fb5f12b..a53ff7c 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -48,6 +48,16 @@
                         uint32_t,
                         mips64::VectorRegister> Base;
 
+  // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<>
+  // and reimplement it without the verification against `assembly_string`. b/73903608
+  void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED,
+                 const std::string& test_name ATTRIBUTE_UNUSED) {
+    GetAssembler()->FinalizeCode();
+    std::vector<uint8_t> data(GetAssembler()->CodeSize());
+    MemoryRegion code(data.data(), data.size());
+    GetAssembler()->FinalizeInstructions(code);
+  }
+
   AssemblerMIPS64Test()
       : instruction_set_features_(Mips64InstructionSetFeatures::FromVariant("default", nullptr)) {}
 
diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h
index 310f23c..41eb77c 100644
--- a/compiler/utils/mips64/constants_mips64.h
+++ b/compiler/utils/mips64/constants_mips64.h
@@ -22,8 +22,8 @@
 #include <android-base/logging.h>
 
 #include "arch/mips64/registers_mips64.h"
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 namespace art {
 namespace mips64 {
diff --git a/compiler/utils/mips64/managed_register_mips64.cc b/compiler/utils/mips64/managed_register_mips64.cc
index 42d061e..01cb6dd 100644
--- a/compiler/utils/mips64/managed_register_mips64.cc
+++ b/compiler/utils/mips64/managed_register_mips64.cc
@@ -16,7 +16,7 @@
 
 #include "managed_register_mips64.h"
 
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 namespace mips64 {
diff --git a/compiler/utils/mips64/managed_register_mips64.h b/compiler/utils/mips64/managed_register_mips64.h
index 3980199..94166d3 100644
--- a/compiler/utils/mips64/managed_register_mips64.h
+++ b/compiler/utils/mips64/managed_register_mips64.h
@@ -18,7 +18,6 @@
 #define ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_
 
 #include "constants_mips64.h"
-#include "debug/dwarf/register.h"
 #include "utils/managed_register.h"
 
 namespace art {
diff --git a/compiler/utils/mips64/managed_register_mips64_test.cc b/compiler/utils/mips64/managed_register_mips64_test.cc
index 8b72d7e..bbfeeee 100644
--- a/compiler/utils/mips64/managed_register_mips64_test.cc
+++ b/compiler/utils/mips64/managed_register_mips64_test.cc
@@ -15,7 +15,8 @@
  */
 
 #include "managed_register_mips64.h"
-#include "globals.h"
+
+#include "base/globals.h"
 #include "gtest/gtest.h"
 
 namespace art {
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 22eaedc..e42c4c9 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -23,9 +23,9 @@
 #include "base/array_ref.h"
 #include "base/bit_utils.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "constants_x86.h"
-#include "globals.h"
 #include "heap_poisoning.h"
 #include "managed_register_x86.h"
 #include "offsets.h"
diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h
index 2e03b9f..a782b16 100644
--- a/compiler/utils/x86/constants_x86.h
+++ b/compiler/utils/x86/constants_x86.h
@@ -22,8 +22,8 @@
 #include <android-base/logging.h>
 
 #include "arch/x86/registers_x86.h"
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 namespace art {
 namespace x86 {
@@ -40,21 +40,6 @@
   kNoByteRegister = -1  // Signals an illegal register.
 };
 
-
-enum XmmRegister {
-  XMM0 = 0,
-  XMM1 = 1,
-  XMM2 = 2,
-  XMM3 = 3,
-  XMM4 = 4,
-  XMM5 = 5,
-  XMM6 = 6,
-  XMM7 = 7,
-  kNumberOfXmmRegisters = 8,
-  kNoXmmRegister = -1  // Signals an illegal register.
-};
-std::ostream& operator<<(std::ostream& os, const XmmRegister& reg);
-
 enum X87Register {
   ST0 = 0,
   ST1 = 1,
diff --git a/compiler/utils/x86/managed_register_x86.cc b/compiler/utils/x86/managed_register_x86.cc
index 69e6fce..cc7cedf 100644
--- a/compiler/utils/x86/managed_register_x86.cc
+++ b/compiler/utils/x86/managed_register_x86.cc
@@ -16,7 +16,7 @@
 
 #include "managed_register_x86.h"
 
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 namespace x86 {
diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h
index c0c2b65..8810bfa 100644
--- a/compiler/utils/x86/managed_register_x86.h
+++ b/compiler/utils/x86/managed_register_x86.h
@@ -18,7 +18,6 @@
 #define ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_
 
 #include "constants_x86.h"
-#include "debug/dwarf/register.h"
 #include "utils/managed_register.h"
 
 namespace art {
diff --git a/compiler/utils/x86/managed_register_x86_test.cc b/compiler/utils/x86/managed_register_x86_test.cc
index 0ed5c36..28af531 100644
--- a/compiler/utils/x86/managed_register_x86_test.cc
+++ b/compiler/utils/x86/managed_register_x86_test.cc
@@ -16,7 +16,7 @@
 
 #include "managed_register_x86.h"
 
-#include "globals.h"
+#include "base/globals.h"
 #include "gtest/gtest.h"
 
 namespace art {
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index ab761fb..e4d72a7 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -22,9 +22,9 @@
 #include "base/arena_containers.h"
 #include "base/array_ref.h"
 #include "base/bit_utils.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "constants_x86_64.h"
-#include "globals.h"
 #include "heap_poisoning.h"
 #include "managed_register_x86_64.h"
 #include "offsets.h"
diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h
index 2af3e7b..b02e246 100644
--- a/compiler/utils/x86_64/constants_x86_64.h
+++ b/compiler/utils/x86_64/constants_x86_64.h
@@ -22,8 +22,8 @@
 #include <android-base/logging.h>
 
 #include "arch/x86_64/registers_x86_64.h"
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 namespace art {
 namespace x86_64 {
diff --git a/compiler/utils/x86_64/managed_register_x86_64.cc b/compiler/utils/x86_64/managed_register_x86_64.cc
index b8c2db2..c0eec9d 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.cc
+++ b/compiler/utils/x86_64/managed_register_x86_64.cc
@@ -16,7 +16,7 @@
 
 #include "managed_register_x86_64.h"
 
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 namespace x86_64 {
diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h
index 32af672..6760882 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.h
+++ b/compiler/utils/x86_64/managed_register_x86_64.h
@@ -18,7 +18,6 @@
 #define ART_COMPILER_UTILS_X86_64_MANAGED_REGISTER_X86_64_H_
 
 #include "constants_x86_64.h"
-#include "debug/dwarf/register.h"
 #include "utils/managed_register.h"
 
 namespace art {
diff --git a/compiler/utils/x86_64/managed_register_x86_64_test.cc b/compiler/utils/x86_64/managed_register_x86_64_test.cc
index e43d717..46a405f 100644
--- a/compiler/utils/x86_64/managed_register_x86_64_test.cc
+++ b/compiler/utils/x86_64/managed_register_x86_64_test.cc
@@ -15,7 +15,7 @@
  */
 
 #include "managed_register_x86_64.h"
-#include "globals.h"
+#include "base/globals.h"
 #include "gtest/gtest.h"
 
 namespace art {
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 623732f..e74947a 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -70,6 +70,7 @@
     },
     generated_sources: ["art_dex2oat_operator_srcs"],
     shared_libs: [
+        "libprofile",
         "libbase",
         "liblz4",
         "liblzma",
@@ -196,10 +197,12 @@
         "dex2oat-pgo-defaults",
     ],
     shared_libs: [
+        "libprofile",
         "libart-compiler",
         "libart-dexlayout",
         "libart",
         "libdexfile",
+        "libartbase",
         "libbase",
         "liblz4",
         "libsigchain",
@@ -232,10 +235,12 @@
         "dex2oat-defaults",
     ],
     shared_libs: [
+        "libprofiled",
         "libartd-compiler",
         "libartd-dexlayout",
         "libartd",
         "libdexfiled",
+        "libartbased",
         "libbase",
         "liblz4",
         "libsigchain",
@@ -268,7 +273,9 @@
         "libart-compiler",
         "libart-dexlayout",
         "libart",
+        "libartbase",
         "libdexfile",
+        "libprofile",
         "libvixl-arm",
         "libvixl-arm64",
     ] + art_static_dependencies,
@@ -290,6 +297,8 @@
             use_clang_lld: true,
         },
     },
+    // b/79417743, oatdump 32-bit tests failed with clang lld
+    use_clang_lld: false,
     ldflags: [
         // We need this because GC stress mode makes use of
         // _Unwind_GetIP and _Unwind_Backtrace and the symbols are also
@@ -303,6 +312,8 @@
         "libartd-compiler",
         "libartd-dexlayout",
         "libartd",
+        "libartbased",
+        "libprofiled",
         "libdexfiled",
         "libvixld-arm",
         "libvixld-arm64",
@@ -364,6 +375,7 @@
         "external/zlib",
     ],
     shared_libs: [
+        "libprofiled",
         "libartd-compiler",
         "libartd-dexlayout",
         "libbase",
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 3fe9c47..00c893a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -78,8 +78,7 @@
 #include "gc/space/space-inl.h"
 #include "gc/verification.h"
 #include "interpreter/unstarted_runtime.h"
-#include "java_vm_ext.h"
-#include "jit/profile_compilation_info.h"
+#include "jni/java_vm_ext.h"
 #include "linker/buffered_output_stream.h"
 #include "linker/elf_writer.h"
 #include "linker/elf_writer_quick.h"
@@ -93,6 +92,7 @@
 #include "mirror/object_array-inl.h"
 #include "oat_file.h"
 #include "oat_file_assistant.h"
+#include "profile/profile_compilation_info.h"
 #include "runtime.h"
 #include "runtime_options.h"
 #include "scoped_thread_state_change-inl.h"
@@ -350,6 +350,9 @@
   UsageError("");
   UsageError("  --dump-timings: display a breakdown of where time was spent");
   UsageError("");
+  UsageError("  --dump-pass-timings: display a breakdown of time spent in optimization");
+  UsageError("      passes for each compiled method.");
+  UsageError("");
   UsageError("  -g");
   UsageError("  --generate-debug-info: Generate debug information for native debugging,");
   UsageError("      such as stack unwinding information, ELF symbols and DWARF sections.");
@@ -654,7 +657,7 @@
     // the runtime.
     LogCompletionTime();
 
-    if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+    if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
       // We want to just exit on non-debug builds, not bringing the runtime down
       // in an orderly fashion. So release the following fields.
       driver_.release();
@@ -1254,10 +1257,10 @@
         if (stored_class_loader_context_ == nullptr) {
           Usage("Option --stored-class-loader-context has an incorrect format: %s",
                 stored_context_arg.c_str());
-        } else if (!class_loader_context_->VerifyClassLoaderContextMatch(
+        } else if (class_loader_context_->VerifyClassLoaderContextMatch(
             stored_context_arg,
             /*verify_names*/ false,
-            /*verify_checksums*/ false)) {
+            /*verify_checksums*/ false) != ClassLoaderContext::VerificationResult::kVerifies) {
           Usage(
               "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'",
               stored_context_arg.c_str(),
@@ -3116,9 +3119,9 @@
 int main(int argc, char** argv) {
   int result = static_cast<int>(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 or instrumented build or running on valgrind. Note:
-  // The Dex2Oat class should not destruct the runtime in this case.
-  if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && (RUNNING_ON_MEMORY_TOOL == 0)) {
+  // time (bug 10645725) unless we're a debug or instrumented build or running on a memory tool.
+  // Note: The Dex2Oat class should not destruct the runtime in this case.
+  if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && !art::kRunningOnMemoryTool) {
     _exit(result);
   }
   return result;
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 11c0c95..0366467 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -34,7 +34,7 @@
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_loader.h"
 #include "dex/method_reference.h"
-#include "jit/profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 #include "runtime.h"
 
 namespace art {
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index bc8468e..1d0735d 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -38,9 +38,9 @@
 #include "dex/dex_file_loader.h"
 #include "dex2oat_environment_test.h"
 #include "dex2oat_return_codes.h"
-#include "jit/profile_compilation_info.h"
 #include "oat.h"
 #include "oat_file.h"
+#include "profile/profile_compilation_info.h"
 #include "vdex_file.h"
 #include "ziparchive/zip_writer.h"
 
@@ -135,7 +135,8 @@
       ASSERT_TRUE(success) << error_msg << std::endl << output_;
 
       // Verify the odex file was generated as expected.
-      std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+      std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                       odex_location.c_str(),
                                                        odex_location.c_str(),
                                                        nullptr,
                                                        nullptr,
@@ -154,7 +155,8 @@
 
       if (!test_accepts_odex_file_on_failure) {
         // Verify there's no loadable odex file.
-        std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+        std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                         odex_location.c_str(),
                                                          odex_location.c_str(),
                                                          nullptr,
                                                          nullptr,
@@ -470,8 +472,8 @@
 };
 
 TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) {
-  // Native memory usage isn't correctly tracked under sanitization.
-  TEST_DISABLED_FOR_MEMORY_TOOL_ASAN();
+  // Native memory usage isn't correctly tracked when running under ASan.
+  TEST_DISABLED_FOR_MEMORY_TOOL();
 
   // The `native_alloc_2_ >= native_alloc_1_` assertion below may not
   // hold true on some x86 systems; disable this test while we
@@ -542,7 +544,8 @@
     }
     // Host/target independent checks.
     std::string error_msg;
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                     odex_location.c_str(),
                                                      odex_location.c_str(),
                                                      nullptr,
                                                      nullptr,
@@ -812,7 +815,8 @@
                    const std::string& app_image_file_name) {
     // Host/target independent checks.
     std::string error_msg;
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                     odex_location.c_str(),
                                                      odex_location.c_str(),
                                                      nullptr,
                                                      nullptr,
@@ -973,7 +977,8 @@
 
   void CheckResult(const std::string& dex_location, const std::string& odex_location) {
     std::string error_msg;
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                     odex_location.c_str(),
                                                      odex_location.c_str(),
                                                      nullptr,
                                                      nullptr,
@@ -1049,8 +1054,6 @@
 }
 
 TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) {
-  TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND();  // b/63052624
-
   // The watchdog is independent of dex2oat and will not delete intermediates. It is possible
   // that the compilation succeeds and the file is completely written by the time the watchdog
   // kills dex2oat (but the dex2oat threads must have been scheduled pretty badly).
@@ -1366,7 +1369,8 @@
   EXPECT_EQ(res, 0);
 
   // Open our generated oat file.
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_filename.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   oat_filename.c_str(),
                                                    oat_filename.c_str(),
                                                    nullptr,
                                                    nullptr,
@@ -1479,7 +1483,8 @@
       {"--compact-dex-level=fast"});
   EXPECT_EQ(res, 0);
   // Open our generated oat file.
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_filename.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   oat_filename.c_str(),
                                                    oat_filename.c_str(),
                                                    nullptr,
                                                    nullptr,
@@ -1724,7 +1729,8 @@
                       });
   // Open our generated oat file.
   std::string error_msg;
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_filename.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   oat_filename.c_str(),
                                                    oat_filename.c_str(),
                                                    nullptr,
                                                    nullptr,
@@ -1801,7 +1807,8 @@
                       { "--compilation-reason=install" },
                       true);
   std::string error_msg;
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   odex_location.c_str(),
                                                    odex_location.c_str(),
                                                    nullptr,
                                                    nullptr,
@@ -1826,7 +1833,8 @@
                       {},
                       true);
   std::string error_msg;
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   odex_location.c_str(),
                                                    odex_location.c_str(),
                                                    nullptr,
                                                    nullptr,
@@ -1863,7 +1871,8 @@
     ASSERT_TRUE(vdex != nullptr);
     EXPECT_FALSE(vdex->HasDexSection()) << output_;
   }
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   odex_location.c_str(),
                                                    odex_location.c_str(),
                                                    nullptr,
                                                    nullptr,
@@ -2106,7 +2115,8 @@
                       [](const OatFile&) {});
   // Open our generated oat file.
   std::string error_msg;
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   odex_location.c_str(),
                                                    odex_location.c_str(),
                                                    nullptr,
                                                    nullptr,
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index 4ab2012..58bd1b0 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -23,6 +23,7 @@
 #include <android-base/logging.h>
 
 #include "base/casts.h"
+#include "base/globals.h"
 #include "base/leb128.h"
 #include "base/utils.h"
 #include "compiled_method.h"
@@ -31,7 +32,6 @@
 #include "driver/compiler_options.h"
 #include "elf.h"
 #include "elf_utils.h"
-#include "globals.h"
 #include "linker/buffered_output_stream.h"
 #include "linker/elf_builder.h"
 #include "linker/file_output_stream.h"
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index c7a30a0..8ae93ff 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -29,6 +29,7 @@
 #include "art_method-inl.h"
 #include "base/callee_save_type.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/unix_file/fd_file.h"
 #include "class_linker-inl.h"
@@ -47,12 +48,11 @@
 #include "gc/space/large_object_space.h"
 #include "gc/space/space-inl.h"
 #include "gc/verification.h"
-#include "globals.h"
 #include "handle_scope-inl.h"
 #include "image.h"
 #include "imt_conflict_table.h"
 #include "subtype_check.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "linear_alloc.h"
 #include "lock_word.h"
 #include "mirror/array-inl.h"
@@ -248,7 +248,6 @@
 
     CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
     switch (image_storage_mode_) {
-      case ImageHeader::kStorageModeLZ4HC:  // Fall-through.
       case ImageHeader::kStorageModeLZ4: {
         const size_t compressed_max_size = LZ4_compressBound(image_data_size);
         compressed_data.reset(new char[compressed_max_size]);
@@ -257,22 +256,20 @@
             &compressed_data[0],
             image_data_size,
             compressed_max_size);
-
         break;
       }
-      /*
-       * Disabled due to image_test64 flakyness. Both use same decompression. b/27560444
       case ImageHeader::kStorageModeLZ4HC: {
         // Bound is same as non HC.
         const size_t compressed_max_size = LZ4_compressBound(image_data_size);
         compressed_data.reset(new char[compressed_max_size]);
-        data_size = LZ4_compressHC(
+        data_size = LZ4_compress_HC(
             reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader),
             &compressed_data[0],
-            image_data_size);
+            image_data_size,
+            compressed_max_size,
+            LZ4HC_CLEVEL_MAX);
         break;
       }
-      */
       case ImageHeader::kStorageModeUncompressed: {
         data_size = image_data_size;
         image_data_to_write = image_data;
@@ -1067,18 +1064,12 @@
     }
     if (method == nullptr || i < stored_index) {
       if (last_class != nullptr) {
-        const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
-        Signature signature = dex_file.GetMethodSignature(method_id);
-        if (last_class->IsInterface()) {
-          method = last_class->FindInterfaceMethod(name, signature, target_ptr_size_);
-        } else {
-          method = last_class->FindClassMethod(name, signature, target_ptr_size_);
-        }
-        if (method != nullptr) {
-          // If the referenced class is in the image, the defining class must also be there.
-          DCHECK(KeepClass(method->GetDeclaringClass()));
-          dex_cache->SetResolvedMethod(i, method, target_ptr_size_);
-        }
+        // Try to resolve the method with the class linker, which will insert
+        // it into the dex cache if successful.
+        method = class_linker->FindResolvedMethod(last_class, dex_cache, class_loader, i);
+        // If the referenced class is in the image, the defining class must also be there.
+        DCHECK(method == nullptr || KeepClass(method->GetDeclaringClass()));
+        DCHECK(method == nullptr || dex_cache->GetResolvedMethod(i, target_ptr_size_) == method);
       }
     } else {
       DCHECK_EQ(i, stored_index);
@@ -1112,14 +1103,10 @@
     }
     if (field == nullptr || i < stored_index) {
       if (last_class != nullptr) {
-        const char* name = dex_file.StringDataByIdx(field_id.name_idx_);
-        const char* type = dex_file.StringByTypeIdx(field_id.type_idx_);
-        field = mirror::Class::FindField(Thread::Current(), last_class, name, type);
-        if (field != nullptr) {
-          // If the referenced class is in the image, the defining class must also be there.
-          DCHECK(KeepClass(field->GetDeclaringClass()));
-          dex_cache->SetResolvedField(i, field, target_ptr_size_);
-        }
+        field = class_linker->FindResolvedFieldJLS(last_class, dex_cache, class_loader, i);
+        // If the referenced class is in the image, the defining class must also be there.
+        DCHECK(field == nullptr || KeepClass(field->GetDeclaringClass()));
+        DCHECK(field == nullptr || dex_cache->GetResolvedField(i, target_ptr_size_) == field);
       }
     } else {
       DCHECK_EQ(i, stored_index);
@@ -1208,7 +1195,9 @@
     }
   }
   for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
-    PruneAndPreloadDexCache(dex_cache, class_loader);
+    // Pass the class loader associated with the DexCache. This can either be
+    // the app's `class_loader` or `nullptr` if boot class loader.
+    PruneAndPreloadDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader);
   }
 
   // Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
@@ -2094,7 +2083,8 @@
         size_t size = ArtMethod::Size(target_ptr_size_);
         size_t alignment = ArtMethod::Alignment(target_ptr_size_);
         memcpy(dest, pair.first, LengthPrefixedArray<ArtMethod>::ComputeSize(0, size, alignment));
-        // Clear padding to avoid non-deterministic data in the image (and placate valgrind).
+        // Clear padding to avoid non-deterministic data in the image.
+        // Historical note: We also did that to placate Valgrind.
         reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(dest)->ClearPadding(size, alignment);
         break;
       }
diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc
index 1449d47..a6797ff 100644
--- a/dex2oat/linker/multi_oat_relative_patcher.cc
+++ b/dex2oat/linker/multi_oat_relative_patcher.cc
@@ -19,7 +19,7 @@
 #include <android-base/logging.h>
 
 #include "base/bit_utils.h"
-#include "globals.h"
+#include "base/globals.h"
 #include "driver/compiled_method_storage.h"
 
 namespace art {
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index d3f4754..4046dc1 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -51,7 +51,6 @@
 #include "gc/space/space.h"
 #include "handle_scope-inl.h"
 #include "image_writer.h"
-#include "jit/profile_compilation_info.h"
 #include "linker/buffered_output_stream.h"
 #include "linker/file_output_stream.h"
 #include "linker/index_bss_mapping_encoder.h"
@@ -63,6 +62,7 @@
 #include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
 #include "oat_quick_method_header.h"
+#include "profile/profile_compilation_info.h"
 #include "quicken_info.h"
 #include "scoped_thread_state_change-inl.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 37e69f7..df0641c 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -33,7 +33,6 @@
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "jit/profile_compilation_info.h"
 #include "linker/buffered_output_stream.h"
 #include "linker/elf_writer.h"
 #include "linker/elf_writer_quick.h"
@@ -45,6 +44,7 @@
 #include "mirror/object_array-inl.h"
 #include "oat_file-inl.h"
 #include "oat_writer.h"
+#include "profile/profile_compilation_info.h"
 #include "scoped_thread_state_change-inl.h"
 #include "vdex_file.h"
 
@@ -426,7 +426,8 @@
   if (kCompile) {  // OatWriter strips the code, regenerate to compare
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
   }
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(),
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                  tmp_oat.GetFilename(),
                                                   tmp_oat.GetFilename(),
                                                   nullptr,
                                                   nullptr,
@@ -496,7 +497,7 @@
   EXPECT_EQ(76U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
-  EXPECT_EQ(162 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+  EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
             sizeof(QuickEntryPoints));
 }
 
@@ -560,7 +561,8 @@
                           /* verify */ false);
   ASSERT_TRUE(success);
 
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(),
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                  tmp_oat.GetFilename(),
                                                   tmp_oat.GetFilename(),
                                                   nullptr,
                                                   nullptr,
@@ -637,7 +639,8 @@
   ASSERT_TRUE(success);
 
   std::string error_msg;
-  std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(tmp_oat.GetFilename(),
+  std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                         tmp_oat.GetFilename(),
                                                          tmp_oat.GetFilename(),
                                                          nullptr,
                                                          nullptr,
@@ -767,7 +770,8 @@
       ASSERT_TRUE(success);
 
       std::string error_msg;
-      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(tmp_oat.GetFilename(),
+      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                             tmp_oat.GetFilename(),
                                                              tmp_oat.GetFilename(),
                                                              nullptr,
                                                              nullptr,
@@ -816,7 +820,8 @@
       ASSERT_TRUE(success);
 
       std::string error_msg;
-      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(tmp_oat.GetFilename(),
+      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                             tmp_oat.GetFilename(),
                                                              tmp_oat.GetFilename(),
                                                              nullptr,
                                                              nullptr,
diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h
index 9cd51e9..b4123ee 100644
--- a/dex2oat/linker/relative_patcher_test.h
+++ b/dex2oat/linker/relative_patcher_test.h
@@ -20,6 +20,7 @@
 #include "arch/instruction_set.h"
 #include "arch/instruction_set_features.h"
 #include "base/array_ref.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "compiled_method-inl.h"
 #include "dex/verification_results.h"
@@ -27,7 +28,6 @@
 #include "dex/string_reference.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
-#include "globals.h"
 #include "gtest/gtest.h"
 #include "linker/relative_patcher.h"
 #include "linker/vector_output_stream.h"
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
index c63d6c3..2f0962c 100644
--- a/dexdump/Android.bp
+++ b/dexdump/Android.bp
@@ -35,6 +35,7 @@
     host_supported: true,
     shared_libs: [
         "libdexfile",
+        "libartbase",
         "libbase",
     ],
 }
@@ -46,6 +47,7 @@
     device_supported: false,
     static_libs: [
         "libdexfile",
+        "libartbase",
     ] + art_static_dependencies,
     target: {
         darwin: {
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 9536381..f5a13f0 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -45,6 +45,7 @@
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 
+#include "dex/class_accessor-inl.h"
 #include "dex/code_item_accessors-inl.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_exception_helpers.h"
@@ -611,19 +612,11 @@
           pClassDef.class_data_off_, pClassDef.class_data_off_);
 
   // Fields and methods.
-  const u1* pEncodedData = pDexFile->GetClassData(pClassDef);
-  if (pEncodedData != nullptr) {
-    ClassDataItemIterator pClassData(*pDexFile, pEncodedData);
-    fprintf(gOutFile, "static_fields_size  : %d\n", pClassData.NumStaticFields());
-    fprintf(gOutFile, "instance_fields_size: %d\n", pClassData.NumInstanceFields());
-    fprintf(gOutFile, "direct_methods_size : %d\n", pClassData.NumDirectMethods());
-    fprintf(gOutFile, "virtual_methods_size: %d\n", pClassData.NumVirtualMethods());
-  } else {
-    fprintf(gOutFile, "static_fields_size  : 0\n");
-    fprintf(gOutFile, "instance_fields_size: 0\n");
-    fprintf(gOutFile, "direct_methods_size : 0\n");
-    fprintf(gOutFile, "virtual_methods_size: 0\n");
-  }
+  ClassAccessor accessor(*pDexFile, pClassDef);
+  fprintf(gOutFile, "static_fields_size  : %d\n", accessor.NumStaticFields());
+  fprintf(gOutFile, "instance_fields_size: %d\n", accessor.NumInstanceFields());
+  fprintf(gOutFile, "direct_methods_size : %d\n", accessor.NumDirectMethods());
+  fprintf(gOutFile, "virtual_methods_size: %d\n", accessor.NumVirtualMethods());
   fprintf(gOutFile, "\n");
 }
 
@@ -786,11 +779,10 @@
 static std::unique_ptr<char[]> indexString(const DexFile* pDexFile,
                                            const Instruction* pDecInsn,
                                            size_t bufSize) {
-  static const u4 kInvalidIndex = std::numeric_limits<u4>::max();
   std::unique_ptr<char[]> buf(new char[bufSize]);
   // Determine index and width of the string.
   u4 index = 0;
-  u4 secondary_index = kInvalidIndex;
+  u2 secondary_index = 0;
   u4 width = 4;
   switch (Instruction::FormatOf(pDecInsn->Opcode())) {
     // SOME NOT SUPPORTED:
@@ -898,7 +890,7 @@
                                              signature.ToString().c_str());
       }
       if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
-        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
+        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(secondary_index));
         const Signature signature = pDexFile->GetProtoSignature(protoId);
         proto = signature.ToString();
       }
@@ -916,7 +908,7 @@
       break;
     case Instruction::kIndexProtoRef:
       if (index < pDexFile->GetHeader().proto_ids_size_) {
-        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(index);
+        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(index));
         const Signature signature = pDexFile->GetProtoSignature(protoId);
         const std::string& proto = signature.ToString();
         outSize = snprintf(buf.get(), bufSize, "%s // proto@%0*x", proto.c_str(), width, index);
@@ -1705,7 +1697,7 @@
   dex::StringIndex method_name_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i);
   const char* method_name = pDexFile->StringDataByIdx(method_name_idx);
   it.Next();
-  uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  dex::ProtoIndex method_type_idx = static_cast<dex::ProtoIndex>(it.GetJavaValue().i);
   const DexFile::ProtoId& method_type_id = pDexFile->GetProtoId(method_type_idx);
   std::string method_type = pDexFile->GetProtoSignature(method_type_id).ToString();
   it.Next();
@@ -1763,7 +1755,7 @@
         break;
       case EncodedArrayValueIterator::ValueType::kMethodType: {
         type = "MethodType";
-        uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+        dex::ProtoIndex proto_idx = static_cast<dex::ProtoIndex>(it.GetJavaValue().i);
         const DexFile::ProtoId& proto_id = pDexFile->GetProtoId(proto_idx);
         value = pDexFile->GetProtoSignature(proto_id).ToString();
         break;
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index 33ba58f..147af0c 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -39,8 +39,9 @@
         "dex2oat-pgo-defaults",
     ],
     shared_libs: [
-        "libart",
         "libdexfile",
+        "libartbase",
+        "libprofile",
     ],
 
     target: {
@@ -59,8 +60,9 @@
       "art_debug_defaults",
     ],
     shared_libs: [
-        "libartd",
         "libdexfiled",
+        "libartbased",
+        "libprofiled",
     ],
 }
 
@@ -68,7 +70,6 @@
     name: "dexlayout-defaults",
     defaults: ["art_defaults"],
     host_supported: true,
-    srcs: ["dexlayout_main.cc"],
     shared_libs: [
         "libbase",
     ],
@@ -77,8 +78,11 @@
 art_cc_binary {
     name: "dexlayout",
     defaults: ["dexlayout-defaults"],
+    srcs: ["dexlayout_main.cc"],
     shared_libs: [
-        "libart",
+        "libdexfile",
+        "libprofile",
+        "libartbase",
         "libart-dexlayout",
     ],
 }
@@ -89,8 +93,11 @@
         "art_debug_defaults",
         "dexlayout-defaults",
     ],
+    srcs: ["dexlayout_main.cc"],
     shared_libs: [
-        "libartd",
+        "libdexfiled",
+        "libprofiled",
+        "libartbased",
         "libartd-dexlayout",
     ],
 }
@@ -98,7 +105,10 @@
 art_cc_test {
     name: "art_dexlayout_tests",
     defaults: ["art_gtest_defaults"],
-    shared_libs: ["libart-dexlayout"],
+    shared_libs: [
+        "libprofiled",
+        "libartd-dexlayout",
+    ],
     srcs: ["dexlayout_test.cc"],
 }
 
@@ -110,6 +120,8 @@
     cflags: ["-Wall"],
     shared_libs: [
         "libart",
+        "libdexfile",
+        "libartbase",
         "libart-dexlayout",
         "libbase",
     ],
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 1525d53..b7d9db6 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -332,7 +332,7 @@
 }
 
 void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) {
-  const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i);
+  const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(dex::ProtoIndex(i));
   const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id);
   TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_);
 
@@ -353,7 +353,7 @@
 void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) {
   const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i);
   MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_),
-                                     GetProtoId(disk_method_id.proto_idx_),
+                                     GetProtoId(disk_method_id.proto_idx_.index_),
                                      GetStringId(disk_method_id.name_idx_.index_));
   AddIndexedItem(method_ids_, method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i);
 }
diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc
index 516a338..c8aac94 100644
--- a/dexlayout/dex_visualize.cc
+++ b/dexlayout/dex_visualize.cc
@@ -33,7 +33,7 @@
 
 #include "dex_ir.h"
 #include "dexlayout.h"
-#include "jit/profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 
 namespace art {
 
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
index 6cb141f..aa4e6d0 100644
--- a/dexlayout/dexdiag.cc
+++ b/dexlayout/dexdiag.cc
@@ -27,7 +27,6 @@
 #include "android-base/stringprintf.h"
 
 #include "base/logging.h"  // For InitLogging.
-#include "base/mutex.h"
 #include "base/stringpiece.h"
 
 #include "dexlayout.h"
@@ -37,7 +36,6 @@
 #ifdef ART_TARGET_ANDROID
 #include "pagemap/pagemap.h"
 #endif
-#include "runtime.h"
 #include "vdex_file.h"
 
 namespace art {
@@ -446,6 +444,11 @@
   PrintLetterKey();
 }
 
+NO_RETURN static void Abort(const char* msg) {
+  std::cerr << msg;
+  exit(1);
+}
+
 static int DexDiagMain(int argc, char* argv[]) {
   if (argc < 2) {
     Usage(argv[0]);
@@ -471,8 +474,7 @@
   }
 
   // Art specific set up.
-  Locks::Init();
-  InitLogging(argv, Runtime::Abort);
+  InitLogging(argv, Abort);
   MemMap::Init();
 
 #ifdef ART_TARGET_ANDROID
diff --git a/dexlayout/dexdiag_test.cc b/dexlayout/dexdiag_test.cc
index 068949c..53145c2 100644
--- a/dexlayout/dexdiag_test.cc
+++ b/dexlayout/dexdiag_test.cc
@@ -68,7 +68,8 @@
     EXPECT_TRUE(!oat_location.empty());
     std::cout << "==" << oat_location << std::endl;
     std::string error_msg;
-    std::unique_ptr<OatFile> oat(OatFile::Open(oat_location.c_str(),
+    std::unique_ptr<OatFile> oat(OatFile::Open(/* zip_fd */ -1,
+                                               oat_location.c_str(),
                                                oat_location.c_str(),
                                                nullptr,
                                                nullptr,
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 7a8c31b..62dd1a9 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -49,7 +49,7 @@
 #include "dex_verify.h"
 #include "dex_visualize.h"
 #include "dex_writer.h"
-#include "jit/profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 
 namespace art {
 
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index f81d16c..71e56d19 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -33,8 +33,7 @@
 
 #include "base/logging.h"  // For InitLogging.
 #include "base/mem_map.h"
-#include "jit/profile_compilation_info.h"
-#include "runtime.h"
+#include "profile/profile_compilation_info.h"
 
 namespace art {
 
@@ -66,12 +65,17 @@
   LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'";
 }
 
+NO_RETURN static void Abort(const char* msg) {
+  LOG(ERROR) << msg;
+  exit(1);
+}
+
 /*
  * Main driver of the dexlayout utility.
  */
 int DexlayoutDriver(int argc, char** argv) {
   // Art specific set up.
-  InitLogging(argv, Runtime::Abort);
+  InitLogging(argv, Abort);
   MemMap::Init();
 
   Options options;
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 6719d08..f148b94 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -31,7 +31,7 @@
 #include "dex/dex_file_loader.h"
 #include "dexlayout.h"
 #include "exec_utils.h"
-#include "jit/profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 
 namespace art {
 
diff --git a/dexlist/Android.bp b/dexlist/Android.bp
index 2703732..bd521ac 100644
--- a/dexlist/Android.bp
+++ b/dexlist/Android.bp
@@ -17,7 +17,11 @@
     host_supported: true,
     srcs: ["dexlist.cc"],
     cflags: ["-Wall", "-Werror"],
-    shared_libs: ["libdexfile", "libbase"],
+    shared_libs: [
+        "libdexfile",
+        "libartbase",
+        "libbase"
+    ],
     // TODO: fix b/72216369 and remove the need for this.
     include_dirs: [
         "art/runtime"  // dex utils.
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
index 33366ad..99a11cd 100644
--- a/dexoptanalyzer/Android.bp
+++ b/dexoptanalyzer/Android.bp
@@ -38,6 +38,7 @@
     defaults: ["dexoptanalyzer-defaults"],
     shared_libs: [
         "libart",
+        "libartbase",
     ],
 }
 
@@ -49,6 +50,7 @@
     ],
     shared_libs: [
         "libartd",
+        "libartbased",
     ],
 }
 
diff --git a/imgdiag/Android.bp b/imgdiag/Android.bp
index 2b89497..972c8f7 100644
--- a/imgdiag/Android.bp
+++ b/imgdiag/Android.bp
@@ -57,6 +57,7 @@
     defaults: ["imgdiag-defaults"],
     shared_libs: [
         "libart",
+        "libartbase",
         "libart-compiler",
     ],
 }
@@ -69,6 +70,7 @@
     ],
     shared_libs: [
         "libartd",
+        "libartbased",
         "libartd-compiler",
     ],
 }
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 8cf4d03..adf0ad6 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -19,11 +19,13 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
+        "arch/instruction_set.cc",
         "base/allocator.cc",
         "base/arena_allocator.cc",
         "base/arena_bit_vector.cc",
         "base/bit_vector.cc",
         "base/file_magic.cc",
+        "base/file_utils.cc",
         "base/hex_dump.cc",
         "base/logging.cc",
         "base/malloc_arena_pool.cc",
@@ -62,7 +64,6 @@
     generated_sources: ["art_libartbase_operator_srcs"],
     cflags: ["-DBUILDING_LIBART=1"],
     shared_libs: [
-        "libbacktrace",
         "liblog",
 	// For ashmem.
         "libcutils",
@@ -81,6 +82,7 @@
     cmd: "$(location generate_operator_out) art/libartbase $(in) > $(out)",
     tools: ["generate_operator_out"],
     srcs: [
+        "arch/instruction_set.h",
         "base/allocator.h",
         "base/callee_save_type.h",
         "base/unix_file/fd_file.h",
@@ -116,21 +118,42 @@
     export_shared_lib_headers: ["libbase"],
 }
 
-// For now many of these tests still use CommonRuntimeTest, almost universally because of
-// ScratchFile and related.
-// TODO: Remove CommonRuntimeTest dependency from these tests.
+art_cc_library {
+    name: "libartbase-art-gtest",
+    defaults: ["libart-gtest-defaults"],
+    srcs: [
+        "base/common_art_test.cc",
+    ],
+    shared_libs: [
+        "libartbased",
+        "libdexfiled",
+        "libbase",
+        "libbacktrace",
+    ],
+    header_libs: [
+        "libnativehelper_header_only",
+    ],
+    include_dirs: [
+        "external/icu/icu4c/source/common",
+    ],
+}
+
 art_cc_test {
     name: "art_libartbase_tests",
     defaults: [
         "art_gtest_defaults",
     ],
     srcs: [
+        "arch/instruction_set_test.cc",
         "base/arena_allocator_test.cc",
         "base/bit_field_test.cc",
+        "base/bit_memory_region_test.cc",
         "base/bit_string_test.cc",
         "base/bit_struct_test.cc",
+        "base/bit_table_test.cc",
         "base/bit_utils_test.cc",
         "base/bit_vector_test.cc",
+        "base/file_utils_test.cc",
         "base/hash_set_test.cc",
         "base/hex_dump_test.cc",
         "base/histogram_test.cc",
diff --git a/runtime/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc
similarity index 88%
rename from runtime/arch/instruction_set.cc
rename to libartbase/arch/instruction_set.cc
index ecccdcf..a187663 100644
--- a/runtime/arch/instruction_set.cc
+++ b/libartbase/arch/instruction_set.cc
@@ -16,11 +16,9 @@
 
 #include "instruction_set.h"
 
-// Explicitly include our own elf.h to avoid Linux and other dependencies.
-#include "../elf.h"
 #include "android-base/logging.h"
 #include "base/bit_utils.h"
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 
@@ -83,29 +81,6 @@
   return InstructionSet::kNone;
 }
 
-InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) {
-  switch (e_machine) {
-    case EM_ARM:
-      return InstructionSet::kArm;
-    case EM_AARCH64:
-      return InstructionSet::kArm64;
-    case EM_386:
-      return InstructionSet::kX86;
-    case EM_X86_64:
-      return InstructionSet::kX86_64;
-    case EM_MIPS: {
-      if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 ||
-          (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) {
-        return InstructionSet::kMips;
-      } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) {
-        return InstructionSet::kMips64;
-      }
-      break;
-    }
-  }
-  return InstructionSet::kNone;
-}
-
 size_t GetInstructionSetAlignment(InstructionSet isa) {
   switch (isa) {
     case InstructionSet::kArm:
diff --git a/runtime/arch/instruction_set.h b/libartbase/arch/instruction_set.h
similarity index 97%
rename from runtime/arch/instruction_set.h
rename to libartbase/arch/instruction_set.h
index 6434005..06bd53a 100644
--- a/runtime/arch/instruction_set.h
+++ b/libartbase/arch/instruction_set.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_ARCH_INSTRUCTION_SET_H_
-#define ART_RUNTIME_ARCH_INSTRUCTION_SET_H_
+#ifndef ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
+#define ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
 
 #include <iosfwd>
 #include <string>
@@ -89,8 +89,6 @@
 // Note: Returns kNone when the string cannot be parsed to a known value.
 InstructionSet GetInstructionSetFromString(const char* instruction_set);
 
-InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags);
-
 // Fatal logging out of line to keep the header clean of logging.h.
 NO_RETURN void InstructionSetAbort(InstructionSet isa);
 
@@ -299,4 +297,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_ARCH_INSTRUCTION_SET_H_
+#endif  // ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
diff --git a/runtime/arch/instruction_set_test.cc b/libartbase/arch/instruction_set_test.cc
similarity index 100%
rename from runtime/arch/instruction_set_test.cc
rename to libartbase/arch/instruction_set_test.cc
diff --git a/libartbase/base/allocator.cc b/libartbase/base/allocator.cc
index c7be4e0..c4ac180 100644
--- a/libartbase/base/allocator.cc
+++ b/libartbase/base/allocator.cc
@@ -21,7 +21,7 @@
 
 #include <android-base/logging.h>
 
-#include "base/atomic.h"
+#include "atomic.h"
 
 namespace art {
 
diff --git a/libartbase/base/allocator.h b/libartbase/base/allocator.h
index 662f78e..5eb6ea6 100644
--- a/libartbase/base/allocator.h
+++ b/libartbase/base/allocator.h
@@ -19,8 +19,8 @@
 
 #include <type_traits>
 
-#include "base/atomic.h"
-#include "base/macros.h"
+#include "atomic.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h
index 4d535df..4ad77ba 100644
--- a/libartbase/base/arena_allocator.h
+++ b/libartbase/base/arena_allocator.h
@@ -20,11 +20,11 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include "base/bit_utils.h"
-#include "base/debug_stack.h"
-#include "base/dchecked_vector.h"
-#include "base/macros.h"
-#include "base/memory_tool.h"
+#include "bit_utils.h"
+#include "debug_stack.h"
+#include "dchecked_vector.h"
+#include "macros.h"
+#include "memory_tool.h"
 
 namespace art {
 
@@ -147,34 +147,9 @@
 
 typedef ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations> ArenaAllocatorStats;
 
-template <bool kAvailable, bool kValgrind>
-class ArenaAllocatorMemoryToolCheckImpl {
-  // This is the generic template but since there is a partial specialization
-  // for kValgrind == false, this can be instantiated only for kValgrind == true.
-  static_assert(kValgrind, "This template can be instantiated only for Valgrind.");
-  static_assert(kAvailable, "Valgrind implies memory tool availability.");
-
+class ArenaAllocatorMemoryTool {
  public:
-  ArenaAllocatorMemoryToolCheckImpl() : is_running_on_valgrind_(RUNNING_ON_MEMORY_TOOL) { }
-  bool IsRunningOnMemoryTool() { return is_running_on_valgrind_; }
-
- private:
-  const bool is_running_on_valgrind_;
-};
-
-template <bool kAvailable>
-class ArenaAllocatorMemoryToolCheckImpl<kAvailable, false> {
- public:
-  ArenaAllocatorMemoryToolCheckImpl() { }
-  bool IsRunningOnMemoryTool() { return kAvailable; }
-};
-
-typedef ArenaAllocatorMemoryToolCheckImpl<kMemoryToolIsAvailable, kMemoryToolIsValgrind>
-    ArenaAllocatorMemoryToolCheck;
-
-class ArenaAllocatorMemoryTool : private ArenaAllocatorMemoryToolCheck {
- public:
-  using ArenaAllocatorMemoryToolCheck::IsRunningOnMemoryTool;
+  bool IsRunningOnMemoryTool() { return kMemoryToolIsAvailable; }
 
   void MakeDefined(void* ptr, size_t size) {
     if (UNLIKELY(IsRunningOnMemoryTool())) {
diff --git a/libartbase/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc
index 68e99f4..6323a2b 100644
--- a/libartbase/base/arena_allocator_test.cc
+++ b/libartbase/base/arena_allocator_test.cc
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include "base/arena_allocator-inl.h"
-#include "base/arena_bit_vector.h"
-#include "base/malloc_arena_pool.h"
-#include "base/memory_tool.h"
+#include "arena_allocator-inl.h"
+#include "arena_bit_vector.h"
+#include "base/common_art_test.h"
 #include "gtest/gtest.h"
+#include "malloc_arena_pool.h"
+#include "memory_tool.h"
 
 namespace art {
 
@@ -146,11 +147,8 @@
 }
 
 TEST_F(ArenaAllocatorTest, ReallocReuse) {
-  // Realloc does not reuse arenas when running under sanitization. So we cannot do those
-  if (RUNNING_ON_MEMORY_TOOL != 0) {
-    printf("WARNING: TEST DISABLED FOR MEMORY_TOOL\n");
-    return;
-  }
+  // Realloc does not reuse arenas when running under sanitization.
+  TEST_DISABLED_FOR_MEMORY_TOOL();
 
   {
     // Case 1: small aligned allocation, aligned extend inside arena.
diff --git a/libartbase/base/arena_bit_vector.cc b/libartbase/base/arena_bit_vector.cc
index 1542e9d..01f9013 100644
--- a/libartbase/base/arena_bit_vector.cc
+++ b/libartbase/base/arena_bit_vector.cc
@@ -16,8 +16,8 @@
 
 #include "arena_bit_vector.h"
 
-#include "base/allocator.h"
-#include "base/arena_allocator.h"
+#include "allocator.h"
+#include "arena_allocator.h"
 
 namespace art {
 
diff --git a/libartbase/base/arena_bit_vector.h b/libartbase/base/arena_bit_vector.h
index 2b2322e..0fb6bbf 100644
--- a/libartbase/base/arena_bit_vector.h
+++ b/libartbase/base/arena_bit_vector.h
@@ -17,8 +17,8 @@
 #ifndef ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_
 #define ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_
 
-#include "base/arena_object.h"
-#include "base/bit_vector.h"
+#include "arena_object.h"
+#include "bit_vector.h"
 
 namespace art {
 
diff --git a/libartbase/base/arena_containers.h b/libartbase/base/arena_containers.h
index 40cf23c..bd57fb1 100644
--- a/libartbase/base/arena_containers.h
+++ b/libartbase/base/arena_containers.h
@@ -25,10 +25,10 @@
 #include <utility>
 
 #include "arena_allocator.h"
-#include "base/dchecked_vector.h"
-#include "base/hash_map.h"
-#include "base/hash_set.h"
-#include "base/safe_map.h"
+#include "dchecked_vector.h"
+#include "hash_map.h"
+#include "hash_set.h"
+#include "safe_map.h"
 
 namespace art {
 
diff --git a/libartbase/base/arena_object.h b/libartbase/base/arena_object.h
index de7cb64..ed09225 100644
--- a/libartbase/base/arena_object.h
+++ b/libartbase/base/arena_object.h
@@ -20,7 +20,7 @@
 #include <android-base/logging.h>
 
 #include "arena_allocator.h"
-#include "base/macros.h"
+#include "macros.h"
 #include "scoped_arena_allocator.h"
 
 namespace art {
diff --git a/libartbase/base/array_slice.h b/libartbase/base/array_slice.h
index 1ef2fba..fb3da6b 100644
--- a/libartbase/base/array_slice.h
+++ b/libartbase/base/array_slice.h
@@ -17,9 +17,9 @@
 #ifndef ART_LIBARTBASE_BASE_ARRAY_SLICE_H_
 #define ART_LIBARTBASE_BASE_ARRAY_SLICE_H_
 
-#include "base/bit_utils.h"
-#include "base/casts.h"
-#include "base/iteration_range.h"
+#include "bit_utils.h"
+#include "casts.h"
+#include "iteration_range.h"
 #include "stride_iterator.h"
 
 namespace art {
diff --git a/libartbase/base/atomic.h b/libartbase/base/atomic.h
index f736667..b68f867 100644
--- a/libartbase/base/atomic.h
+++ b/libartbase/base/atomic.h
@@ -24,7 +24,7 @@
 
 #include <android-base/logging.h>
 
-#include "base/macros.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h
index f3926bc..3f4d0ba 100644
--- a/libartbase/base/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -19,6 +19,9 @@
 
 #include "memory_region.h"
 
+#include "bit_utils.h"
+#include "memory_tool.h"
+
 namespace art {
 
 // Bit memory region is a bit offset subregion of a normal memoryregion. This is useful for
@@ -26,46 +29,126 @@
 class BitMemoryRegion FINAL : public ValueObject {
  public:
   BitMemoryRegion() = default;
-  ALWAYS_INLINE BitMemoryRegion(MemoryRegion region, size_t bit_offset, size_t bit_size) {
-    bit_start_ = bit_offset % kBitsPerByte;
-    const size_t start = bit_offset / kBitsPerByte;
-    const size_t end = (bit_offset + bit_size + kBitsPerByte - 1) / kBitsPerByte;
-    region_ = region.Subregion(start, end - start);
+  ALWAYS_INLINE explicit BitMemoryRegion(MemoryRegion region)
+    : data_(reinterpret_cast<uintptr_t*>(AlignDown(region.pointer(), sizeof(uintptr_t)))),
+      bit_start_(8 * (reinterpret_cast<uintptr_t>(region.pointer()) % sizeof(uintptr_t))),
+      bit_size_(region.size_in_bits()) {
+  }
+  ALWAYS_INLINE BitMemoryRegion(MemoryRegion region, size_t bit_offset, size_t bit_length)
+    : BitMemoryRegion(region) {
+    DCHECK_LE(bit_offset, bit_size_);
+    DCHECK_LE(bit_length, bit_size_ - bit_offset);
+    bit_start_ += bit_offset;
+    bit_size_ = bit_length;
   }
 
-  void* pointer() const { return region_.pointer(); }
-  size_t size() const { return region_.size(); }
-  size_t BitOffset() const { return bit_start_; }
+  ALWAYS_INLINE bool IsValid() const { return data_ != nullptr; }
+
   size_t size_in_bits() const {
-    return region_.size_in_bits();
+    return bit_size_;
   }
 
-  ALWAYS_INLINE BitMemoryRegion Subregion(size_t bit_offset, size_t bit_size) const {
-    return BitMemoryRegion(region_, bit_start_ + bit_offset, bit_size);
+  ALWAYS_INLINE BitMemoryRegion Subregion(size_t bit_offset, size_t bit_length) const {
+    DCHECK_LE(bit_offset, bit_size_);
+    DCHECK_LE(bit_length, bit_size_ - bit_offset);
+    BitMemoryRegion result = *this;
+    result.bit_start_ += bit_offset;
+    result.bit_size_ = bit_length;
+    return result;
   }
 
   // Load a single bit in the region. The bit at offset 0 is the least
   // significant bit in the first byte.
+  ATTRIBUTE_NO_SANITIZE_ADDRESS  // We might touch extra bytes due to the alignment.
   ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const {
-    return region_.LoadBit(bit_offset + bit_start_);
+    DCHECK_LT(bit_offset, bit_size_);
+    size_t index = (bit_start_ + bit_offset) / kBitsPerIntPtrT;
+    size_t shift = (bit_start_ + bit_offset) % kBitsPerIntPtrT;
+    return ((data_[index] >> shift) & 1) != 0;
   }
 
   ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const {
-    region_.StoreBit(bit_offset + bit_start_, value);
+    DCHECK_LT(bit_offset, bit_size_);
+    uint8_t* data = reinterpret_cast<uint8_t*>(data_);
+    size_t index = (bit_start_ + bit_offset) / kBitsPerByte;
+    size_t shift = (bit_start_ + bit_offset) % kBitsPerByte;
+    data[index] &= ~(1 << shift);  // Clear bit.
+    data[index] |= (value ? 1 : 0) << shift;  // Set bit.
+    DCHECK_EQ(value, LoadBit(bit_offset));
   }
 
-  ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const {
-    return region_.LoadBits(bit_offset + bit_start_, length);
+  // Load `bit_length` bits from `data` starting at given `bit_offset`.
+  // The least significant bit is stored in the smallest memory offset.
+  ATTRIBUTE_NO_SANITIZE_ADDRESS  // We might touch extra bytes due to the alignment.
+  ALWAYS_INLINE uint32_t LoadBits(size_t bit_offset, size_t bit_length) const {
+    DCHECK(IsAligned<sizeof(uintptr_t)>(data_));
+    DCHECK_LE(bit_offset, bit_size_);
+    DCHECK_LE(bit_length, bit_size_ - bit_offset);
+    DCHECK_LE(bit_length, BitSizeOf<uint32_t>());
+    if (bit_length == 0) {
+      return 0;
+    }
+    uintptr_t mask = std::numeric_limits<uintptr_t>::max() >> (kBitsPerIntPtrT - bit_length);
+    size_t index = (bit_start_ + bit_offset) / kBitsPerIntPtrT;
+    size_t shift = (bit_start_ + bit_offset) % kBitsPerIntPtrT;
+    uintptr_t value = data_[index] >> shift;
+    size_t finished_bits = kBitsPerIntPtrT - shift;
+    if (finished_bits < bit_length) {
+      value |= data_[index + 1] << finished_bits;
+    }
+    return value & mask;
   }
 
-  // Store at a bit offset from inside the bit memory region.
-  ALWAYS_INLINE void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) {
-    region_.StoreBits(bit_offset + bit_start_, value, length);
+  // Load bits starting at given `bit_offset`, and advance the `bit_offset`.
+  ALWAYS_INLINE uint32_t LoadBitsAndAdvance(size_t* bit_offset, size_t bit_length) const {
+    uint32_t result = LoadBits(*bit_offset, bit_length);
+    *bit_offset += bit_length;
+    return result;
+  }
+
+  // Store `bit_length` bits in `data` starting at given `bit_offset`.
+  // The least significant bit is stored in the smallest memory offset.
+  ALWAYS_INLINE void StoreBits(size_t bit_offset, uint32_t value, size_t bit_length) {
+    DCHECK_LE(bit_offset, bit_size_);
+    DCHECK_LE(bit_length, bit_size_ - bit_offset);
+    DCHECK_LE(bit_length, BitSizeOf<uint32_t>());
+    DCHECK_LE(value, MaxInt<uint32_t>(bit_length));
+    if (bit_length == 0) {
+      return;
+    }
+    // Write data byte by byte to avoid races with other threads
+    // on bytes that do not overlap with this region.
+    uint8_t* data = reinterpret_cast<uint8_t*>(data_);
+    uint32_t mask = std::numeric_limits<uint32_t>::max() >> (BitSizeOf<uint32_t>() - bit_length);
+    size_t index = (bit_start_ + bit_offset) / kBitsPerByte;
+    size_t shift = (bit_start_ + bit_offset) % kBitsPerByte;
+    data[index] &= ~(mask << shift);  // Clear bits.
+    data[index] |= (value << shift);  // Set bits.
+    size_t finished_bits = kBitsPerByte - shift;
+    for (int i = 1; finished_bits < bit_length; i++, finished_bits += kBitsPerByte) {
+      data[index + i] &= ~(mask >> finished_bits);  // Clear bits.
+      data[index + i] |= (value >> finished_bits);  // Set bits.
+    }
+    DCHECK_EQ(value, LoadBits(bit_offset, bit_length));
+  }
+
+  // Store bits starting at given `bit_offset`, and advance the `bit_offset`.
+  ALWAYS_INLINE void StoreBitsAndAdvance(size_t* bit_offset, uint32_t value, size_t bit_length) {
+    StoreBits(*bit_offset, value, bit_length);
+    *bit_offset += bit_length;
+  }
+
+  ALWAYS_INLINE bool Equals(const BitMemoryRegion& other) const {
+    return data_ == other.data_ &&
+           bit_start_ == other.bit_start_ &&
+           bit_size_ == other.bit_size_;
   }
 
  private:
-  MemoryRegion region_;
+  // The data pointer must be naturally aligned. This makes loading code faster.
+  uintptr_t* data_ = nullptr;
   size_t bit_start_ = 0;
+  size_t bit_size_ = 0;
 };
 
 }  // namespace art
diff --git a/libartbase/base/bit_memory_region_test.cc b/libartbase/base/bit_memory_region_test.cc
new file mode 100644
index 0000000..b754698
--- /dev/null
+++ b/libartbase/base/bit_memory_region_test.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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 "bit_memory_region.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+static void CheckBits(uint8_t* data,
+                      size_t size,
+                      uint32_t init,
+                      size_t offset,
+                      size_t length,
+                      uint32_t value) {
+  for (size_t i = 0; i < size * kBitsPerByte; i++) {
+    uint8_t expected = (offset <= i && i < offset + length) ? value >> (i - offset) : init;
+    uint8_t actual = data[i / kBitsPerByte] >> (i % kBitsPerByte);
+    EXPECT_EQ(expected & 1, actual & 1);
+  }
+}
+
+TEST(BitMemoryRegion, TestBit) {
+  uint8_t data[sizeof(uint32_t) * 2];
+  for (size_t bit_offset = 0; bit_offset < 2 * sizeof(uint32_t) * kBitsPerByte; ++bit_offset) {
+    for (uint32_t initial_value = 0; initial_value <= 1; initial_value++) {
+      for (uint32_t value = 0; value <= 1; value++) {
+        // Check Store and Load with bit_offset set on the region.
+        std::fill_n(data, sizeof(data), initial_value * 0xFF);
+        BitMemoryRegion bmr1(MemoryRegion(&data, sizeof(data)), bit_offset, 1);
+        bmr1.StoreBit(0, value);
+        EXPECT_EQ(bmr1.LoadBit(0), value);
+        CheckBits(data, sizeof(data), initial_value, bit_offset, 1, value);
+        // Check Store and Load with bit_offset set on the methods.
+        std::fill_n(data, sizeof(data), initial_value * 0xFF);
+        BitMemoryRegion bmr2(MemoryRegion(&data, sizeof(data)));
+        bmr2.StoreBit(bit_offset, value);
+        EXPECT_EQ(bmr2.LoadBit(bit_offset), value);
+        CheckBits(data, sizeof(data), initial_value, bit_offset, 1, value);
+      }
+    }
+  }
+}
+
+TEST(BitMemoryRegion, TestBits) {
+  uint8_t data[sizeof(uint32_t) * 4];
+  for (size_t bit_offset = 0; bit_offset < 3 * sizeof(uint32_t) * kBitsPerByte; ++bit_offset) {
+    uint32_t mask = 0;
+    for (size_t bit_length = 0; bit_length < sizeof(uint32_t) * kBitsPerByte; ++bit_length) {
+      const uint32_t value = 0xDEADBEEF & mask;
+      for (uint32_t initial_value = 0; initial_value <= 1; initial_value++) {
+        // Check Store and Load with bit_offset set on the region.
+        std::fill_n(data, sizeof(data), initial_value * 0xFF);
+        BitMemoryRegion bmr1(MemoryRegion(&data, sizeof(data)), bit_offset, bit_length);
+        bmr1.StoreBits(0, value, bit_length);
+        EXPECT_EQ(bmr1.LoadBits(0, bit_length), value);
+        CheckBits(data, sizeof(data), initial_value, bit_offset, bit_length, value);
+        // Check Store and Load with bit_offset set on the methods.
+        std::fill_n(data, sizeof(data), initial_value * 0xFF);
+        BitMemoryRegion bmr2(MemoryRegion(&data, sizeof(data)));
+        bmr2.StoreBits(bit_offset, value, bit_length);
+        EXPECT_EQ(bmr2.LoadBits(bit_offset, bit_length), value);
+        CheckBits(data, sizeof(data), initial_value, bit_offset, bit_length, value);
+      }
+      mask = (mask << 1) | 1;
+    }
+  }
+}
+
+}  // namespace art
diff --git a/libartbase/base/bit_string.h b/libartbase/base/bit_string.h
index 0e051f3..d995f8d 100644
--- a/libartbase/base/bit_string.h
+++ b/libartbase/base/bit_string.h
@@ -17,8 +17,8 @@
 #ifndef ART_LIBARTBASE_BASE_BIT_STRING_H_
 #define ART_LIBARTBASE_BASE_BIT_STRING_H_
 
-#include "base/bit_struct.h"
-#include "base/bit_utils.h"
+#include "bit_struct.h"
+#include "bit_utils.h"
 
 #include <ostream>
 
diff --git a/libartbase/base/bit_string_test.cc b/libartbase/base/bit_string_test.cc
index 23274e3..89a71a1 100644
--- a/libartbase/base/bit_string_test.cc
+++ b/libartbase/base/bit_string_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "base/bit_string.h"
+#include "bit_string.h"
 
 #include "gtest/gtest.h"
 #include "android-base/logging.h"
diff --git a/libartbase/base/bit_struct.h b/libartbase/base/bit_struct.h
index 386b896..9814fd4 100644
--- a/libartbase/base/bit_struct.h
+++ b/libartbase/base/bit_struct.h
@@ -17,8 +17,8 @@
 #ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_H_
 #define ART_LIBARTBASE_BASE_BIT_STRUCT_H_
 
-#include "base/bit_utils.h"
 #include "bit_struct_detail.h"
+#include "bit_utils.h"
 
 //
 // Zero-cost, type-safe, well-defined "structs" of bit fields.
diff --git a/libartbase/base/bit_struct_detail.h b/libartbase/base/bit_struct_detail.h
index facfa61..68c2e44 100644
--- a/libartbase/base/bit_struct_detail.h
+++ b/libartbase/base/bit_struct_detail.h
@@ -17,7 +17,7 @@
 #ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_
 #define ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_
 
-#include "base/bit_utils.h"
+#include "bit_utils.h"
 #include "globals.h"
 
 #include <type_traits>
diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h
new file mode 100644
index 0000000..24bdd13
--- /dev/null
+++ b/libartbase/base/bit_table.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2018 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_LIBARTBASE_BASE_BIT_TABLE_H_
+#define ART_LIBARTBASE_BASE_BIT_TABLE_H_
+
+#include <vector>
+
+#include "base/bit_memory_region.h"
+#include "base/bit_utils.h"
+#include "base/memory_region.h"
+
+namespace art {
+
+constexpr uint32_t kVarintHeaderBits = 4;
+constexpr uint32_t kVarintSmallValue = 11;  // Maximum value which is stored as-is.
+
+// Load variable-length bit-packed integer from `data` starting at `bit_offset`.
+// The first four bits determine the variable length of the encoded integer:
+//   Values 0..11 represent the result as-is, with no further following bits.
+//   Values 12..15 mean the result is in the next 8/16/24/32-bits respectively.
+ALWAYS_INLINE static inline uint32_t DecodeVarintBits(BitMemoryRegion region, size_t* bit_offset) {
+  uint32_t x = region.LoadBitsAndAdvance(bit_offset, kVarintHeaderBits);
+  if (x > kVarintSmallValue) {
+    x = region.LoadBitsAndAdvance(bit_offset, (x - kVarintSmallValue) * kBitsPerByte);
+  }
+  return x;
+}
+
+// Store variable-length bit-packed integer from `data` starting at `bit_offset`.
+template<typename Vector>
+ALWAYS_INLINE static inline void EncodeVarintBits(Vector* out, size_t* bit_offset, uint32_t value) {
+  if (value <= kVarintSmallValue) {
+    out->resize(BitsToBytesRoundUp(*bit_offset + kVarintHeaderBits));
+    BitMemoryRegion region(MemoryRegion(out->data(), out->size()));
+    region.StoreBitsAndAdvance(bit_offset, value, kVarintHeaderBits);
+  } else {
+    uint32_t num_bits = RoundUp(MinimumBitsToStore(value), kBitsPerByte);
+    out->resize(BitsToBytesRoundUp(*bit_offset + kVarintHeaderBits + num_bits));
+    BitMemoryRegion region(MemoryRegion(out->data(), out->size()));
+    uint32_t header = kVarintSmallValue + num_bits / kBitsPerByte;
+    region.StoreBitsAndAdvance(bit_offset, header, kVarintHeaderBits);
+    region.StoreBitsAndAdvance(bit_offset, value, num_bits);
+  }
+}
+
+template<uint32_t kNumColumns>
+class BitTable {
+ public:
+  class Accessor {
+   public:
+    static constexpr uint32_t kNoValue = std::numeric_limits<uint32_t>::max();
+
+    Accessor(const BitTable* table, uint32_t row) : table_(table), row_(row) {}
+
+    ALWAYS_INLINE uint32_t Row() const { return row_; }
+
+    ALWAYS_INLINE bool IsValid() const { return table_ != nullptr && row_ < table_->NumRows(); }
+
+    template<uint32_t Column>
+    ALWAYS_INLINE uint32_t Get() const {
+      static_assert(Column < kNumColumns, "Column out of bounds");
+      return table_->Get(row_, Column);
+    }
+
+    ALWAYS_INLINE bool Equals(const Accessor& other) {
+      return this->table_ == other.table_ && this->row_ == other.row_;
+    }
+
+    Accessor& operator++() {
+      row_++;
+      return *this;
+    }
+
+   protected:
+    const BitTable* table_;
+    uint32_t row_;
+  };
+
+  static constexpr uint32_t kValueBias = -1;
+
+  BitTable() {}
+  BitTable(void* data, size_t size, size_t* bit_offset = 0) {
+    Decode(BitMemoryRegion(MemoryRegion(data, size)), bit_offset);
+  }
+
+  ALWAYS_INLINE void Decode(BitMemoryRegion region, size_t* bit_offset) {
+    // Decode row count and column sizes from the table header.
+    num_rows_ = DecodeVarintBits(region, bit_offset);
+    if (num_rows_ != 0) {
+      column_offset_[0] = 0;
+      for (uint32_t i = 0; i < kNumColumns; i++) {
+        size_t column_end = column_offset_[i] + DecodeVarintBits(region, bit_offset);
+        column_offset_[i + 1] = column_end;
+        DCHECK_EQ(column_offset_[i + 1], column_end) << "Overflow";
+      }
+    }
+
+    // Record the region which contains the table data and skip past it.
+    table_data_ = region.Subregion(*bit_offset, num_rows_ * NumRowBits());
+    *bit_offset += table_data_.size_in_bits();
+  }
+
+  ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column = 0) const {
+    DCHECK_LT(row, num_rows_);
+    DCHECK_LT(column, kNumColumns);
+    size_t offset = row * NumRowBits() + column_offset_[column];
+    return table_data_.LoadBits(offset, NumColumnBits(column)) + kValueBias;
+  }
+
+  size_t NumRows() const { return num_rows_; }
+
+  uint32_t NumRowBits() const { return column_offset_[kNumColumns]; }
+
+  constexpr size_t NumColumns() const { return kNumColumns; }
+
+  uint32_t NumColumnBits(uint32_t column) const {
+    return column_offset_[column + 1] - column_offset_[column];
+  }
+
+  size_t DataBitSize() const { return num_rows_ * column_offset_[kNumColumns]; }
+
+ protected:
+  BitMemoryRegion table_data_;
+  size_t num_rows_ = 0;
+
+  uint16_t column_offset_[kNumColumns + 1] = {};
+};
+
+template<uint32_t kNumColumns>
+constexpr uint32_t BitTable<kNumColumns>::Accessor::kNoValue;
+
+template<uint32_t kNumColumns>
+constexpr uint32_t BitTable<kNumColumns>::kValueBias;
+
+template<uint32_t kNumColumns, typename Alloc = std::allocator<uint32_t>>
+class BitTableBuilder {
+ public:
+  explicit BitTableBuilder(Alloc alloc = Alloc()) : buffer_(alloc) {}
+
+  template<typename ... T>
+  uint32_t AddRow(T ... values) {
+    constexpr size_t count = sizeof...(values);
+    static_assert(count == kNumColumns, "Incorrect argument count");
+    uint32_t data[count] = { values... };
+    buffer_.insert(buffer_.end(), data, data + count);
+    return num_rows_++;
+  }
+
+  ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column) const {
+    return buffer_[row * kNumColumns + column];
+  }
+
+  template<typename Vector>
+  void Encode(Vector* out, size_t* bit_offset) {
+    constexpr uint32_t bias = BitTable<kNumColumns>::kValueBias;
+    size_t initial_bit_offset = *bit_offset;
+    // Measure data size.
+    uint32_t max_column_value[kNumColumns] = {};
+    for (uint32_t r = 0; r < num_rows_; r++) {
+      for (uint32_t c = 0; c < kNumColumns; c++) {
+        max_column_value[c] |= Get(r, c) - bias;
+      }
+    }
+    // Write table header.
+    uint32_t table_data_bits = 0;
+    uint32_t column_bits[kNumColumns] = {};
+    EncodeVarintBits(out, bit_offset, num_rows_);
+    if (num_rows_ != 0) {
+      for (uint32_t c = 0; c < kNumColumns; c++) {
+        column_bits[c] = MinimumBitsToStore(max_column_value[c]);
+        EncodeVarintBits(out, bit_offset, column_bits[c]);
+        table_data_bits += num_rows_ * column_bits[c];
+      }
+    }
+    // Write table data.
+    out->resize(BitsToBytesRoundUp(*bit_offset + table_data_bits));
+    BitMemoryRegion region(MemoryRegion(out->data(), out->size()));
+    for (uint32_t r = 0; r < num_rows_; r++) {
+      for (uint32_t c = 0; c < kNumColumns; c++) {
+        region.StoreBitsAndAdvance(bit_offset, Get(r, c) - bias, column_bits[c]);
+      }
+    }
+    // Verify the written data.
+    if (kIsDebugBuild) {
+      BitTable<kNumColumns> table;
+      table.Decode(region, &initial_bit_offset);
+      DCHECK_EQ(this->num_rows_, table.NumRows());
+      for (uint32_t c = 0; c < kNumColumns; c++) {
+        DCHECK_EQ(column_bits[c], table.NumColumnBits(c));
+      }
+      for (uint32_t r = 0; r < num_rows_; r++) {
+        for (uint32_t c = 0; c < kNumColumns; c++) {
+          DCHECK_EQ(this->Get(r, c), table.Get(r, c)) << " (" << r << ", " << c << ")";
+        }
+      }
+    }
+  }
+
+ protected:
+  std::vector<uint32_t, Alloc> buffer_;
+  uint32_t num_rows_ = 0;
+};
+
+}  // namespace art
+
+#endif  // ART_LIBARTBASE_BASE_BIT_TABLE_H_
diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc
new file mode 100644
index 0000000..25bfcf0
--- /dev/null
+++ b/libartbase/base/bit_table_test.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 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 "bit_table.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(BitTableTest, TestVarint) {
+  for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) {
+    uint32_t values[] = { 0, 1, 11, 12, 15, 16, 255, 256, ~1u, ~0u };
+    for (uint32_t value : values) {
+      std::vector<uint8_t> buffer;
+      size_t encode_bit_offset = start_bit_offset;
+      EncodeVarintBits(&buffer, &encode_bit_offset, value);
+
+      size_t decode_bit_offset = start_bit_offset;
+      BitMemoryRegion region(MemoryRegion(buffer.data(), buffer.size()));
+      uint32_t result = DecodeVarintBits(region, &decode_bit_offset);
+      EXPECT_EQ(encode_bit_offset, decode_bit_offset);
+      EXPECT_EQ(value, result);
+    }
+  }
+}
+
+TEST(BitTableTest, TestEmptyTable) {
+  std::vector<uint8_t> buffer;
+  size_t encode_bit_offset = 0;
+  BitTableBuilder<1> builder;
+  builder.Encode(&buffer, &encode_bit_offset);
+
+  size_t decode_bit_offset = 0;
+  BitTable<1> table(buffer.data(), buffer.size(), &decode_bit_offset);
+  EXPECT_EQ(encode_bit_offset, decode_bit_offset);
+  EXPECT_EQ(0u, table.NumRows());
+}
+
+TEST(BitTableTest, TestSingleColumnTable) {
+  constexpr uint32_t kNoValue = -1;
+  std::vector<uint8_t> buffer;
+  size_t encode_bit_offset = 0;
+  BitTableBuilder<1> builder;
+  builder.AddRow(42u);
+  builder.AddRow(kNoValue);
+  builder.AddRow(1000u);
+  builder.AddRow(kNoValue);
+  builder.Encode(&buffer, &encode_bit_offset);
+
+  size_t decode_bit_offset = 0;
+  BitTable<1> table(buffer.data(), buffer.size(), &decode_bit_offset);
+  EXPECT_EQ(encode_bit_offset, decode_bit_offset);
+  EXPECT_EQ(4u, table.NumRows());
+  EXPECT_EQ(42u, table.Get(0));
+  EXPECT_EQ(kNoValue, table.Get(1));
+  EXPECT_EQ(1000u, table.Get(2));
+  EXPECT_EQ(kNoValue, table.Get(3));
+  EXPECT_EQ(10u, table.NumColumnBits(0));
+}
+
+TEST(BitTableTest, TestUnalignedTable) {
+  for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) {
+    std::vector<uint8_t> buffer;
+    size_t encode_bit_offset = start_bit_offset;
+    BitTableBuilder<1> builder;
+    builder.AddRow(42u);
+    builder.Encode(&buffer, &encode_bit_offset);
+
+    size_t decode_bit_offset = start_bit_offset;
+    BitTable<1> table(buffer.data(), buffer.size(), &decode_bit_offset);
+    EXPECT_EQ(encode_bit_offset, decode_bit_offset) << " start_bit_offset=" << start_bit_offset;
+    EXPECT_EQ(1u, table.NumRows());
+    EXPECT_EQ(42u, table.Get(0));
+  }
+}
+
+TEST(BitTableTest, TestBigTable) {
+  constexpr uint32_t kNoValue = -1;
+  std::vector<uint8_t> buffer;
+  size_t encode_bit_offset = 0;
+  BitTableBuilder<4> builder;
+  builder.AddRow(42u, kNoValue, 0u, static_cast<uint32_t>(-2));
+  builder.AddRow(62u, kNoValue, 63u, static_cast<uint32_t>(-3));
+  builder.Encode(&buffer, &encode_bit_offset);
+
+  size_t decode_bit_offset = 0;
+  BitTable<4> table(buffer.data(), buffer.size(), &decode_bit_offset);
+  EXPECT_EQ(encode_bit_offset, decode_bit_offset);
+  EXPECT_EQ(2u, table.NumRows());
+  EXPECT_EQ(42u, table.Get(0, 0));
+  EXPECT_EQ(kNoValue, table.Get(0, 1));
+  EXPECT_EQ(0u, table.Get(0, 2));
+  EXPECT_EQ(static_cast<uint32_t>(-2), table.Get(0, 3));
+  EXPECT_EQ(62u, table.Get(1, 0));
+  EXPECT_EQ(kNoValue, table.Get(1, 1));
+  EXPECT_EQ(63u, table.Get(1, 2));
+  EXPECT_EQ(static_cast<uint32_t>(-3), table.Get(1, 3));
+  EXPECT_EQ(6u, table.NumColumnBits(0));
+  EXPECT_EQ(0u, table.NumColumnBits(1));
+  EXPECT_EQ(7u, table.NumColumnBits(2));
+  EXPECT_EQ(32u, table.NumColumnBits(3));
+}
+
+}  // namespace art
diff --git a/libartbase/base/bit_utils.h b/libartbase/base/bit_utils.h
index ff6c160..58cc78c 100644
--- a/libartbase/base/bit_utils.h
+++ b/libartbase/base/bit_utils.h
@@ -22,7 +22,8 @@
 
 #include <android-base/logging.h>
 
-#include "base/stl_util_identity.h"
+#include "globals.h"
+#include "stl_util_identity.h"
 
 namespace art {
 
@@ -499,6 +500,10 @@
   return bitfield_unsigned;
 }
 
+inline static constexpr size_t BitsToBytesRoundUp(size_t num_bits) {
+  return RoundUp(num_bits, kBitsPerByte) / kBitsPerByte;
+}
+
 }  // namespace art
 
 #endif  // ART_LIBARTBASE_BASE_BIT_UTILS_H_
diff --git a/libartbase/base/bit_utils_iterator.h b/libartbase/base/bit_utils_iterator.h
index 3fab15a..4975ebf 100644
--- a/libartbase/base/bit_utils_iterator.h
+++ b/libartbase/base/bit_utils_iterator.h
@@ -23,9 +23,9 @@
 
 #include <android-base/logging.h>
 
-#include "base/bit_utils.h"
-#include "base/iteration_range.h"
-#include "base/stl_util.h"
+#include "bit_utils.h"
+#include "iteration_range.h"
+#include "stl_util.h"
 
 namespace art {
 
diff --git a/libartbase/base/bit_vector-inl.h b/libartbase/base/bit_vector-inl.h
index 7a9f465..2bdc14e 100644
--- a/libartbase/base/bit_vector-inl.h
+++ b/libartbase/base/bit_vector-inl.h
@@ -21,7 +21,7 @@
 
 #include <android-base/logging.h>
 
-#include "base/bit_utils.h"
+#include "bit_utils.h"
 
 namespace art {
 
diff --git a/libartbase/base/bit_vector.h b/libartbase/base/bit_vector.h
index 2ffa2aa..a930f4e 100644
--- a/libartbase/base/bit_vector.h
+++ b/libartbase/base/bit_vector.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <iterator>
 
-#include "base/bit_utils.h"
+#include "bit_utils.h"
 #include "globals.h"
 
 namespace art {
diff --git a/libartbase/base/bounded_fifo.h b/libartbase/base/bounded_fifo.h
index 444f31a..43d14f4 100644
--- a/libartbase/base/bounded_fifo.h
+++ b/libartbase/base/bounded_fifo.h
@@ -19,7 +19,7 @@
 
 #include <android-base/logging.h>
 
-#include "base/bit_utils.h"
+#include "bit_utils.h"
 
 namespace art {
 
diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc
new file mode 100644
index 0000000..0d798f3
--- /dev/null
+++ b/libartbase/base/common_art_test.cc
@@ -0,0 +1,420 @@
+/*
+ * 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 "common_art_test.h"
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <cstdio>
+#include "nativehelper/scoped_local_ref.h"
+
+#include "android-base/stringprintf.h"
+#include <unicode/uvernum.h>
+
+#include "art_field-inl.h"
+#include "base/file_utils.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mem_map.h"
+#include "base/mutex.h"
+#include "base/os.h"
+#include "base/runtime_debug.h"
+#include "base/stl_util.h"
+#include "base/unix_file/fd_file.h"
+#include "dex/art_dex_file_loader.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_file_loader.h"
+#include "dex/primitive.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+using android::base::StringPrintf;
+
+ScratchFile::ScratchFile() {
+  // ANDROID_DATA needs to be set
+  CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) <<
+      "Are you subclassing RuntimeTest?";
+  filename_ = getenv("ANDROID_DATA");
+  filename_ += "/TmpFile-XXXXXX";
+  int fd = mkstemp(&filename_[0]);
+  CHECK_NE(-1, fd) << strerror(errno) << " for " << filename_;
+  file_.reset(new File(fd, GetFilename(), true));
+}
+
+ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix)
+    : ScratchFile(other.GetFilename() + suffix) {}
+
+ScratchFile::ScratchFile(const std::string& filename) : filename_(filename) {
+  int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666);
+  CHECK_NE(-1, fd);
+  file_.reset(new File(fd, GetFilename(), true));
+}
+
+ScratchFile::ScratchFile(File* file) {
+  CHECK(file != nullptr);
+  filename_ = file->GetPath();
+  file_.reset(file);
+}
+
+ScratchFile::ScratchFile(ScratchFile&& other) {
+  *this = std::move(other);
+}
+
+ScratchFile& ScratchFile::operator=(ScratchFile&& other) {
+  if (GetFile() != other.GetFile()) {
+    std::swap(filename_, other.filename_);
+    std::swap(file_, other.file_);
+  }
+  return *this;
+}
+
+ScratchFile::~ScratchFile() {
+  Unlink();
+}
+
+int ScratchFile::GetFd() const {
+  return file_->Fd();
+}
+
+void ScratchFile::Close() {
+  if (file_.get() != nullptr) {
+    if (file_->FlushCloseOrErase() != 0) {
+      PLOG(WARNING) << "Error closing scratch file.";
+    }
+  }
+}
+
+void ScratchFile::Unlink() {
+  if (!OS::FileExists(filename_.c_str())) {
+    return;
+  }
+  Close();
+  int unlink_result = unlink(filename_.c_str());
+  CHECK_EQ(0, unlink_result);
+}
+
+void CommonArtTestImpl::SetUpAndroidRoot() {
+  if (IsHost()) {
+    // $ANDROID_ROOT is set on the device, but not necessarily on the host.
+    // But it needs to be set so that icu4c can find its locale data.
+    const char* android_root_from_env = getenv("ANDROID_ROOT");
+    if (android_root_from_env == nullptr) {
+      // Use ANDROID_HOST_OUT for ANDROID_ROOT if it is set.
+      const char* android_host_out = getenv("ANDROID_HOST_OUT");
+      if (android_host_out != nullptr) {
+        setenv("ANDROID_ROOT", android_host_out, 1);
+      } else {
+        // Build it from ANDROID_BUILD_TOP or cwd
+        std::string root;
+        const char* android_build_top = getenv("ANDROID_BUILD_TOP");
+        if (android_build_top != nullptr) {
+          root += android_build_top;
+        } else {
+          // Not set by build server, so default to current directory
+          char* cwd = getcwd(nullptr, 0);
+          setenv("ANDROID_BUILD_TOP", cwd, 1);
+          root += cwd;
+          free(cwd);
+        }
+#if defined(__linux__)
+        root += "/out/host/linux-x86";
+#elif defined(__APPLE__)
+        root += "/out/host/darwin-x86";
+#else
+#error unsupported OS
+#endif
+        setenv("ANDROID_ROOT", root.c_str(), 1);
+      }
+    }
+    setenv("LD_LIBRARY_PATH", ":", 0);  // Required by java.lang.System.<clinit>.
+
+    // Not set by build server, so default
+    if (getenv("ANDROID_HOST_OUT") == nullptr) {
+      setenv("ANDROID_HOST_OUT", getenv("ANDROID_ROOT"), 1);
+    }
+  }
+}
+
+void CommonArtTestImpl::SetUpAndroidData(std::string& android_data) {
+  // On target, Cannot use /mnt/sdcard because it is mounted noexec, so use subdir of dalvik-cache
+  if (IsHost()) {
+    const char* tmpdir = getenv("TMPDIR");
+    if (tmpdir != nullptr && tmpdir[0] != 0) {
+      android_data = tmpdir;
+    } else {
+      android_data = "/tmp";
+    }
+  } else {
+    android_data = "/data/dalvik-cache";
+  }
+  android_data += "/art-data-XXXXXX";
+  if (mkdtemp(&android_data[0]) == nullptr) {
+    PLOG(FATAL) << "mkdtemp(\"" << &android_data[0] << "\") failed";
+  }
+  setenv("ANDROID_DATA", android_data.c_str(), 1);
+}
+
+void CommonArtTestImpl::SetUp() {
+  SetUpAndroidRoot();
+  SetUpAndroidData(android_data_);
+  dalvik_cache_.append(android_data_.c_str());
+  dalvik_cache_.append("/dalvik-cache");
+  int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700);
+  ASSERT_EQ(mkdir_result, 0);
+}
+
+void CommonArtTestImpl::TearDownAndroidData(const std::string& android_data, bool fail_on_error) {
+  if (fail_on_error) {
+    ASSERT_EQ(rmdir(android_data.c_str()), 0);
+  } else {
+    rmdir(android_data.c_str());
+  }
+}
+
+// Helper - find directory with the following format:
+// ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/
+std::string CommonArtTestImpl::GetAndroidToolsDir(const std::string& subdir1,
+                                                  const std::string& subdir2,
+                                                  const std::string& subdir3) {
+  std::string root;
+  const char* android_build_top = getenv("ANDROID_BUILD_TOP");
+  if (android_build_top != nullptr) {
+    root = android_build_top;
+  } else {
+    // Not set by build server, so default to current directory
+    char* cwd = getcwd(nullptr, 0);
+    setenv("ANDROID_BUILD_TOP", cwd, 1);
+    root = cwd;
+    free(cwd);
+  }
+
+  std::string toolsdir = root + "/" + subdir1;
+  std::string founddir;
+  DIR* dir;
+  if ((dir = opendir(toolsdir.c_str())) != nullptr) {
+    float maxversion = 0;
+    struct dirent* entry;
+    while ((entry = readdir(dir)) != nullptr) {
+      std::string format = subdir2 + "-%f";
+      float version;
+      if (std::sscanf(entry->d_name, format.c_str(), &version) == 1) {
+        if (version > maxversion) {
+          maxversion = version;
+          founddir = toolsdir + "/" + entry->d_name + "/" + subdir3 + "/bin/";
+        }
+      }
+    }
+    closedir(dir);
+  }
+
+  if (founddir.empty()) {
+    ADD_FAILURE() << "Cannot find Android tools directory.";
+  }
+  return founddir;
+}
+
+std::string CommonArtTestImpl::GetAndroidHostToolsDir() {
+  return GetAndroidToolsDir("prebuilts/gcc/linux-x86/host",
+                            "x86_64-linux-glibc2.15",
+                            "x86_64-linux");
+}
+
+std::string CommonArtTestImpl::GetCoreArtLocation() {
+  return GetCoreFileLocation("art");
+}
+
+std::string CommonArtTestImpl::GetCoreOatLocation() {
+  return GetCoreFileLocation("oat");
+}
+
+std::unique_ptr<const DexFile> CommonArtTestImpl::LoadExpectSingleDexFile(const char* location) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  MemMap::Init();
+  static constexpr bool kVerifyChecksum = true;
+  const ArtDexFileLoader dex_file_loader;
+  if (!dex_file_loader.Open(
+        location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)) {
+    LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n";
+    UNREACHABLE();
+  } else {
+    CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location;
+    return std::move(dex_files[0]);
+  }
+}
+
+void CommonArtTestImpl::ClearDirectory(const char* dirpath, bool recursive) {
+  ASSERT_TRUE(dirpath != nullptr);
+  DIR* dir = opendir(dirpath);
+  ASSERT_TRUE(dir != nullptr);
+  dirent* e;
+  struct stat s;
+  while ((e = readdir(dir)) != nullptr) {
+    if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) {
+      continue;
+    }
+    std::string filename(dirpath);
+    filename.push_back('/');
+    filename.append(e->d_name);
+    int stat_result = lstat(filename.c_str(), &s);
+    ASSERT_EQ(0, stat_result) << "unable to stat " << filename;
+    if (S_ISDIR(s.st_mode)) {
+      if (recursive) {
+        ClearDirectory(filename.c_str());
+        int rmdir_result = rmdir(filename.c_str());
+        ASSERT_EQ(0, rmdir_result) << filename;
+      }
+    } else {
+      int unlink_result = unlink(filename.c_str());
+      ASSERT_EQ(0, unlink_result) << filename;
+    }
+  }
+  closedir(dir);
+}
+
+void CommonArtTestImpl::TearDown() {
+  const char* android_data = getenv("ANDROID_DATA");
+  ASSERT_TRUE(android_data != nullptr);
+  ClearDirectory(dalvik_cache_.c_str());
+  int rmdir_cache_result = rmdir(dalvik_cache_.c_str());
+  ASSERT_EQ(0, rmdir_cache_result);
+  TearDownAndroidData(android_data_, true);
+  dalvik_cache_.clear();
+}
+
+static std::string GetDexFileName(const std::string& jar_prefix, bool host) {
+  std::string path;
+  if (host) {
+    const char* host_dir = getenv("ANDROID_HOST_OUT");
+    CHECK(host_dir != nullptr);
+    path = host_dir;
+  } else {
+    path = GetAndroidRoot();
+  }
+
+  std::string suffix = host
+      ? "-hostdex"                 // The host version.
+      : "-testdex";                // The unstripped target version.
+
+  return StringPrintf("%s/framework/%s%s.jar", path.c_str(), jar_prefix.c_str(), suffix.c_str());
+}
+
+std::vector<std::string> CommonArtTestImpl::GetLibCoreDexFileNames() {
+  return std::vector<std::string>({GetDexFileName("core-oj", IsHost()),
+                                   GetDexFileName("core-libart", IsHost())});
+}
+
+std::string CommonArtTestImpl::GetTestAndroidRoot() {
+  if (IsHost()) {
+    const char* host_dir = getenv("ANDROID_HOST_OUT");
+    CHECK(host_dir != nullptr);
+    return host_dir;
+  }
+  return GetAndroidRoot();
+}
+
+// Check that for target builds we have ART_TARGET_NATIVETEST_DIR set.
+#ifdef ART_TARGET
+#ifndef ART_TARGET_NATIVETEST_DIR
+#error "ART_TARGET_NATIVETEST_DIR not set."
+#endif
+// Wrap it as a string literal.
+#define ART_TARGET_NATIVETEST_DIR_STRING STRINGIFY(ART_TARGET_NATIVETEST_DIR) "/"
+#else
+#define ART_TARGET_NATIVETEST_DIR_STRING ""
+#endif
+
+std::string CommonArtTestImpl::GetTestDexFileName(const char* name) const {
+  CHECK(name != nullptr);
+  std::string filename;
+  if (IsHost()) {
+    filename += getenv("ANDROID_HOST_OUT");
+    filename += "/framework/";
+  } else {
+    filename += ART_TARGET_NATIVETEST_DIR_STRING;
+  }
+  filename += "art-gtest-";
+  filename += name;
+  filename += ".jar";
+  return filename;
+}
+
+std::vector<std::unique_ptr<const DexFile>> CommonArtTestImpl::OpenTestDexFiles(const char* name) {
+  std::string filename = GetTestDexFileName(name);
+  static constexpr bool kVerifyChecksum = true;
+  std::string error_msg;
+  const ArtDexFileLoader dex_file_loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  bool success = dex_file_loader.Open(filename.c_str(),
+                                      filename.c_str(),
+                                      /* verify */ true,
+                                      kVerifyChecksum,
+                                      &error_msg, &dex_files);
+  CHECK(success) << "Failed to open '" << filename << "': " << error_msg;
+  for (auto& dex_file : dex_files) {
+    CHECK_EQ(PROT_READ, dex_file->GetPermissions());
+    CHECK(dex_file->IsReadOnly());
+  }
+  return dex_files;
+}
+
+std::unique_ptr<const DexFile> CommonArtTestImpl::OpenTestDexFile(const char* name) {
+  std::vector<std::unique_ptr<const DexFile>> vector = OpenTestDexFiles(name);
+  EXPECT_EQ(1U, vector.size());
+  return std::move(vector[0]);
+}
+
+std::string CommonArtTestImpl::GetCoreFileLocation(const char* suffix) {
+  CHECK(suffix != nullptr);
+
+  std::string location;
+  if (IsHost()) {
+    const char* host_dir = getenv("ANDROID_HOST_OUT");
+    CHECK(host_dir != nullptr);
+    location = StringPrintf("%s/framework/core.%s", host_dir, suffix);
+  } else {
+    location = StringPrintf("/data/art-test/core.%s", suffix);
+  }
+
+  return location;
+}
+
+std::string CommonArtTestImpl::CreateClassPath(
+    const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+  CHECK(!dex_files.empty());
+  std::string classpath = dex_files[0]->GetLocation();
+  for (size_t i = 1; i < dex_files.size(); i++) {
+    classpath += ":" + dex_files[i]->GetLocation();
+  }
+  return classpath;
+}
+
+std::string CommonArtTestImpl::CreateClassPathWithChecksums(
+    const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+  CHECK(!dex_files.empty());
+  std::string classpath = dex_files[0]->GetLocation() + "*" +
+      std::to_string(dex_files[0]->GetLocationChecksum());
+  for (size_t i = 1; i < dex_files.size(); i++) {
+    classpath += ":" + dex_files[i]->GetLocation() + "*" +
+        std::to_string(dex_files[i]->GetLocationChecksum());
+  }
+  return classpath;
+}
+
+}  // namespace art
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
new file mode 100644
index 0000000..fe988a4
--- /dev/null
+++ b/libartbase/base/common_art_test.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2018 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_LIBARTBASE_BASE_COMMON_ART_TEST_H_
+#define ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+
+#include "base/globals.h"
+#include "base/mutex.h"
+#include "base/os.h"
+#include "base/unix_file/fd_file.h"
+#include "dex/art_dex_file_loader.h"
+#include "dex/compact_dex_level.h"
+
+namespace art {
+
+using LogSeverity = android::base::LogSeverity;
+using ScopedLogSeverity = android::base::ScopedLogSeverity;
+
+// OBJ pointer helpers to avoid needing .Decode everywhere.
+#define EXPECT_OBJ_PTR_EQ(a, b) EXPECT_EQ(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr());
+#define ASSERT_OBJ_PTR_EQ(a, b) ASSERT_EQ(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr());
+#define EXPECT_OBJ_PTR_NE(a, b) EXPECT_NE(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr());
+#define ASSERT_OBJ_PTR_NE(a, b) ASSERT_NE(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr());
+
+class DexFile;
+
+class ScratchFile {
+ public:
+  ScratchFile();
+
+  explicit ScratchFile(const std::string& filename);
+
+  ScratchFile(const ScratchFile& other, const char* suffix);
+
+  ScratchFile(ScratchFile&& other);
+
+  ScratchFile& operator=(ScratchFile&& other);
+
+  explicit ScratchFile(File* file);
+
+  ~ScratchFile();
+
+  const std::string& GetFilename() const {
+    return filename_;
+  }
+
+  File* GetFile() const {
+    return file_.get();
+  }
+
+  int GetFd() const;
+
+  void Close();
+  void Unlink();
+
+ private:
+  std::string filename_;
+  std::unique_ptr<File> file_;
+};
+
+class CommonArtTestImpl {
+ public:
+  CommonArtTestImpl() = default;
+  virtual ~CommonArtTestImpl() = default;
+
+  static void SetUpAndroidRoot();
+
+  // Note: setting up ANDROID_DATA may create a temporary directory. If this is used in a
+  // non-derived class, be sure to also call the corresponding tear-down below.
+  static void SetUpAndroidData(std::string& android_data);
+
+  static void TearDownAndroidData(const std::string& android_data, bool fail_on_error);
+
+  // Gets the paths of the libcore dex files.
+  static std::vector<std::string> GetLibCoreDexFileNames();
+
+  // Returns bin directory which contains host's prebuild tools.
+  static std::string GetAndroidHostToolsDir();
+
+  // Retuerns the filename for a test dex (i.e. XandY or ManyMethods).
+  std::string GetTestDexFileName(const char* name) const;
+
+  template <typename Mutator>
+  bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) {
+    std::vector<std::unique_ptr<const DexFile>> dex_files;
+    std::string error_msg;
+    const ArtDexFileLoader dex_file_loader;
+    CHECK(dex_file_loader.Open(input_jar.c_str(),
+                               input_jar.c_str(),
+                               /*verify*/ true,
+                               /*verify_checksum*/ true,
+                               &error_msg,
+                               &dex_files)) << error_msg;
+    EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported";
+    const std::unique_ptr<const DexFile>& dex = dex_files[0];
+    CHECK(dex->EnableWrite()) << "Failed to enable write";
+    DexFile* dex_file = const_cast<DexFile*>(dex.get());
+    mutator(dex_file);
+    const_cast<DexFile::Header&>(dex_file->GetHeader()).checksum_ = dex_file->CalculateChecksum();
+    if (!output_dex->WriteFully(dex->Begin(), dex->Size())) {
+      return false;
+    }
+    if (output_dex->Flush() != 0) {
+      PLOG(FATAL) << "Could not flush the output file.";
+    }
+    return true;
+  }
+
+ protected:
+  static bool IsHost() {
+    return !kIsTargetBuild;
+  }
+
+  // Helper - find directory with the following format:
+  // ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/
+  static std::string GetAndroidToolsDir(const std::string& subdir1,
+                                        const std::string& subdir2,
+                                        const std::string& subdir3);
+
+  // File location to core.art, e.g. $ANDROID_HOST_OUT/system/framework/core.art
+  static std::string GetCoreArtLocation();
+
+  // File location to core.oat, e.g. $ANDROID_HOST_OUT/system/framework/core.oat
+  static std::string GetCoreOatLocation();
+
+  std::unique_ptr<const DexFile> LoadExpectSingleDexFile(const char* location);
+
+  void ClearDirectory(const char* dirpath, bool recursive = true);
+
+  std::string GetTestAndroidRoot();
+
+  std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name);
+
+  std::unique_ptr<const DexFile> OpenTestDexFile(const char* name);
+
+
+  std::string android_data_;
+  std::string dalvik_cache_;
+
+  virtual void SetUp();
+
+  virtual void TearDown();
+
+  // Creates the class path string for the given dex files (the list of dex file locations
+  // separated by ':').
+  std::string CreateClassPath(const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+  // Same as CreateClassPath but add the dex file checksum after each location. The separator
+  // is '*'.
+  std::string CreateClassPathWithChecksums(
+      const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+
+  static std::string GetCoreFileLocation(const char* suffix);
+
+  std::vector<std::unique_ptr<const DexFile>> loaded_dex_files_;
+};
+
+template <typename TestType>
+class CommonArtTestBase : public TestType, public CommonArtTestImpl {
+ public:
+  CommonArtTestBase() {}
+  virtual ~CommonArtTestBase() {}
+
+ protected:
+  virtual void SetUp() OVERRIDE {
+    CommonArtTestImpl::SetUp();
+  }
+
+  virtual void TearDown() OVERRIDE {
+    CommonArtTestImpl::TearDown();
+  }
+};
+
+using CommonArtTest = CommonArtTestBase<testing::Test>;
+
+template <typename Param>
+using CommonArtTestWithParam = CommonArtTestBase<testing::TestWithParam<Param>>;
+
+#define TEST_DISABLED_FOR_TARGET() \
+  if (kIsTargetBuild) { \
+    printf("WARNING: TEST DISABLED FOR TARGET\n"); \
+    return; \
+  }
+
+#define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \
+  if (!kHostStaticBuildEnabled) { \
+    printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \
+    return; \
+  }
+
+#define TEST_DISABLED_FOR_MEMORY_TOOL() \
+  if (kRunningOnMemoryTool) { \
+    printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \
+    return; \
+  }
+
+#define TEST_DISABLED_FOR_HEAP_POISONING() \
+  if (kPoisonHeapReferences) { \
+    printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \
+    return; \
+  }
+}  // namespace art
+
+#endif  // ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_
diff --git a/libartbase/base/dumpable.h b/libartbase/base/dumpable.h
index 6621397..0c00505 100644
--- a/libartbase/base/dumpable.h
+++ b/libartbase/base/dumpable.h
@@ -19,7 +19,7 @@
 
 #include <ostream>
 
-#include "base/macros.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/file_magic.cc b/libartbase/base/file_magic.cc
index 2b9bed0..d8d843b 100644
--- a/libartbase/base/file_magic.cc
+++ b/libartbase/base/file_magic.cc
@@ -23,7 +23,7 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include "base/unix_file/fd_file.h"
+#include "unix_file/fd_file.h"
 
 namespace art {
 
diff --git a/libartbase/base/file_magic.h b/libartbase/base/file_magic.h
index 53f551c..0d0322c 100644
--- a/libartbase/base/file_magic.h
+++ b/libartbase/base/file_magic.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <string>
 
-#include "base/os.h"
+#include "os.h"
 
 namespace art {
 
diff --git a/runtime/base/file_utils.cc b/libartbase/base/file_utils.cc
similarity index 92%
rename from runtime/base/file_utils.cc
rename to libartbase/base/file_utils.cc
index 7b0796c..56934ac 100644
--- a/runtime/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -43,11 +43,10 @@
 #include "android-base/strings.h"
 
 #include "base/bit_utils.h"
-#include "base/stl_util.h"
+#include "base/globals.h"
 #include "base/os.h"
+#include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
-#include "dex/dex_file_loader.h"
-#include "globals.h"
 
 #if defined(__APPLE__)
 #include <crt_externs.h>
@@ -64,6 +63,8 @@
 using android::base::StringAppendF;
 using android::base::StringPrintf;
 
+static constexpr const char* kClassesDex = "classes.dex";
+
 bool ReadFileToString(const std::string& file_name, std::string* result) {
   File file(file_name, O_RDONLY, false);
   if (!file.IsOpened()) {
@@ -224,7 +225,7 @@
       !android::base::EndsWith(location, ".art") &&
       !android::base::EndsWith(location, ".oat")) {
     cache_file += "/";
-    cache_file += DexFileLoader::kClassesDex;
+    cache_file += kClassesDex;
   }
   std::replace(cache_file.begin(), cache_file.end(), '/', '@');
   *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
@@ -261,12 +262,13 @@
   }
 }
 
-bool LocationIsOnSystem(const char* location) {
-  UniqueCPtr<const char[]> path(realpath(location, nullptr));
-  return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str());
+bool LocationIsOnSystem(const char* path) {
+  UniqueCPtr<const char[]> full_path(realpath(path, nullptr));
+  return full_path != nullptr &&
+      android::base::StartsWith(full_path.get(), GetAndroidRoot().c_str());
 }
 
-bool LocationIsOnSystemFramework(const char* location) {
+bool LocationIsOnSystemFramework(const char* full_path) {
   std::string error_msg;
   std::string root_path = GetAndroidRootSafe(&error_msg);
   if (root_path.empty()) {
@@ -275,12 +277,7 @@
     return false;
   }
   std::string framework_path = root_path + "/framework/";
-
-  // Warning: Bionic implementation of realpath() allocates > 12KB on the stack.
-  // Do not run this code on a small stack, e.g. in signal handler.
-  UniqueCPtr<const char[]> path(realpath(location, nullptr));
-  return path != nullptr &&
-         android::base::StartsWith(path.get(), framework_path.c_str());
+  return android::base::StartsWith(full_path, framework_path);
 }
 
 }  // namespace art
diff --git a/runtime/base/file_utils.h b/libartbase/base/file_utils.h
similarity index 95%
rename from runtime/base/file_utils.h
rename to libartbase/base/file_utils.h
index d4f6c57..063393b 100644
--- a/runtime/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_FILE_UTILS_H_
-#define ART_RUNTIME_BASE_FILE_UTILS_H_
+#ifndef ART_LIBARTBASE_BASE_FILE_UTILS_H_
+#define ART_LIBARTBASE_BASE_FILE_UTILS_H_
 
 #include <stdlib.h>
 
@@ -46,6 +46,7 @@
 // Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
 // could not be found.
 std::string GetDalvikCache(const char* subdir);
+
 // Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
 // have_android_data will be set to true if we have an ANDROID_DATA that exists,
 // dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
@@ -79,4 +80,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_FILE_UTILS_H_
+#endif  // ART_LIBARTBASE_BASE_FILE_UTILS_H_
diff --git a/runtime/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc
similarity index 100%
rename from runtime/base/file_utils_test.cc
rename to libartbase/base/file_utils_test.cc
diff --git a/libartbase/base/globals.h b/libartbase/base/globals.h
index 69d1a64..39e0c50 100644
--- a/libartbase/base/globals.h
+++ b/libartbase/base/globals.h
@@ -38,6 +38,9 @@
 // compile-time constant so the compiler can generate better code.
 static constexpr int kPageSize = 4096;
 
+// Size of Dex virtual registers.
+static constexpr size_t kVRegSize = 4;
+
 // Returns whether the given memory offset can be used for generating
 // an implicit null check.
 static inline bool CanDoImplicitNullCheckOn(uintptr_t offset) {
diff --git a/libartbase/base/hex_dump.h b/libartbase/base/hex_dump.h
index 55f4d53..d13595d 100644
--- a/libartbase/base/hex_dump.h
+++ b/libartbase/base/hex_dump.h
@@ -17,7 +17,7 @@
 #ifndef ART_LIBARTBASE_BASE_HEX_DUMP_H_
 #define ART_LIBARTBASE_BASE_HEX_DUMP_H_
 
-#include "base/macros.h"
+#include "macros.h"
 
 #include <ostream>
 
diff --git a/libartbase/base/histogram-inl.h b/libartbase/base/histogram-inl.h
index 26d01b2..9832f03 100644
--- a/libartbase/base/histogram-inl.h
+++ b/libartbase/base/histogram-inl.h
@@ -26,9 +26,9 @@
 
 #include <android-base/logging.h>
 
-#include "base/bit_utils.h"
-#include "base/time_utils.h"
-#include "base/utils.h"
+#include "bit_utils.h"
+#include "time_utils.h"
+#include "utils.h"
 
 namespace art {
 
diff --git a/libartbase/base/indenter.h b/libartbase/base/indenter.h
index 850b7c4..06e7340 100644
--- a/libartbase/base/indenter.h
+++ b/libartbase/base/indenter.h
@@ -22,7 +22,7 @@
 
 #include <android-base/logging.h>
 
-#include "base/macros.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/leb128.h b/libartbase/base/leb128.h
index ab19daa..d5847fd 100644
--- a/libartbase/base/leb128.h
+++ b/libartbase/base/leb128.h
@@ -21,9 +21,9 @@
 
 #include <android-base/logging.h>
 
-#include "base/bit_utils.h"
-#include "base/globals.h"
-#include "base/macros.h"
+#include "bit_utils.h"
+#include "globals.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/leb128_test.cc b/libartbase/base/leb128_test.cc
index 747fc19..58b0d07 100644
--- a/libartbase/base/leb128_test.cc
+++ b/libartbase/base/leb128_test.cc
@@ -17,9 +17,8 @@
 #include "leb128.h"
 
 #include "gtest/gtest.h"
-
-#include "base/histogram-inl.h"
-#include "base/time_utils.h"
+#include "histogram-inl.h"
+#include "time_utils.h"
 
 namespace art {
 
diff --git a/libartbase/base/length_prefixed_array.h b/libartbase/base/length_prefixed_array.h
index 7c09bdd..9238e81 100644
--- a/libartbase/base/length_prefixed_array.h
+++ b/libartbase/base/length_prefixed_array.h
@@ -20,10 +20,10 @@
 #include <stddef.h>  // for offsetof()
 #include <string.h>  // for memset()
 
-#include "base/bit_utils.h"
-#include "base/casts.h"
-#include "base/iteration_range.h"
-#include "base/stride_iterator.h"
+#include "bit_utils.h"
+#include "casts.h"
+#include "iteration_range.h"
+#include "stride_iterator.h"
 
 namespace art {
 
diff --git a/libartbase/base/logging.cc b/libartbase/base/logging.cc
index fd2cc20..a66a7e3 100644
--- a/libartbase/base/logging.cc
+++ b/libartbase/base/logging.cc
@@ -21,8 +21,8 @@
 #include <sstream>
 
 #include "aborting.h"
-#include "base/os.h"
-#include "base/unix_file/fd_file.h"
+#include "os.h"
+#include "unix_file/fd_file.h"
 
 // Headers for LogMessage::LogLine.
 #ifdef ART_TARGET_ANDROID
diff --git a/libartbase/base/logging.h b/libartbase/base/logging.h
index 986704e..d2c0a02 100644
--- a/libartbase/base/logging.h
+++ b/libartbase/base/logging.h
@@ -21,7 +21,7 @@
 #include <sstream>
 
 #include "android-base/logging.h"
-#include "base/macros.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/logging_test.cc b/libartbase/base/logging_test.cc
index 1456eb3..46ba41b 100644
--- a/libartbase/base/logging_test.cc
+++ b/libartbase/base/logging_test.cc
@@ -19,9 +19,9 @@
 #include <type_traits>
 
 #include "android-base/logging.h"
-#include "base/bit_utils.h"
-#include "base/macros.h"
+#include "bit_utils.h"
 #include "gtest/gtest.h"
+#include "macros.h"
 #include "runtime_debug.h"
 
 namespace art {
diff --git a/libartbase/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc
index 7df4aef..15a5d71 100644
--- a/libartbase/base/malloc_arena_pool.cc
+++ b/libartbase/base/malloc_arena_pool.cc
@@ -24,7 +24,7 @@
 #include <numeric>
 
 #include <android-base/logging.h>
-#include "base/arena_allocator-inl.h"
+#include "arena_allocator-inl.h"
 
 namespace art {
 
@@ -53,7 +53,7 @@
     memory_ = unaligned_memory_;
   } else {
     memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment);
-    if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+    if (kRunningOnMemoryTool) {
       size_t head = memory_ - unaligned_memory_;
       size_t tail = overallocation - head;
       MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head);
@@ -66,7 +66,7 @@
 
 MallocArena::~MallocArena() {
   constexpr size_t overallocation = RequiredOverallocation();
-  if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+  if (overallocation != 0u && kRunningOnMemoryTool) {
     size_t head = memory_ - unaligned_memory_;
     size_t tail = overallocation - head;
     MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head);
@@ -132,7 +132,7 @@
 }
 
 void MallocArenaPool::FreeArenaChain(Arena* first) {
-  if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+  if (kRunningOnMemoryTool) {
     for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
       MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
     }
diff --git a/libartbase/base/malloc_arena_pool.h b/libartbase/base/malloc_arena_pool.h
index 8720189..c48be59 100644
--- a/libartbase/base/malloc_arena_pool.h
+++ b/libartbase/base/malloc_arena_pool.h
@@ -19,7 +19,7 @@
 
 #include <mutex>
 
-#include "base/arena_allocator.h"
+#include "arena_allocator.h"
 
 namespace art {
 
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index 21634f8..9ba1d6c 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -29,15 +29,14 @@
 
 #include "android-base/stringprintf.h"
 #include "android-base/unique_fd.h"
-#include "backtrace/BacktraceMap.h"
 #include "cutils/ashmem.h"
 
-#include "base/allocator.h"
-#include "base/bit_utils.h"
-#include "base/globals.h"
-#include "base/logging.h"  // For VLOG_IS_ON.
-#include "base/memory_tool.h"
-#include "base/utils.h"
+#include "allocator.h"
+#include "bit_utils.h"
+#include "globals.h"
+#include "logging.h"  // For VLOG_IS_ON.
+#include "memory_tool.h"
+#include "utils.h"
 
 #ifndef MAP_ANONYMOUS
 #define MAP_ANONYMOUS MAP_ANON
@@ -57,21 +56,6 @@
 // All the non-empty MemMaps. Use a multimap as we do a reserve-and-divide (eg ElfMap::Load()).
 static Maps* gMaps GUARDED_BY(MemMap::GetMemMapsLock()) = nullptr;
 
-static std::ostream& operator<<(
-    std::ostream& os,
-    std::pair<BacktraceMap::iterator, BacktraceMap::iterator> iters) {
-  for (BacktraceMap::iterator it = iters.first; it != iters.second; ++it) {
-    const backtrace_map_t* entry = *it;
-    os << StringPrintf("0x%08x-0x%08x %c%c%c %s\n",
-                       static_cast<uint32_t>(entry->start),
-                       static_cast<uint32_t>(entry->end),
-                       (entry->flags & PROT_READ) ? 'r' : '-',
-                       (entry->flags & PROT_WRITE) ? 'w' : '-',
-                       (entry->flags & PROT_EXEC) ? 'x' : '-', entry->name.c_str());
-  }
-  return os;
-}
-
 std::ostream& operator<<(std::ostream& os, const Maps& mem_maps) {
   os << "MemMap:" << std::endl;
   for (auto it = mem_maps.begin(); it != mem_maps.end(); ++it) {
@@ -149,8 +133,6 @@
   uintptr_t begin = reinterpret_cast<uintptr_t>(ptr);
   uintptr_t end = begin + size;
 
-  // There is a suspicion that BacktraceMap::Create is occasionally missing maps. TODO: Investigate
-  // further.
   {
     std::lock_guard<std::mutex> mu(*mem_maps_lock_);
     for (auto& pair : *gMaps) {
@@ -162,22 +144,6 @@
     }
   }
 
-  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
-  if (map == nullptr) {
-    if (error_msg != nullptr) {
-      *error_msg = StringPrintf("Failed to build process map");
-    }
-    return false;
-  }
-
-  ScopedBacktraceMapIteratorLock lock(map.get());
-  for (BacktraceMap::iterator it = map->begin(); it != map->end(); ++it) {
-    const backtrace_map_t* entry = *it;
-    if ((begin >= entry->start && begin < entry->end)     // start of new within old
-        && (end > entry->start && end <= entry->end)) {   // end of new within old
-      return true;
-    }
-  }
   if (error_msg != nullptr) {
     PrintFileToLog("/proc/self/maps", LogSeverity::ERROR);
     *error_msg = StringPrintf("Requested region 0x%08" PRIxPTR "-0x%08" PRIxPTR " does not overlap "
@@ -186,36 +152,6 @@
   return false;
 }
 
-// Return true if the address range does not conflict with any /proc/self/maps entry.
-static bool CheckNonOverlapping(uintptr_t begin,
-                                uintptr_t end,
-                                std::string* error_msg) {
-  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
-  if (map.get() == nullptr) {
-    *error_msg = StringPrintf("Failed to build process map");
-    return false;
-  }
-  ScopedBacktraceMapIteratorLock lock(map.get());
-  for (BacktraceMap::iterator it = map->begin(); it != map->end(); ++it) {
-    const backtrace_map_t* entry = *it;
-    if ((begin >= entry->start && begin < entry->end)      // start of new within old
-        || (end > entry->start && end < entry->end)        // end of new within old
-        || (begin <= entry->start && end > entry->end)) {  // start/end of new includes all of old
-      std::ostringstream map_info;
-      map_info << std::make_pair(it, map->end());
-      *error_msg = StringPrintf("Requested region 0x%08" PRIxPTR "-0x%08" PRIxPTR " overlaps with "
-                                "existing map 0x%08" PRIxPTR "-0x%08" PRIxPTR " (%s)\n%s",
-                                begin, end,
-                                static_cast<uintptr_t>(entry->start),
-                                static_cast<uintptr_t>(entry->end),
-                                entry->name.c_str(),
-                                map_info.str().c_str());
-      return false;
-    }
-  }
-  return true;
-}
-
 // CheckMapRequest to validate a non-MAP_FAILED mmap result based on
 // the expected value, calling munmap if validation fails, giving the
 // reason in error_msg.
@@ -236,7 +172,6 @@
 
   uintptr_t actual = reinterpret_cast<uintptr_t>(actual_ptr);
   uintptr_t expected = reinterpret_cast<uintptr_t>(expected_ptr);
-  uintptr_t limit = expected + byte_count;
 
   if (expected_ptr == actual_ptr) {
     return true;
@@ -256,15 +191,16 @@
     //   true, even if there is no overlap
     // - There might have been an overlap at the point of mmap, but the
     //   overlapping region has since been unmapped.
-    std::string error_detail;
-    CheckNonOverlapping(expected, limit, &error_detail);
+
+    // Tell the client the mappings that were in place at the time.
+    if (kIsDebugBuild) {
+      PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+    }
+
     std::ostringstream os;
     os <<  StringPrintf("Failed to mmap at expected address, mapped at "
                         "0x%08" PRIxPTR " instead of 0x%08" PRIxPTR,
                         actual, expected);
-    if (!error_detail.empty()) {
-      os << " : " << error_detail;
-    }
     *error_msg = os.str();
   }
   return false;
@@ -524,7 +460,7 @@
       (expected_ptr == nullptr) ? nullptr : (expected_ptr - page_offset);
 
   size_t redzone_size = 0;
-  if (RUNNING_ON_MEMORY_TOOL && kMemoryToolAddsRedzones && expected_ptr == nullptr) {
+  if (kRunningOnMemoryTool && kMemoryToolAddsRedzones && expected_ptr == nullptr) {
     redzone_size = kPageSize;
     page_aligned_byte_count += redzone_size;
   }
@@ -713,9 +649,11 @@
 bool MemMap::Sync() {
   bool result;
   if (redzone_size_ != 0) {
-    // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing
-    // it to msync() as it only accepts page-aligned base address, and exclude the higher-end
-    // noaccess protection from the msync range. b/27552451.
+    // To avoid errors when running on a memory tool, temporarily lift the lower-end noaccess
+    // protection before passing it to msync() as it only accepts page-aligned base address,
+    // and exclude the higher-end noaccess protection from the msync range. b/27552451.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether this special case is needed for ASan.
     uint8_t* base_begin = reinterpret_cast<uint8_t*>(base_begin_);
     MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin);
     result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0;
diff --git a/libartbase/base/mem_map.h b/libartbase/base/mem_map.h
index b7beb08..3a324b2 100644
--- a/libartbase/base/mem_map.h
+++ b/libartbase/base/mem_map.h
@@ -25,7 +25,7 @@
 #include <string>
 
 #include "android-base/thread_annotations.h"
-#include "base/macros.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc
index 3adbf18..4a78bdc 100644
--- a/libartbase/base/mem_map_test.cc
+++ b/libartbase/base/mem_map_test.cc
@@ -21,13 +21,14 @@
 #include <memory>
 #include <random>
 
-#include "base/memory_tool.h"
-#include "base/unix_file/fd_file.h"
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
+#include "common_runtime_test.h"  // For TEST_DISABLED_FOR_MIPS
+#include "memory_tool.h"
+#include "unix_file/fd_file.h"
 
 namespace art {
 
-class MemMapTest : public CommonRuntimeTest {
+class MemMapTest : public CommonArtTest {
  public:
   static uint8_t* BaseBegin(MemMap* mem_map) {
     return reinterpret_cast<uint8_t*>(mem_map->base_begin_);
@@ -470,31 +471,33 @@
   // cannot allocate in the 2GB-4GB region.
   TEST_DISABLED_FOR_MIPS();
 
+  // This test may not work under Valgrind.
+  // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+  // check whether this test works with ASan.
+  TEST_DISABLED_FOR_MEMORY_TOOL();
+
   CommonInit();
-  // This test may not work under valgrind.
-  if (RUNNING_ON_MEMORY_TOOL == 0) {
-    constexpr size_t size = 0x100000;
-    // Try all addresses starting from 2GB to 4GB.
-    size_t start_addr = 2 * GB;
-    std::string error_msg;
-    std::unique_ptr<MemMap> map;
-    for (; start_addr <= std::numeric_limits<uint32_t>::max() - size; start_addr += size) {
-      map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
-                                     reinterpret_cast<uint8_t*>(start_addr),
-                                     size,
-                                     PROT_READ | PROT_WRITE,
-                                     /*low_4gb*/true,
-                                     false,
-                                     &error_msg));
-      if (map != nullptr) {
-        break;
-      }
+  constexpr size_t size = 0x100000;
+  // Try all addresses starting from 2GB to 4GB.
+  size_t start_addr = 2 * GB;
+  std::string error_msg;
+  std::unique_ptr<MemMap> map;
+  for (; start_addr <= std::numeric_limits<uint32_t>::max() - size; start_addr += size) {
+    map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
+                                   reinterpret_cast<uint8_t*>(start_addr),
+                                   size,
+                                   PROT_READ | PROT_WRITE,
+                                   /*low_4gb*/true,
+                                   false,
+                                   &error_msg));
+    if (map != nullptr) {
+      break;
     }
-    ASSERT_TRUE(map.get() != nullptr) << error_msg;
-    ASSERT_GE(reinterpret_cast<uintptr_t>(map->End()), 2u * GB);
-    ASSERT_TRUE(error_msg.empty());
-    ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast<void*>(start_addr));
   }
+  ASSERT_TRUE(map.get() != nullptr) << error_msg;
+  ASSERT_GE(reinterpret_cast<uintptr_t>(map->End()), 2u * GB);
+  ASSERT_TRUE(error_msg.empty());
+  ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast<void*>(start_addr));
 }
 
 TEST_F(MemMapTest, MapAnonymousOverflow) {
diff --git a/libartbase/base/memory_region.cc b/libartbase/base/memory_region.cc
index 862ff73..d207872 100644
--- a/libartbase/base/memory_region.cc
+++ b/libartbase/base/memory_region.cc
@@ -29,36 +29,4 @@
   memmove(reinterpret_cast<void*>(begin() + offset), from.pointer(), from.size());
 }
 
-void MemoryRegion::StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) {
-  DCHECK_LE(value, MaxInt<uint32_t>(length));
-  DCHECK_LE(length, BitSizeOf<uint32_t>());
-  DCHECK_LE(bit_offset + length, size_in_bits());
-  if (length == 0) {
-    return;
-  }
-  // Bits are stored in this order {7 6 5 4 3 2 1 0}.
-  // How many remaining bits in current byte is (bit_offset % kBitsPerByte) + 1.
-  uint8_t* out = ComputeInternalPointer<uint8_t>(bit_offset >> kBitsPerByteLog2);
-  size_t orig_len = length;
-  uint32_t orig_value = value;
-  uintptr_t bit_remainder = bit_offset % kBitsPerByte;
-  while (true) {
-    const uintptr_t remaining_bits = kBitsPerByte - bit_remainder;
-    if (length <= remaining_bits) {
-      // Length is smaller than all of remainder bits.
-      size_t mask = ((1 << length) - 1) << bit_remainder;
-      *out = (*out & ~mask) | (value << bit_remainder);
-      break;
-    }
-    // Copy remaining bits in current byte.
-    size_t value_mask = (1 << remaining_bits) - 1;
-    *out = (*out & ~(value_mask << bit_remainder)) | ((value & value_mask) << bit_remainder);
-    value >>= remaining_bits;
-    bit_remainder = 0;
-    length -= remaining_bits;
-    ++out;
-  }
-  DCHECK_EQ(LoadBits(bit_offset, orig_len), orig_value) << bit_offset << " " << orig_len;
-}
-
 }  // namespace art
diff --git a/libartbase/base/memory_region.h b/libartbase/base/memory_region.h
index 7add466..2060329 100644
--- a/libartbase/base/memory_region.h
+++ b/libartbase/base/memory_region.h
@@ -22,12 +22,12 @@
 
 #include <android-base/logging.h>
 
-#include "base/bit_utils.h"
-#include "base/casts.h"
-#include "base/enums.h"
-#include "base/macros.h"
-#include "base/value_object.h"
+#include "bit_utils.h"
+#include "casts.h"
+#include "enums.h"
 #include "globals.h"
+#include "macros.h"
+#include "value_object.h"
 
 namespace art {
 
@@ -109,67 +109,6 @@
     return ComputeInternalPointer<T>(offset);
   }
 
-  // Load a single bit in the region. The bit at offset 0 is the least
-  // significant bit in the first byte.
-  ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const {
-    uint8_t bit_mask;
-    uint8_t byte = *ComputeBitPointer(bit_offset, &bit_mask);
-    return byte & bit_mask;
-  }
-
-  ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const {
-    uint8_t bit_mask;
-    uint8_t* byte = ComputeBitPointer(bit_offset, &bit_mask);
-    if (value) {
-      *byte |= bit_mask;
-    } else {
-      *byte &= ~bit_mask;
-    }
-  }
-
-  // Load `length` bits from the region starting at bit offset `bit_offset`.
-  // The bit at the smallest offset is the least significant bit in the
-  // loaded value.  `length` must not be larger than the number of bits
-  // contained in the return value (32).
-  ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const {
-    DCHECK_LE(length, BitSizeOf<uint32_t>());
-    DCHECK_LE(bit_offset + length, size_in_bits());
-    if (UNLIKELY(length == 0)) {
-      // Do not touch any memory if the range is empty.
-      return 0;
-    }
-    const uint8_t* address = begin() + bit_offset / kBitsPerByte;
-    const uint32_t shift = bit_offset & (kBitsPerByte - 1);
-    // Load the value (reading only the strictly needed bytes).
-    const uint32_t load_bit_count = shift + length;
-    uint32_t value = address[0] >> shift;
-    if (load_bit_count > 8) {
-      value |= static_cast<uint32_t>(address[1]) << (8 - shift);
-      if (load_bit_count > 16) {
-        value |= static_cast<uint32_t>(address[2]) << (16 - shift);
-        if (load_bit_count > 24) {
-          value |= static_cast<uint32_t>(address[3]) << (24 - shift);
-          if (load_bit_count > 32) {
-            value |= static_cast<uint32_t>(address[4]) << (32 - shift);
-          }
-        }
-      }
-    }
-    // Clear unwanted most significant bits.
-    uint32_t clear_bit_count = BitSizeOf(value) - length;
-    value = (value << clear_bit_count) >> clear_bit_count;
-    for (size_t i = 0; i < length; ++i) {
-      DCHECK_EQ((value >> i) & 1, LoadBit(bit_offset + i));
-    }
-    return value;
-  }
-
-  // Store `value` on `length` bits in the region starting at bit offset
-  // `bit_offset`.  The bit at the smallest offset is the least significant
-  // bit of the stored `value`.  `value` must not be larger than `length`
-  // bits.
-  void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length);
-
   void CopyFrom(size_t offset, const MemoryRegion& from) const;
 
   template<class Vector>
diff --git a/libartbase/base/memory_region_test.cc b/libartbase/base/memory_region_test.cc
index e3aead4..72e03a4 100644
--- a/libartbase/base/memory_region_test.cc
+++ b/libartbase/base/memory_region_test.cc
@@ -18,8 +18,6 @@
 
 #include "gtest/gtest.h"
 
-#include "bit_memory_region.h"
-
 namespace art {
 
 TEST(MemoryRegion, LoadUnaligned) {
@@ -57,35 +55,4 @@
   }
 }
 
-TEST(MemoryRegion, TestBits) {
-  const size_t n = 8;
-  uint8_t data[n] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-  MemoryRegion region(&data, n);
-  uint32_t value = 0xDEADBEEF;
-  // Try various offsets and lengths.
-  for (size_t bit_offset = 0; bit_offset < 2 * kBitsPerByte; ++bit_offset) {
-    for (size_t length = 0; length < 2 * kBitsPerByte; ++length) {
-      const uint32_t length_mask = (1 << length) - 1;
-      uint32_t masked_value = value & length_mask;
-      BitMemoryRegion bmr(region, bit_offset, length);
-      region.StoreBits(bit_offset, masked_value, length);
-      EXPECT_EQ(region.LoadBits(bit_offset, length), masked_value);
-      EXPECT_EQ(bmr.LoadBits(0, length), masked_value);
-      // Check adjacent bits to make sure they were not incorrectly cleared.
-      EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1);
-      EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask);
-      region.StoreBits(bit_offset, length_mask, length);
-      // Store with bit memory region.
-      bmr.StoreBits(0, masked_value, length);
-      EXPECT_EQ(bmr.LoadBits(0, length), masked_value);
-      // Check adjacent bits to make sure they were not incorrectly cleared.
-      EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1);
-      EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask);
-      region.StoreBits(bit_offset, length_mask, length);
-      // Flip the value to try different edge bit combinations.
-      value = ~value;
-    }
-  }
-}
-
 }  // namespace art
diff --git a/libartbase/base/memory_tool.h b/libartbase/base/memory_tool.h
index e1df99f..d381f01 100644
--- a/libartbase/base/memory_tool.h
+++ b/libartbase/base/memory_tool.h
@@ -19,53 +19,53 @@
 
 #include <stddef.h>
 
+namespace art {
+
 #if !defined(__has_feature)
-#define __has_feature(x) 0
+# define __has_feature(x) 0
 #endif
 
 #if __has_feature(address_sanitizer)
 
-#include <sanitizer/asan_interface.h>
-#define ADDRESS_SANITIZER
+# include <sanitizer/asan_interface.h>
+# define ADDRESS_SANITIZER
 
-#ifdef ART_ENABLE_ADDRESS_SANITIZER
-#define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s)
-#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s)
-#define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s)
+# ifdef ART_ENABLE_ADDRESS_SANITIZER
+#  define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s)
+#  define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s)
+#  define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s)
 constexpr bool kMemoryToolIsAvailable = true;
-#else
-#define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0)
-#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0)
-#define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+# else
+#  define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0)
+#  define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+#  define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0)
 constexpr bool kMemoryToolIsAvailable = false;
-#endif
+# endif
 
 extern "C" void __asan_handle_no_return();
 
-#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
-#define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return()
-#define RUNNING_ON_MEMORY_TOOL 1U
-constexpr bool kMemoryToolIsValgrind = false;
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
+# define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return()
+constexpr bool kRunningOnMemoryTool = true;
 constexpr bool kMemoryToolDetectsLeaks = true;
 constexpr bool kMemoryToolAddsRedzones = true;
 constexpr size_t kMemoryToolStackGuardSizeScale = 2;
 
 #else
 
-#include <memcheck/memcheck.h>
-#include <valgrind.h>
-#define MEMORY_TOOL_MAKE_NOACCESS(p, s) VALGRIND_MAKE_MEM_NOACCESS(p, s)
-#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s)
-#define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s)
-#define ATTRIBUTE_NO_SANITIZE_ADDRESS
-#define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0)
-#define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND
-constexpr bool kMemoryToolIsAvailable = true;
-constexpr bool kMemoryToolIsValgrind = true;
-constexpr bool kMemoryToolDetectsLeaks = true;
-constexpr bool kMemoryToolAddsRedzones = true;
+# define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0)
+# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+# define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS
+# define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0)
+constexpr bool kRunningOnMemoryTool = false;
+constexpr bool kMemoryToolIsAvailable = false;
+constexpr bool kMemoryToolDetectsLeaks = false;
+constexpr bool kMemoryToolAddsRedzones = false;
 constexpr size_t kMemoryToolStackGuardSizeScale = 1;
 
 #endif
 
+}  // namespace art
+
 #endif  // ART_LIBARTBASE_BASE_MEMORY_TOOL_H_
diff --git a/libartbase/base/os_linux.cc b/libartbase/base/os_linux.cc
index cb228bd..f8b31cf 100644
--- a/libartbase/base/os_linux.cc
+++ b/libartbase/base/os_linux.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "base/os.h"
+#include "os.h"
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -25,7 +25,7 @@
 
 #include <android-base/logging.h>
 
-#include "base/unix_file/fd_file.h"
+#include "unix_file/fd_file.h"
 
 namespace art {
 
diff --git a/libartbase/base/safe_copy.cc b/libartbase/base/safe_copy.cc
index 7ba5cbd..b46b921 100644
--- a/libartbase/base/safe_copy.cc
+++ b/libartbase/base/safe_copy.cc
@@ -24,7 +24,7 @@
 
 #include <android-base/macros.h>
 
-#include "base/bit_utils.h"
+#include "bit_utils.h"
 
 namespace art {
 
diff --git a/libartbase/base/safe_copy_test.cc b/libartbase/base/safe_copy_test.cc
index f1d7c55..c23651f 100644
--- a/libartbase/base/safe_copy_test.cc
+++ b/libartbase/base/safe_copy_test.cc
@@ -22,7 +22,7 @@
 #include <sys/user.h>
 
 #include "android-base/logging.h"
-#include "base/globals.h"
+#include "globals.h"
 #include "gtest/gtest.h"
 
 
diff --git a/libartbase/base/scoped_arena_allocator.cc b/libartbase/base/scoped_arena_allocator.cc
index 7240842..ab05c60 100644
--- a/libartbase/base/scoped_arena_allocator.cc
+++ b/libartbase/base/scoped_arena_allocator.cc
@@ -17,7 +17,7 @@
 #include "scoped_arena_allocator.h"
 
 #include "arena_allocator-inl.h"
-#include "base/memory_tool.h"
+#include "memory_tool.h"
 
 namespace art {
 
diff --git a/libartbase/base/scoped_arena_allocator.h b/libartbase/base/scoped_arena_allocator.h
index d5f6df8..7eaec5e 100644
--- a/libartbase/base/scoped_arena_allocator.h
+++ b/libartbase/base/scoped_arena_allocator.h
@@ -20,9 +20,9 @@
 #include <android-base/logging.h>
 
 #include "arena_allocator.h"
-#include "base/debug_stack.h"
-#include "base/globals.h"
-#include "base/macros.h"
+#include "debug_stack.h"
+#include "globals.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h
index 4df02b6..679bcc0 100644
--- a/libartbase/base/scoped_arena_containers.h
+++ b/libartbase/base/scoped_arena_containers.h
@@ -25,8 +25,8 @@
 #include <utility>
 
 #include "arena_containers.h"  // For ArenaAllocatorAdapterKind.
-#include "base/dchecked_vector.h"
-#include "base/safe_map.h"
+#include "dchecked_vector.h"
+#include "safe_map.h"
 #include "scoped_arena_allocator.h"
 
 namespace art {
@@ -228,7 +228,7 @@
  protected:
   // Used for variable sized objects such as RegisterLine.
   ALWAYS_INLINE void ProtectMemory(T* ptr, size_t size) const {
-    if (RUNNING_ON_MEMORY_TOOL > 0) {
+    if (kRunningOnMemoryTool) {
       // Writing to the memory will fail ift we already destroyed the pointer with
       // DestroyOnlyDelete since we make it no access.
       memset(ptr, kMagicFill, size);
diff --git a/libartbase/base/scoped_flock.cc b/libartbase/base/scoped_flock.cc
index 514b97b..d679328 100644
--- a/libartbase/base/scoped_flock.cc
+++ b/libartbase/base/scoped_flock.cc
@@ -22,7 +22,7 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include "base/unix_file/fd_file.h"
+#include "unix_file/fd_file.h"
 
 namespace art {
 
diff --git a/libartbase/base/scoped_flock.h b/libartbase/base/scoped_flock.h
index 476b257..39b36b4 100644
--- a/libartbase/base/scoped_flock.h
+++ b/libartbase/base/scoped_flock.h
@@ -22,9 +22,9 @@
 
 #include <android-base/unique_fd.h>
 
-#include "base/macros.h"
-#include "base/os.h"
-#include "base/unix_file/fd_file.h"
+#include "macros.h"
+#include "os.h"
+#include "unix_file/fd_file.h"
 
 namespace art {
 
diff --git a/libartbase/base/scoped_flock_test.cc b/libartbase/base/scoped_flock_test.cc
index 1b6caaf..f9ac1e0 100644
--- a/libartbase/base/scoped_flock_test.cc
+++ b/libartbase/base/scoped_flock_test.cc
@@ -16,11 +16,11 @@
 
 #include "scoped_flock.h"
 
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
 
 namespace art {
 
-class ScopedFlockTest : public CommonRuntimeTest {};
+class ScopedFlockTest : public CommonArtTest {};
 
 TEST_F(ScopedFlockTest, TestLocking) {
   ScratchFile scratch_file;
diff --git a/libartbase/base/time_utils.cc b/libartbase/base/time_utils.cc
index 3c09d5a..89a1109 100644
--- a/libartbase/base/time_utils.cc
+++ b/libartbase/base/time_utils.cc
@@ -22,7 +22,7 @@
 
 #include "android-base/stringprintf.h"
 
-#include "base/logging.h"
+#include "logging.h"
 
 #if defined(__APPLE__)
 #include <sys/time.h>
diff --git a/libartbase/base/time_utils.h b/libartbase/base/time_utils.h
index 811af5d..431d3e1 100644
--- a/libartbase/base/time_utils.h
+++ b/libartbase/base/time_utils.h
@@ -22,7 +22,7 @@
 
 #include <string>
 
-#include "base/macros.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/tracking_safe_map.h b/libartbase/base/tracking_safe_map.h
index 2750de1..9b015c4 100644
--- a/libartbase/base/tracking_safe_map.h
+++ b/libartbase/base/tracking_safe_map.h
@@ -17,8 +17,8 @@
 #ifndef ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_
 #define ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_
 
-#include "base/allocator.h"
-#include "base/safe_map.h"
+#include "allocator.h"
+#include "safe_map.h"
 
 namespace art {
 
diff --git a/libartbase/base/transform_array_ref.h b/libartbase/base/transform_array_ref.h
index de2739e..ef29573 100644
--- a/libartbase/base/transform_array_ref.h
+++ b/libartbase/base/transform_array_ref.h
@@ -19,8 +19,8 @@
 
 #include <type_traits>
 
-#include "base/array_ref.h"
-#include "base/transform_iterator.h"
+#include "array_ref.h"
+#include "transform_iterator.h"
 
 namespace art {
 
diff --git a/libartbase/base/transform_array_ref_test.cc b/libartbase/base/transform_array_ref_test.cc
index da0340d..fc73d56 100644
--- a/libartbase/base/transform_array_ref_test.cc
+++ b/libartbase/base/transform_array_ref_test.cc
@@ -18,8 +18,7 @@
 #include <vector>
 
 #include "gtest/gtest.h"
-
-#include "base/transform_array_ref.h"
+#include "transform_array_ref.h"
 
 namespace art {
 
diff --git a/libartbase/base/transform_iterator.h b/libartbase/base/transform_iterator.h
index 82d9f9e..9265543 100644
--- a/libartbase/base/transform_iterator.h
+++ b/libartbase/base/transform_iterator.h
@@ -20,7 +20,7 @@
 #include <iterator>
 #include <type_traits>
 
-#include "base/iteration_range.h"
+#include "iteration_range.h"
 
 namespace art {
 
diff --git a/libartbase/base/transform_iterator_test.cc b/libartbase/base/transform_iterator_test.cc
index 63b6e4f..5a5c37d 100644
--- a/libartbase/base/transform_iterator_test.cc
+++ b/libartbase/base/transform_iterator_test.cc
@@ -21,8 +21,7 @@
 #include <vector>
 
 #include "gtest/gtest.h"
-
-#include "base/transform_iterator.h"
+#include "transform_iterator.h"
 
 namespace art {
 
diff --git a/libartbase/base/unix_file/fd_file.cc b/libartbase/base/unix_file/fd_file.cc
index b2881b8..c5313e9 100644
--- a/libartbase/base/unix_file/fd_file.cc
+++ b/libartbase/base/unix_file/fd_file.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "base/unix_file/fd_file.h"
+#include "fd_file.h"
 
 #include <errno.h>
 #include <sys/stat.h>
@@ -30,8 +30,8 @@
 #include <sys/sendfile.h>
 #else
 #include <algorithm>
-#include "base/stl_util.h"
 #include "base/globals.h"
+#include "base/stl_util.h"
 #endif
 
 namespace unix_file {
diff --git a/libartbase/base/unix_file/fd_file.h b/libartbase/base/unix_file/fd_file.h
index fe3317f..d61dab6 100644
--- a/libartbase/base/unix_file/fd_file.h
+++ b/libartbase/base/unix_file/fd_file.h
@@ -22,7 +22,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "base/unix_file/random_access_file.h"
+#include "random_access_file.h"
 
 namespace unix_file {
 
diff --git a/libartbase/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc
index 042fbc9..1f731a7 100644
--- a/libartbase/base/unix_file/fd_file_test.cc
+++ b/libartbase/base/unix_file/fd_file_test.cc
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include "base/unix_file/fd_file.h"
-#include "base/unix_file/random_access_file_test.h"
-#include "common_runtime_test.h"  // For ScratchFile
+#include "base/common_art_test.h"  // For ScratchFile
 #include "gtest/gtest.h"
+#include "fd_file.h"
+#include "random_access_file_test.h"
 
 namespace unix_file {
 
diff --git a/libartbase/base/unix_file/random_access_file_test.h b/libartbase/base/unix_file/random_access_file_test.h
index 1de5f7b..dbe6ca9 100644
--- a/libartbase/base/unix_file/random_access_file_test.h
+++ b/libartbase/base/unix_file/random_access_file_test.h
@@ -21,7 +21,7 @@
 #include <memory>
 #include <string>
 
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
 
 namespace unix_file {
 
@@ -35,11 +35,11 @@
   virtual RandomAccessFile* MakeTestFile() = 0;
 
   virtual void SetUp() {
-    art::CommonRuntimeTest::SetUpAndroidData(android_data_);
+    art::CommonArtTest::SetUpAndroidData(android_data_);
   }
 
   virtual void TearDown() {
-    art::CommonRuntimeTest::TearDownAndroidData(android_data_, true);
+    art::CommonArtTest::TearDownAndroidData(android_data_, true);
   }
 
   std::string GetTmpPath(const std::string& name) {
diff --git a/libartbase/base/unix_file/random_access_file_utils.cc b/libartbase/base/unix_file/random_access_file_utils.cc
index aae65c1..10d8299 100644
--- a/libartbase/base/unix_file/random_access_file_utils.cc
+++ b/libartbase/base/unix_file/random_access_file_utils.cc
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "base/unix_file/random_access_file_utils.h"
+#include "random_access_file_utils.h"
 
 #include <vector>
 
-#include "base/unix_file/random_access_file.h"
+#include "random_access_file.h"
 
 namespace unix_file {
 
diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc
index 029cffd..b7a542f 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -30,7 +30,7 @@
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
-#include "base/os.h"
+#include "os.h"
 
 #if defined(__APPLE__)
 #include <crt_externs.h>
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index c8c5b72..73c1c22 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -25,11 +25,11 @@
 
 #include <android-base/logging.h>
 
-#include "base/casts.h"
-#include "base/enums.h"
-#include "base/globals.h"
-#include "base/macros.h"
-#include "base/stringpiece.h"
+#include "casts.h"
+#include "enums.h"
+#include "globals.h"
+#include "macros.h"
+#include "stringpiece.h"
 
 namespace art {
 
diff --git a/libartbase/base/value_object.h b/libartbase/base/value_object.h
index 441bd1a..dab6b76 100644
--- a/libartbase/base/value_object.h
+++ b/libartbase/base/value_object.h
@@ -17,7 +17,7 @@
 #ifndef ART_LIBARTBASE_BASE_VALUE_OBJECT_H_
 #define ART_LIBARTBASE_BASE_VALUE_OBJECT_H_
 
-#include "base/macros.h"
+#include "macros.h"
 
 namespace art {
 
diff --git a/libartbase/base/variant_map.h b/libartbase/base/variant_map.h
index 4e02c54..581bc23 100644
--- a/libartbase/base/variant_map.h
+++ b/libartbase/base/variant_map.h
@@ -23,7 +23,7 @@
 #include <utility>
 
 #include "android-base/logging.h"
-#include "base/stl_util_identity.h"
+#include "stl_util_identity.h"
 
 namespace art {
 
diff --git a/libartbase/base/zip_archive.cc b/libartbase/base/zip_archive.cc
index 4185c22..b5f946e 100644
--- a/libartbase/base/zip_archive.cc
+++ b/libartbase/base/zip_archive.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "base/zip_archive.h"
+#include "zip_archive.h"
 
 #include <fcntl.h>
 #include <stdio.h>
@@ -27,8 +27,8 @@
 #include "android-base/stringprintf.h"
 #include "ziparchive/zip_archive.h"
 
-#include "base/bit_utils.h"
-#include "base/unix_file/fd_file.h"
+#include "bit_utils.h"
+#include "unix_file/fd_file.h"
 
 namespace art {
 
diff --git a/libartbase/base/zip_archive.h b/libartbase/base/zip_archive.h
index 39c8168..73495da 100644
--- a/libartbase/base/zip_archive.h
+++ b/libartbase/base/zip_archive.h
@@ -23,11 +23,11 @@
 
 #include <android-base/logging.h>
 
-#include "base/os.h"
-#include "base/mem_map.h"
-#include "base/safe_map.h"
-#include "base/unix_file/random_access_file.h"
 #include "globals.h"
+#include "mem_map.h"
+#include "os.h"
+#include "safe_map.h"
+#include "unix_file/random_access_file.h"
 
 // system/core/zip_archive definitions.
 struct ZipEntry;
diff --git a/libartbase/base/zip_archive_test.cc b/libartbase/base/zip_archive_test.cc
index 03f4cd4..b99a471 100644
--- a/libartbase/base/zip_archive_test.cc
+++ b/libartbase/base/zip_archive_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "base/zip_archive.h"
+#include "zip_archive.h"
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -22,13 +22,13 @@
 #include <zlib.h>
 #include <memory>
 
-#include "base/os.h"
-#include "base/unix_file/fd_file.h"
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
+#include "os.h"
+#include "unix_file/fd_file.h"
 
 namespace art {
 
-class ZipArchiveTest : public CommonRuntimeTest {};
+class ZipArchiveTest : public CommonArtTest {};
 
 TEST_F(ZipArchiveTest, FindAndExtract) {
   std::string error_msg;
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index b2c041c..3818624 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -19,6 +19,7 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
+        "dex/art_dex_file_loader.cc",
         "dex/compact_dex_file.cc",
         "dex/compact_offset_table.cc",
         "dex/descriptors_names.cc",
@@ -55,24 +56,20 @@
     },
     generated_sources: ["dexfile_operator_srcs"],
     shared_libs: [
-        // Important note: relying on libartbase's header_lib is perfectly acceptable.
-        // However, relying on the libartbase shared library introduces further, possibly cyclic,
-	// dependencies for clients outside of ART.
+        // For MemMap.
+        "libartbase",
         "liblog",
+        // For atrace.
+        "libcutils",
         // For common macros.
         "libbase",
         "libz",
     ],
-    header_libs: [
-        "art_libartbase_headers",
-    ],
     export_include_dirs: ["."],
     export_shared_lib_headers: [
+        "libartbase",
         "libbase",
     ],
-    export_header_lib_headers: [
-        "art_libartbase_headers",
-    ],
 }
 
 gensrcs {
@@ -114,6 +111,7 @@
         "art_gtest_defaults",
     ],
     srcs: [
+        "dex/art_dex_file_loader_test.cc",
         "dex/code_item_accessors_test.cc",
         "dex/compact_dex_file_test.cc",
         "dex/compact_offset_table_test.cc",
diff --git a/runtime/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
similarity index 98%
rename from runtime/dex/art_dex_file_loader.cc
rename to libdexfile/dex/art_dex_file_loader.cc
index 415e451..392ce1e 100644
--- a/runtime/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -534,7 +534,10 @@
   // Check if this dex file is located in the framework directory.
   // If it is, set a flag on the dex file. This is used by hidden API
   // policy decision logic.
-  if (dex_file != nullptr && LocationIsOnSystemFramework(location.c_str())) {
+  // Location can contain multidex suffix, so fetch its canonical version. Note
+  // that this will call `realpath`.
+  std::string path = DexFileLoader::GetDexCanonicalLocation(location.c_str());
+  if (dex_file != nullptr && LocationIsOnSystemFramework(path.c_str())) {
     dex_file->SetIsPlatformDexFile();
   }
 
diff --git a/runtime/dex/art_dex_file_loader.h b/libdexfile/dex/art_dex_file_loader.h
similarity index 97%
rename from runtime/dex/art_dex_file_loader.h
rename to libdexfile/dex/art_dex_file_loader.h
index 7577945..a460aee 100644
--- a/runtime/dex/art_dex_file_loader.h
+++ b/libdexfile/dex/art_dex_file_loader.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_
-#define ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_
+#ifndef ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_
+#define ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_
 
 #include <cstdint>
 #include <memory>
@@ -137,4 +137,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_
+#endif  // ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_
diff --git a/runtime/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc
similarity index 69%
rename from runtime/dex/art_dex_file_loader_test.cc
rename to libdexfile/dex/art_dex_file_loader_test.cc
index afc2599..d353c26 100644
--- a/runtime/dex/art_dex_file_loader_test.cc
+++ b/libdexfile/dex/art_dex_file_loader_test.cc
@@ -43,34 +43,7 @@
   dst_stream << src_stream.rdbuf();
 }
 
-class ArtDexFileLoaderTest : public CommonRuntimeTest {
- public:
-  virtual void SetUp() {
-    CommonRuntimeTest::SetUp();
-
-    std::string dex_location = GetTestDexFileName("Main");
-
-    data_location_path_ = android_data_ + "/foo.jar";
-    system_location_path_ = GetAndroidRoot() + "/foo.jar";
-    system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar";
-
-    Copy(dex_location, data_location_path_);
-    Copy(dex_location, system_location_path_);
-    Copy(dex_location, system_framework_location_path_);
-  }
-
-  virtual void TearDown() {
-    remove(data_location_path_.c_str());
-    remove(system_location_path_.c_str());
-    remove(system_framework_location_path_.c_str());
-    CommonRuntimeTest::TearDown();
-  }
-
- protected:
-  std::string data_location_path_;
-  std::string system_location_path_;
-  std::string system_framework_location_path_;
-};
+class ArtDexFileLoaderTest : public CommonRuntimeTest {};
 
 // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and
 // the tests that depend upon them should be moved to dex_file_loader_test.cc
@@ -272,7 +245,7 @@
 
 TEST_F(ArtDexFileLoaderTest, FindProtoId) {
   for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) {
-    const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i);
+    const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(dex::ProtoIndex(i));
     const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find);
     std::vector<dex::TypeIndex> to_find_types;
     if (to_find_tl != nullptr) {
@@ -283,7 +256,7 @@
     const DexFile::ProtoId* found =
         java_lang_dex_file_->FindProtoId(to_find.return_type_idx_, to_find_types);
     ASSERT_TRUE(found != nullptr);
-    EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), i);
+    EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), dex::ProtoIndex(i));
   }
 }
 
@@ -339,21 +312,24 @@
   ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
 }
 
-TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) {
-  ArtDexFileLoader loader;
-  bool success;
-  std::string error_msg;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir) {
   // Load file from a non-system directory and check that it is not flagged as framework.
-  ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str()));
-  success = loader.Open(data_location_path_.c_str(),
-                        data_location_path_,
-                        /* verify */ false,
-                        /* verify_checksum */ false,
-                        &error_msg,
-                        &dex_files);
-  ASSERT_TRUE(success);
+  std::string data_location_path = android_data_ + "/foo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path.c_str()));
+
+  Copy(GetTestDexFileName("Main"), data_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(data_location_path.c_str(),
+                             data_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
   ASSERT_GE(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     ASSERT_FALSE(dex_file->IsPlatformDexFile());
@@ -361,15 +337,27 @@
 
   dex_files.clear();
 
+  ASSERT_EQ(0, remove(data_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir) {
   // Load file from a system, non-framework directory and check that it is not flagged as framework.
-  ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str()));
-  success = loader.Open(system_location_path_.c_str(),
-                        system_location_path_,
-                        /* verify */ false,
-                        /* verify_checksum */ false,
-                        &error_msg,
-                        &dex_files);
-  ASSERT_TRUE(success);
+  std::string system_location_path = GetAndroidRoot() + "/foo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path.c_str()));
+
+  Copy(GetTestDexFileName("Main"), system_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(system_location_path.c_str(),
+                             system_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
   ASSERT_GE(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     ASSERT_FALSE(dex_file->IsPlatformDexFile());
@@ -377,19 +365,121 @@
 
   dex_files.clear();
 
+  ASSERT_EQ(0, remove(system_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir) {
   // Load file from a system/framework directory and check that it is flagged as a framework dex.
-  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str()));
-  success = loader.Open(system_framework_location_path_.c_str(),
-                        system_framework_location_path_,
-                        /* verify */ false,
-                        /* verify_checksum */ false,
-                        &error_msg,
-                        &dex_files);
-  ASSERT_TRUE(success);
+  std::string system_framework_location_path = GetAndroidRoot() + "/framework/foo.jar";
+  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path.c_str()));
+
+  Copy(GetTestDexFileName("Main"), system_framework_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(system_framework_location_path.c_str(),
+                             system_framework_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
   ASSERT_GE(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     ASSERT_TRUE(dex_file->IsPlatformDexFile());
   }
+
+  dex_files.clear();
+
+  ASSERT_EQ(0, remove(system_framework_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir_MultiDex) {
+  // Load multidex file from a non-system directory and check that it is not flagged as framework.
+  std::string data_multi_location_path = android_data_ + "/multifoo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(data_multi_location_path.c_str()));
+
+  Copy(GetTestDexFileName("MultiDex"), data_multi_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(data_multi_location_path.c_str(),
+                             data_multi_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
+  ASSERT_GT(dex_files.size(), 1u);
+  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+    ASSERT_FALSE(dex_file->IsPlatformDexFile());
+  }
+
+  dex_files.clear();
+
+  ASSERT_EQ(0, remove(data_multi_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir_MultiDex) {
+  // Load multidex file from a system, non-framework directory and check that it is not flagged
+  // as framework.
+  std::string system_multi_location_path = GetAndroidRoot() + "/multifoo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(system_multi_location_path.c_str()));
+
+  Copy(GetTestDexFileName("MultiDex"), system_multi_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(system_multi_location_path.c_str(),
+                             system_multi_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
+  ASSERT_GT(dex_files.size(), 1u);
+  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+    ASSERT_FALSE(dex_file->IsPlatformDexFile());
+  }
+
+  dex_files.clear();
+
+  ASSERT_EQ(0, remove(system_multi_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir_MultiDex) {
+  // Load multidex file from a system/framework directory and check that it is flagged as a
+  // framework dex.
+  std::string system_framework_multi_location_path = GetAndroidRoot() + "/framework/multifoo.jar";
+  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_multi_location_path.c_str()));
+
+  Copy(GetTestDexFileName("MultiDex"), system_framework_multi_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(system_framework_multi_location_path.c_str(),
+                             system_framework_multi_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
+  ASSERT_GT(dex_files.size(), 1u);
+  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+    ASSERT_TRUE(dex_file->IsPlatformDexFile());
+  }
+
+  dex_files.clear();
+
+  ASSERT_EQ(0, remove(system_framework_multi_location_path.c_str()));
 }
 
 }  // namespace art
diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h
new file mode 100644
index 0000000..bcd0a7b
--- /dev/null
+++ b/libdexfile/dex/class_accessor-inl.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_
+#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_
+
+#include "class_accessor.h"
+
+#include "base/leb128.h"
+
+namespace art {
+
+inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def)
+    : ClassAccessor(dex_file, dex_file.GetClassData(class_def)) {}
+
+inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const uint8_t* class_data)
+    : dex_file_(dex_file),
+      ptr_pos_(class_data),
+      num_static_fields_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
+      num_instance_fields_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
+      num_direct_methods_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
+      num_virtual_methods_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {}
+
+inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) {
+  method_idx_ += DecodeUnsignedLeb128(&ptr);
+  access_flags_ = DecodeUnsignedLeb128(&ptr);
+  code_off_ = DecodeUnsignedLeb128(&ptr);
+  return ptr;
+}
+
+inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) {
+  field_idx_ += DecodeUnsignedLeb128(&ptr);
+  access_flags_ = DecodeUnsignedLeb128(&ptr);
+  return ptr;
+}
+
+template <typename StaticFieldVisitor,
+          typename InstanceFieldVisitor,
+          typename DirectMethodVisitor,
+          typename VirtualMethodVisitor>
+inline void ClassAccessor::VisitMethodsAndFields(
+    const StaticFieldVisitor& static_field_visitor,
+    const InstanceFieldVisitor& instance_field_visitor,
+    const DirectMethodVisitor& direct_method_visitor,
+    const VirtualMethodVisitor& virtual_method_visitor) const {
+  const uint8_t* ptr = ptr_pos_;
+  for (size_t i = 0; i < num_static_fields_; ++i) {
+    Field data;
+    ptr = data.Read(ptr);
+    static_field_visitor(data);
+  }
+  for (size_t i = 0; i < num_instance_fields_; ++i) {
+    Field data;
+    ptr = data.Read(ptr);
+    instance_field_visitor(data);
+  }
+  for (size_t i = 0; i < num_direct_methods_; ++i) {
+    Method data;
+    ptr = data.Read(ptr);
+    direct_method_visitor(data);
+  }
+  for (size_t i = 0; i < num_virtual_methods_; ++i) {
+    Method data;
+    ptr = data.Read(ptr);
+    virtual_method_visitor(data);
+  }
+}
+
+template <typename DirectMethodVisitor,
+          typename VirtualMethodVisitor>
+inline void ClassAccessor::VisitMethods(const DirectMethodVisitor& direct_method_visitor,
+                                        const VirtualMethodVisitor& virtual_method_visitor) const {
+  VisitMethodsAndFields(VoidFunctor(),
+                        VoidFunctor(),
+                        direct_method_visitor,
+                        virtual_method_visitor);
+}
+
+// Visit direct and virtual methods.
+template <typename MethodVisitor>
+inline void ClassAccessor::VisitMethods(const MethodVisitor& method_visitor) const {
+  VisitMethods(method_visitor, method_visitor);
+}
+
+inline const DexFile::CodeItem* ClassAccessor::GetCodeItem(const Method& method) const {
+  return dex_file_.GetCodeItem(method.GetCodeItemOffset());
+}
+
+}  // namespace art
+
+#endif  // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_
diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h
new file mode 100644
index 0000000..59a6b5d
--- /dev/null
+++ b/libdexfile/dex/class_accessor.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_
+#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_
+
+#include "base/utils.h"
+#include "dex_file.h"
+
+namespace art {
+
+// Classes to access Dex data.
+class ClassAccessor {
+ public:
+  // Class method data.
+  class Method {
+   public:
+    uint32_t GetIndex() const {
+      return method_idx_;
+    }
+
+    uint32_t GetAccessFlags() const {
+      return access_flags_;
+    }
+
+    uint32_t GetCodeItemOffset() const {
+      return code_off_;
+    }
+
+   private:
+    const uint8_t* Read(const uint8_t* ptr);
+
+    // A decoded version of the method of a class_data_item.
+    uint32_t method_idx_ = 0u;
+    uint32_t access_flags_ = 0u;
+    uint32_t code_off_ = 0u;
+
+    friend class ClassAccessor;
+  };
+
+  // Class field data.
+  class Field {
+   public:
+    uint32_t GetIndex() const {
+      return field_idx_;
+    }
+
+    uint32_t GetAccessFlags() const {
+      return access_flags_;
+    }
+
+   private:
+    const uint8_t* Read(const uint8_t* ptr);
+
+    // A decoded version of the field of a class_data_item.
+    uint32_t field_idx_ = 0u;
+    uint32_t access_flags_ = 0u;
+
+    friend class ClassAccessor;
+  };
+
+  ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def);
+
+  // Return the code item for a method.
+  const DexFile::CodeItem* GetCodeItem(const Method& method) const;
+
+  // Iterator data is not very iterator friendly, use visitors to get around this.
+  template <typename StaticFieldVisitor,
+            typename InstanceFieldVisitor,
+            typename DirectMethodVisitor,
+            typename VirtualMethodVisitor>
+  void VisitMethodsAndFields(const StaticFieldVisitor& static_field_visitor,
+                             const InstanceFieldVisitor& instance_field_visitor,
+                             const DirectMethodVisitor& direct_method_visitor,
+                             const VirtualMethodVisitor& virtual_method_visitor) const;
+
+  template <typename DirectMethodVisitor,
+            typename VirtualMethodVisitor>
+  void VisitMethods(const DirectMethodVisitor& direct_method_visitor,
+                    const VirtualMethodVisitor& virtual_method_visitor) const;
+
+  // Visit direct and virtual methods.
+  template <typename MethodVisitor>
+  void VisitMethods(const MethodVisitor& method_visitor) const;
+
+  uint32_t NumStaticFields() const {
+    return num_static_fields_;
+  }
+
+  uint32_t NumInstanceFields() const {
+    return num_instance_fields_;
+  }
+
+  uint32_t NumDirectMethods() const {
+    return num_direct_methods_;
+  }
+
+  uint32_t NumVirtualMethods() const {
+    return num_virtual_methods_;
+  }
+
+ protected:
+  ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, const uint8_t* class_data);
+
+  const DexFile& dex_file_;
+  const uint8_t* ptr_pos_ = nullptr;  // Pointer into stream of class_data_item.
+  const uint32_t num_static_fields_ = 0u;
+  const uint32_t num_instance_fields_ = 0u;
+  const uint32_t num_direct_methods_ = 0u;
+  const uint32_t num_virtual_methods_ = 0u;
+  // Only cache descriptor.
+  const void* class_def_ = nullptr;
+  const void* class_data_ = nullptr;
+};
+
+}  // namespace art
+
+#endif  // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index d1b3200..e78e8d7 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -127,7 +127,7 @@
   return StringByTypeIdx(proto_id.return_type_idx_);
 }
 
-inline const char* DexFile::GetShorty(uint32_t proto_idx) const {
+inline const char* DexFile::GetShorty(dex::ProtoIndex proto_idx) const {
   const ProtoId& proto_id = GetProtoId(proto_idx);
   return StringDataByIdx(proto_id.shorty_idx_);
 }
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index 8cfee66..9de260c 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -281,7 +281,7 @@
   // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
   const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass);
   const dex::StringIndex name_idx = GetIndexForStringId(name);
-  const uint16_t proto_idx = GetIndexForProtoId(signature);
+  const dex::ProtoIndex proto_idx = GetIndexForProtoId(signature);
   int32_t lo = 0;
   int32_t hi = NumMethodIds() - 1;
   while (hi >= lo) {
@@ -373,7 +373,8 @@
   int32_t hi = NumProtoIds() - 1;
   while (hi >= lo) {
     int32_t mid = (hi + lo) / 2;
-    const DexFile::ProtoId& proto = GetProtoId(mid);
+    const dex::ProtoIndex proto_idx = static_cast<dex::ProtoIndex>(mid);
+    const DexFile::ProtoId& proto = GetProtoId(proto_idx);
     int compare = return_type_idx.index_ - proto.return_type_idx_.index_;
     if (compare == 0) {
       DexFileParameterIterator it(*this, proto);
@@ -777,6 +778,11 @@
 
 namespace dex {
 
+std::ostream& operator<<(std::ostream& os, const ProtoIndex& index) {
+  os << "ProtoIndex[" << index.index_ << "]";
+  return os;
+}
+
 std::ostream& operator<<(std::ostream& os, const StringIndex& index) {
   os << "StringIndex[" << index.index_ << "]";
   return os;
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index 4098b42..87d2c48 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -189,7 +189,7 @@
   // Raw method_id_item.
   struct MethodId {
     dex::TypeIndex class_idx_;   // index into type_ids_ array for defining class
-    uint16_t proto_idx_;         // index into proto_ids_ array for method prototype
+    dex::ProtoIndex proto_idx_;  // index into proto_ids_ array for method prototype
     dex::StringIndex name_idx_;  // index into string_ids_ array for method name
 
    private:
@@ -692,15 +692,15 @@
   }
 
   // Returns the ProtoId at the specified index.
-  const ProtoId& GetProtoId(uint16_t idx) const {
-    DCHECK_LT(idx, NumProtoIds()) << GetLocation();
-    return proto_ids_[idx];
+  const ProtoId& GetProtoId(dex::ProtoIndex idx) const {
+    DCHECK_LT(idx.index_, NumProtoIds()) << GetLocation();
+    return proto_ids_[idx.index_];
   }
 
-  uint16_t GetIndexForProtoId(const ProtoId& proto_id) const {
+  dex::ProtoIndex GetIndexForProtoId(const ProtoId& proto_id) const {
     CHECK_GE(&proto_id, proto_ids_) << GetLocation();
     CHECK_LT(&proto_id, proto_ids_ + header_->proto_ids_size_) << GetLocation();
-    return &proto_id - proto_ids_;
+    return dex::ProtoIndex(&proto_id - proto_ids_);
   }
 
   // Looks up a proto id for a given return type and signature type list
@@ -722,7 +722,7 @@
   const Signature CreateSignature(const StringPiece& signature) const;
 
   // Returns the short form method descriptor for the given prototype.
-  const char* GetShorty(uint32_t proto_idx) const;
+  const char* GetShorty(dex::ProtoIndex proto_idx) const;
 
   const TypeList* GetProtoParameters(const ProtoId& proto_id) const {
     return DataPointer<TypeList>(proto_id.parameters_off_);
@@ -1346,9 +1346,6 @@
     uint32_t field_idx_delta_;  // delta of index into the field_ids array for FieldId
     uint32_t access_flags_;  // access flags for the field
     ClassDataField() :  field_idx_delta_(0), access_flags_(0) {}
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(ClassDataField);
   };
   ClassDataField field_;
 
@@ -1361,9 +1358,6 @@
     uint32_t access_flags_;
     uint32_t code_off_;
     ClassDataMethod() : method_idx_delta_(0), access_flags_(0), code_off_(0) {}
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(ClassDataMethod);
   };
   ClassDataMethod method_;
 
@@ -1374,7 +1368,6 @@
   size_t pos_;  // integral number of items passed
   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);
 };
 
 class EncodedArrayValueIterator {
diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc
index 1e0f5ac..457addf 100644
--- a/libdexfile/dex/dex_file_loader.cc
+++ b/libdexfile/dex/dex_file_loader.cc
@@ -191,6 +191,8 @@
   std::string base_location = GetBaseLocation(dex_location);
   const char* suffix = dex_location + base_location.size();
   DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator);
+  // Warning: Bionic implementation of realpath() allocates > 12KB on the stack.
+  // Do not run this code on a small stack, e.g. in signal handler.
   UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr));
   if (path != nullptr && path.get() != base_location) {
     return std::string(path.get()) + suffix;
diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h
index 28cdfc1..0153220 100644
--- a/libdexfile/dex/dex_file_loader.h
+++ b/libdexfile/dex/dex_file_loader.h
@@ -71,7 +71,7 @@
   //     of the dex file. In the second case (oat) it will include the file name
   //     and possibly some multidex annotation to uniquely identify it.
   // canonical_dex_location:
-  //     the dex_location where it's file name part has been made canonical.
+  //     the dex_location where its file name part has been made canonical.
   static std::string GetDexCanonicalLocation(const char* dex_location);
 
   // For normal dex files, location and base location coincide. If a dex file is part of a multidex
diff --git a/libdexfile/dex/dex_file_tracking_registrar.cc b/libdexfile/dex/dex_file_tracking_registrar.cc
index 78ea9c1..551bea1 100644
--- a/libdexfile/dex/dex_file_tracking_registrar.cc
+++ b/libdexfile/dex/dex_file_tracking_registrar.cc
@@ -130,7 +130,8 @@
     MEMORY_TOOL_MAKE_NOACCESS(begin, size);
   } else {
     // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address
-    // Sanitizer. The difference has not been tested with Valgrind
+    // Sanitizer.
+    // Historical note: The difference has not been tested with Valgrind.
     MEMORY_TOOL_MAKE_DEFINED(begin, size);
   }
 }
diff --git a/libdexfile/dex/dex_file_types.h b/libdexfile/dex/dex_file_types.h
index 2bb70ff..d4fb3de 100644
--- a/libdexfile/dex/dex_file_types.h
+++ b/libdexfile/dex/dex_file_types.h
@@ -25,73 +25,67 @@
 
 constexpr uint32_t kDexNoIndex = 0xFFFFFFFF;
 
-class StringIndex {
+template<typename T>
+class DexIndex {
  public:
-  uint32_t index_;
+  T index_;
 
-  constexpr StringIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
-  explicit constexpr StringIndex(uint32_t idx) : index_(idx) {}
+  constexpr DexIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
+  explicit constexpr DexIndex(T idx) : index_(idx) {}
 
   bool IsValid() const {
     return index_ != std::numeric_limits<decltype(index_)>::max();
   }
-  static StringIndex Invalid() {
-    return StringIndex(std::numeric_limits<decltype(index_)>::max());
+  static constexpr DexIndex Invalid() {
+    return DexIndex(std::numeric_limits<decltype(index_)>::max());
   }
-
-  bool operator==(const StringIndex& other) const {
+  bool operator==(const DexIndex& other) const {
     return index_ == other.index_;
   }
-  bool operator!=(const StringIndex& other) const {
+  bool operator!=(const DexIndex& other) const {
     return index_ != other.index_;
   }
-  bool operator<(const StringIndex& other) const {
+  bool operator<(const DexIndex& other) const {
     return index_ < other.index_;
   }
-  bool operator<=(const StringIndex& other) const {
+  bool operator<=(const DexIndex& other) const {
     return index_ <= other.index_;
   }
-  bool operator>(const StringIndex& other) const {
+  bool operator>(const DexIndex& other) const {
     return index_ > other.index_;
   }
-  bool operator>=(const StringIndex& other) const {
+  bool operator>=(const DexIndex& other) const {
     return index_ >= other.index_;
   }
 };
+
+class ProtoIndex : public DexIndex<uint16_t> {
+ public:
+  ProtoIndex() {}
+  explicit constexpr ProtoIndex(uint16_t index) : DexIndex<decltype(index_)>(index) {}
+  static constexpr ProtoIndex Invalid() {
+    return ProtoIndex(std::numeric_limits<decltype(index_)>::max());
+  }
+};
+std::ostream& operator<<(std::ostream& os, const ProtoIndex& index);
+
+class StringIndex : public DexIndex<uint32_t> {
+ public:
+  StringIndex() {}
+  explicit constexpr StringIndex(uint32_t index) : DexIndex<decltype(index_)>(index) {}
+  static constexpr StringIndex Invalid() {
+    return StringIndex(std::numeric_limits<decltype(index_)>::max());
+  }
+};
 std::ostream& operator<<(std::ostream& os, const StringIndex& index);
 
-class TypeIndex {
+class TypeIndex : public DexIndex<uint16_t> {
  public:
-  uint16_t index_;
-
-  constexpr TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
-  explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {}
-
-  bool IsValid() const {
-    return index_ != std::numeric_limits<decltype(index_)>::max();
-  }
-  static TypeIndex Invalid() {
+  TypeIndex() {}
+  explicit constexpr TypeIndex(uint16_t index) : DexIndex<uint16_t>(index) {}
+  static constexpr TypeIndex Invalid() {
     return TypeIndex(std::numeric_limits<decltype(index_)>::max());
   }
-
-  bool operator==(const TypeIndex& other) const {
-    return index_ == other.index_;
-  }
-  bool operator!=(const TypeIndex& other) const {
-    return index_ != other.index_;
-  }
-  bool operator<(const TypeIndex& other) const {
-    return index_ < other.index_;
-  }
-  bool operator<=(const TypeIndex& other) const {
-    return index_ <= other.index_;
-  }
-  bool operator>(const TypeIndex& other) const {
-    return index_ > other.index_;
-  }
-  bool operator>=(const TypeIndex& other) const {
-    return index_ >= other.index_;
-  }
 };
 std::ostream& operator<<(std::ostream& os, const TypeIndex& index);
 
@@ -100,15 +94,21 @@
 
 namespace std {
 
+template<> struct hash<art::dex::ProtoIndex> {
+  size_t operator()(const art::dex::ProtoIndex& index) const {
+    return hash<decltype(index.index_)>()(index.index_);
+  }
+};
+
 template<> struct hash<art::dex::StringIndex> {
   size_t operator()(const art::dex::StringIndex& index) const {
-    return hash<uint32_t>()(index.index_);
+    return hash<decltype(index.index_)>()(index.index_);
   }
 };
 
 template<> struct hash<art::dex::TypeIndex> {
   size_t operator()(const art::dex::TypeIndex& index) const {
-    return hash<uint16_t>()(index.index_);
+    return hash<decltype(index.index_)>()(index.index_);
   }
 };
 
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index 68bd19e..78db8b9 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -18,7 +18,6 @@
 
 #include <inttypes.h>
 
-#include <limits>
 #include <memory>
 
 #include "android-base/stringprintf.h"
@@ -106,66 +105,6 @@
   return dex_file_->StringDataByIdx(idx);
 }
 
-// Try to find the name of the method with the given index. We do not want to rely on DexFile
-// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and
-// header_ of the DexFileVerifier. str will contain the pointer to the method name on success
-// (flagged by the return value), otherwise error_msg will contain an error string.
-static bool FindMethodName(uint32_t method_index,
-                           const uint8_t* begin,
-                           const DexFile::Header* header,
-                           const char** str,
-                           std::string* error_msg) {
-  if (method_index >= header->method_ids_size_) {
-    *error_msg = "Method index not available for method flags verification";
-    return false;
-  }
-  uint32_t string_idx =
-      (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) +
-          method_index)->name_idx_.index_;
-  if (string_idx >= header->string_ids_size_) {
-    *error_msg = "String index not available for method flags verification";
-    return false;
-  }
-  uint32_t string_off =
-      (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)->
-          string_data_off_;
-  if (string_off >= header->file_size_) {
-    *error_msg = "String offset out of bounds for method flags verification";
-    return false;
-  }
-  const uint8_t* str_data_ptr = begin + string_off;
-  uint32_t dummy;
-  if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) {
-    *error_msg = "String size out of bounds for method flags verification";
-    return false;
-  }
-  *str = reinterpret_cast<const char*>(str_data_ptr);
-  return true;
-}
-
-// Gets constructor flags based on the |method_name|. Returns true if
-// method_name is either <clinit> or <init> and sets
-// |constructor_flags_by_name| appropriately. Otherwise set
-// |constructor_flags_by_name| to zero and returns whether
-// |method_name| is valid.
-bool GetConstructorFlagsForMethodName(const char* method_name,
-                                      uint32_t* constructor_flags_by_name) {
-  if (method_name[0] != '<') {
-    *constructor_flags_by_name = 0;
-    return true;
-  }
-  if (strcmp(method_name + 1, "clinit>") == 0) {
-    *constructor_flags_by_name = kAccStatic | kAccConstructor;
-    return true;
-  }
-  if (strcmp(method_name + 1, "init>") == 0) {
-    *constructor_flags_by_name = kAccConstructor;
-    return true;
-  }
-  *constructor_flags_by_name = 0;
-  return false;
-}
-
 const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx,
                                                       const char* error_string) {
   if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) {
@@ -188,8 +127,9 @@
   return &dex_file_->GetMethodId(idx);
 }
 
-const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) {
-  if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) {
+const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(dex::ProtoIndex idx,
+                                                          const char* err_string) {
+  if (UNLIKELY(!CheckIndex(idx.index_, dex_file_->NumProtoIds(), err_string))) {
     return nullptr;
   }
   return &dex_file_->GetProtoId(idx);
@@ -674,18 +614,19 @@
                                                uint32_t class_access_flags,
                                                dex::TypeIndex class_type_index,
                                                uint32_t code_offset,
-                                               std::unordered_set<uint32_t>* direct_method_indexes,
+                                               ClassDataItemIterator* direct_it,
                                                bool expect_direct) {
-  DCHECK(direct_method_indexes != nullptr);
+  DCHECK_EQ(expect_direct, direct_it == nullptr);
   // Check for overflow.
   if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
     return false;
   }
 
+  const DexFile::MethodId& method_id =
+      *(reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + idx);
+
   // Check that it's the right class.
-  dex::TypeIndex my_class_index =
-      (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + idx)->
-          class_idx_;
+  dex::TypeIndex my_class_index = method_id.class_idx_;
   if (class_type_index != my_class_index) {
     ErrorStringPrintf("Method's class index unexpected, %" PRIu16 " vs %" PRIu16,
                       my_class_index.index_,
@@ -694,24 +635,39 @@
   }
 
   // Check that it's not defined as both direct and virtual.
-  if (expect_direct) {
-    direct_method_indexes->insert(idx);
-  } else if (direct_method_indexes->find(idx) != direct_method_indexes->end()) {
-    ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx);
-    return false;
+  if (!expect_direct) {
+    // The direct methods are already known to be in ascending index order. So just keep up
+    // with the current index.
+    for (; direct_it->HasNextDirectMethod(); direct_it->Next()) {
+      uint32_t direct_idx = direct_it->GetMemberIndex();
+      if (direct_idx > idx) {
+        break;
+      }
+      if (direct_idx == idx) {
+        ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx);
+        return false;
+      }
+    }
   }
 
   std::string error_msg;
-  const char* method_name;
-  if (!FindMethodName(idx, begin_, header_, &method_name, &error_msg)) {
-    ErrorStringPrintf("%s", error_msg.c_str());
-    return false;
-  }
-
   uint32_t constructor_flags_by_name = 0;
-  if (!GetConstructorFlagsForMethodName(method_name, &constructor_flags_by_name)) {
-    ErrorStringPrintf("Bad method name: %s", method_name);
-    return false;
+  {
+    uint32_t string_idx = method_id.name_idx_.index_;
+    if (!CheckIndex(string_idx, header_->string_ids_size_, "method flags verification")) {
+      return false;
+    }
+    if (UNLIKELY(string_idx < angle_bracket_end_index_) &&
+            string_idx >= angle_bracket_start_index_) {
+      if (string_idx == angle_clinit_angle_index_) {
+        constructor_flags_by_name = kAccStatic | kAccConstructor;
+      } else if (string_idx == angle_init_angle_index_) {
+        constructor_flags_by_name = kAccConstructor;
+      } else {
+        ErrorStringPrintf("Bad method name for method index %u", idx);
+        return false;
+      }
+    }
   }
 
   bool has_code = (code_offset != 0);
@@ -986,31 +942,16 @@
   return false;
 }
 
-bool DexFileVerifier::CheckOrderAndGetClassDef(bool is_field,
-                                               const char* type_descr,
-                                               uint32_t curr_index,
-                                               uint32_t prev_index,
-                                               bool* have_class,
-                                               dex::TypeIndex* class_type_index,
-                                               const DexFile::ClassDef** class_def) {
-  if (curr_index < prev_index) {
+bool DexFileVerifier::CheckOrder(const char* type_descr,
+                                 uint32_t curr_index,
+                                 uint32_t prev_index) {
+  if (UNLIKELY(curr_index < prev_index)) {
     ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32,
                       type_descr,
                       prev_index,
                       curr_index);
     return false;
   }
-
-  if (!*have_class) {
-    *have_class = FindClassIndexAndDef(curr_index, is_field, class_type_index, class_def);
-    if (!*have_class) {
-      // Should have really found one.
-      ErrorStringPrintf("could not find declaring class for %s index %" PRIu32,
-                        type_descr,
-                        curr_index);
-      return false;
-    }
-  }
   return true;
 }
 
@@ -1115,20 +1056,29 @@
                                                     dex::TypeIndex* class_type_index,
                                                     const DexFile::ClassDef** class_def) {
   DCHECK(it != nullptr);
+  constexpr const char* kTypeDescr = kStatic ? "static field" : "instance field";
+
   // These calls use the raw access flags to check whether the whole dex field is valid.
+
+  if (!*have_class && (kStatic ? it->HasNextStaticField() : it->HasNextInstanceField())) {
+    *have_class = FindClassIndexAndDef(it->GetMemberIndex(), true, class_type_index, class_def);
+    if (!*have_class) {
+      // Should have really found one.
+      ErrorStringPrintf("could not find declaring class for %s index %" PRIu32,
+                        kTypeDescr,
+                        it->GetMemberIndex());
+      return false;
+    }
+  }
+  DCHECK(*class_def != nullptr ||
+         !(kStatic ? it->HasNextStaticField() : it->HasNextInstanceField()));
+
   uint32_t prev_index = 0;
   for (; kStatic ? it->HasNextStaticField() : it->HasNextInstanceField(); it->Next()) {
     uint32_t curr_index = it->GetMemberIndex();
-    if (!CheckOrderAndGetClassDef(true,
-                                  kStatic ? "static field" : "instance field",
-                                  curr_index,
-                                  prev_index,
-                                  have_class,
-                                  class_type_index,
-                                  class_def)) {
+    if (!CheckOrder(kTypeDescr, curr_index, prev_index)) {
       return false;
     }
-    DCHECK(class_def != nullptr);
     if (!CheckClassDataItemField(curr_index,
                                  it->GetRawMemberAccessFlags(),
                                  (*class_def)->access_flags_,
@@ -1146,29 +1096,38 @@
 template <bool kDirect>
 bool DexFileVerifier::CheckIntraClassDataItemMethods(
     ClassDataItemIterator* it,
-    std::unordered_set<uint32_t>* direct_method_indexes,
+    ClassDataItemIterator* direct_it,
     bool* have_class,
     dex::TypeIndex* class_type_index,
     const DexFile::ClassDef** class_def) {
+  DCHECK(it != nullptr);
+  constexpr const char* kTypeDescr = kDirect ? "direct method" : "virtual method";
+
+  if (!*have_class && (kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod())) {
+    *have_class = FindClassIndexAndDef(it->GetMemberIndex(), false, class_type_index, class_def);
+    if (!*have_class) {
+      // Should have really found one.
+      ErrorStringPrintf("could not find declaring class for %s index %" PRIu32,
+                        kTypeDescr,
+                        it->GetMemberIndex());
+      return false;
+    }
+  }
+  DCHECK(*class_def != nullptr ||
+         !(kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod()));
+
   uint32_t prev_index = 0;
   for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) {
     uint32_t curr_index = it->GetMemberIndex();
-    if (!CheckOrderAndGetClassDef(false,
-                                  kDirect ? "direct method" : "virtual method",
-                                  curr_index,
-                                  prev_index,
-                                  have_class,
-                                  class_type_index,
-                                  class_def)) {
+    if (!CheckOrder(kTypeDescr, curr_index, prev_index)) {
       return false;
     }
-    DCHECK(class_def != nullptr);
     if (!CheckClassDataItemMethod(curr_index,
                                   it->GetRawMemberAccessFlags(),
                                   (*class_def)->access_flags_,
                                   *class_type_index,
                                   it->GetMethodCodeItemOffset(),
-                                  direct_method_indexes,
+                                  direct_it,
                                   kDirect)) {
       return false;
     }
@@ -1181,7 +1140,6 @@
 
 bool DexFileVerifier::CheckIntraClassDataItem() {
   ClassDataItemIterator it(*dex_file_, ptr_);
-  std::unordered_set<uint32_t> direct_method_indexes;
 
   // This code is complicated by the fact that we don't directly know which class this belongs to.
   // So we need to explicitly search with the first item we find (either field or method), and then,
@@ -1205,15 +1163,17 @@
   }
 
   // Check methods.
+  ClassDataItemIterator direct_it = it;
+
   if (!CheckIntraClassDataItemMethods<true>(&it,
-                                            &direct_method_indexes,
+                                            nullptr /* direct_it */,
                                             &have_class,
                                             &class_type_index,
                                             &class_def)) {
     return false;
   }
   if (!CheckIntraClassDataItemMethods<false>(&it,
-                                             &direct_method_indexes,
+                                             &direct_it,
                                              &have_class,
                                              &class_type_index,
                                              &class_def)) {
@@ -1288,7 +1248,20 @@
     return false;
   }
 
-  std::unique_ptr<uint32_t[]> handler_offsets(new uint32_t[handlers_size]);
+  // Avoid an expensive allocation, if possible.
+  std::unique_ptr<uint32_t[]> handler_offsets_uptr;
+  uint32_t* handler_offsets;
+  constexpr size_t kAllocaMaxSize = 1024;
+  if (handlers_size < kAllocaMaxSize/sizeof(uint32_t)) {
+    // Note: Clang does not specify alignment guarantees for alloca. So align by hand.
+    handler_offsets =
+        AlignUp(reinterpret_cast<uint32_t*>(alloca((handlers_size + 1) * sizeof(uint32_t))),
+                alignof(uint32_t[]));
+  } else {
+    handler_offsets_uptr.reset(new uint32_t[handlers_size]);
+    handler_offsets = handler_offsets_uptr.get();
+  }
+
   if (!CheckAndGetHandlerOffsets(code_item, &handler_offsets[0], handlers_size)) {
     return false;
   }
@@ -1613,11 +1586,11 @@
   return true;
 }
 
-bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count,
-                                               DexFile::MapItemType type) {
+template <DexFile::MapItemType kType>
+bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count) {
   // Get the right alignment mask for the type of section.
   size_t alignment_mask;
-  switch (type) {
+  switch (kType) {
     case DexFile::kDexTypeClassDataItem:
     case DexFile::kDexTypeStringDataItem:
     case DexFile::kDexTypeDebugInfoItem:
@@ -1635,13 +1608,13 @@
     size_t aligned_offset = (offset + alignment_mask) & ~alignment_mask;
 
     // Check the padding between items.
-    if (!CheckPadding(offset, aligned_offset, type)) {
+    if (!CheckPadding(offset, aligned_offset, kType)) {
       return false;
     }
 
     // Check depending on the section type.
     const uint8_t* start_ptr = ptr_;
-    switch (type) {
+    switch (kType) {
       case DexFile::kDexTypeStringIdItem: {
         if (!CheckListSize(ptr_, 1, sizeof(DexFile::StringId), "string_ids")) {
           return false;
@@ -1764,17 +1737,17 @@
     }
 
     if (start_ptr == ptr_) {
-      ErrorStringPrintf("Unknown map item type %x", type);
+      ErrorStringPrintf("Unknown map item type %x", kType);
       return false;
     }
 
-    if (IsDataSectionType(type)) {
+    if (IsDataSectionType(kType)) {
       if (aligned_offset == 0u) {
         ErrorStringPrintf("Item %d offset is 0", i);
         return false;
       }
       DCHECK(offset_to_type_map_.Find(aligned_offset) == offset_to_type_map_.end());
-      offset_to_type_map_.Insert(std::pair<uint32_t, uint16_t>(aligned_offset, type));
+      offset_to_type_map_.Insert(std::pair<uint32_t, uint16_t>(aligned_offset, kType));
     }
 
     aligned_offset = ptr_ - begin_;
@@ -1789,14 +1762,13 @@
   return true;
 }
 
-bool DexFileVerifier::CheckIntraIdSection(size_t offset,
-                                          uint32_t count,
-                                          DexFile::MapItemType type) {
+template <DexFile::MapItemType kType>
+bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count) {
   uint32_t expected_offset;
   uint32_t expected_size;
 
   // Get the expected offset and size from the header.
-  switch (type) {
+  switch (kType) {
     case DexFile::kDexTypeStringIdItem:
       expected_offset = header_->string_ids_off_;
       expected_size = header_->string_ids_size_;
@@ -1822,7 +1794,7 @@
       expected_size = header_->class_defs_size_;
       break;
     default:
-      ErrorStringPrintf("Bad type for id section: %x", type);
+      ErrorStringPrintf("Bad type for id section: %x", kType);
       return false;
   }
 
@@ -1836,12 +1808,11 @@
     return false;
   }
 
-  return CheckIntraSectionIterate(offset, count, type);
+  return CheckIntraSectionIterate<kType>(offset, count);
 }
 
-bool DexFileVerifier::CheckIntraDataSection(size_t offset,
-                                            uint32_t count,
-                                            DexFile::MapItemType type) {
+template <DexFile::MapItemType kType>
+bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count) {
   size_t data_start = header_->data_off_;
   size_t data_end = data_start + header_->data_size_;
 
@@ -1851,7 +1822,7 @@
     return false;
   }
 
-  if (!CheckIntraSectionIterate(offset, count, type)) {
+  if (!CheckIntraSectionIterate<kType>(offset, count)) {
     return false;
   }
 
@@ -1868,7 +1839,8 @@
 }
 
 bool DexFileVerifier::CheckIntraSection() {
-  const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
+  const DexFile::MapList* map =
+      reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
   const DexFile::MapItem* item = map->list_;
   size_t offset = 0;
   uint32_t count = map->size_;
@@ -1889,6 +1861,10 @@
       return false;
     }
 
+    if (type == DexFile::kDexTypeClassDataItem) {
+      FindStringRangesForMethodNames();
+    }
+
     // Check each item based on its type.
     switch (type) {
       case DexFile::kDexTypeHeaderItem:
@@ -1903,17 +1879,22 @@
         ptr_ = begin_ + header_->header_size_;
         offset = header_->header_size_;
         break;
-      case DexFile::kDexTypeStringIdItem:
-      case DexFile::kDexTypeTypeIdItem:
-      case DexFile::kDexTypeProtoIdItem:
-      case DexFile::kDexTypeFieldIdItem:
-      case DexFile::kDexTypeMethodIdItem:
-      case DexFile::kDexTypeClassDefItem:
-        if (!CheckIntraIdSection(section_offset, section_count, type)) {
-          return false;
-        }
-        offset = ptr_ - begin_;
+
+#define CHECK_INTRA_ID_SECTION_CASE(type)                                   \
+      case type:                                                            \
+        if (!CheckIntraIdSection<type>(section_offset, section_count)) {    \
+          return false;                                                     \
+        }                                                                   \
+        offset = ptr_ - begin_;                                             \
         break;
+      CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeStringIdItem)
+      CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeTypeIdItem)
+      CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeProtoIdItem)
+      CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeFieldIdItem)
+      CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeMethodIdItem)
+      CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeClassDefItem)
+#undef CHECK_INTRA_ID_SECTION_CASE
+
       case DexFile::kDexTypeMapList:
         if (UNLIKELY(section_count != 1)) {
           ErrorStringPrintf("Multiple map list items");
@@ -1927,26 +1908,34 @@
         ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
         offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
         break;
-      case DexFile::kDexTypeMethodHandleItem:
-      case DexFile::kDexTypeCallSiteIdItem:
-        CheckIntraSectionIterate(section_offset, section_count, type);
-        offset = ptr_ - begin_;
+
+#define CHECK_INTRA_SECTION_ITERATE_CASE(type)                              \
+      case type:                                                            \
+        CheckIntraSectionIterate<type>(section_offset, section_count);      \
+        offset = ptr_ - begin_;                                             \
         break;
-      case DexFile::kDexTypeTypeList:
-      case DexFile::kDexTypeAnnotationSetRefList:
-      case DexFile::kDexTypeAnnotationSetItem:
-      case DexFile::kDexTypeClassDataItem:
-      case DexFile::kDexTypeCodeItem:
-      case DexFile::kDexTypeStringDataItem:
-      case DexFile::kDexTypeDebugInfoItem:
-      case DexFile::kDexTypeAnnotationItem:
-      case DexFile::kDexTypeEncodedArrayItem:
-      case DexFile::kDexTypeAnnotationsDirectoryItem:
-        if (!CheckIntraDataSection(section_offset, section_count, type)) {
-          return false;
-        }
-        offset = ptr_ - begin_;
+      CHECK_INTRA_SECTION_ITERATE_CASE(DexFile::kDexTypeMethodHandleItem)
+      CHECK_INTRA_SECTION_ITERATE_CASE(DexFile::kDexTypeCallSiteIdItem)
+#undef CHECK_INTRA_SECTION_ITERATE_CASE
+
+#define CHECK_INTRA_DATA_SECTION_CASE(type)                                 \
+      case type:                                                            \
+        if (!CheckIntraDataSection<type>(section_offset, section_count)) {  \
+          return false;                                                     \
+        }                                                                   \
+        offset = ptr_ - begin_;                                             \
         break;
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeTypeList)
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationSetRefList)
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationSetItem)
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeClassDataItem)
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeCodeItem)
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeStringDataItem)
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeDebugInfoItem)
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationItem)
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeEncodedArrayItem)
+      CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationsDirectoryItem)
+#undef CHECK_INTRA_DATA_SECTION_CASE
     }
 
     if (offset == current_offset) {
@@ -2220,7 +2209,7 @@
   }
 
   // Check that the proto id is valid.
-  if (UNLIKELY(!CheckIndex(item->proto_idx_, dex_file_->NumProtoIds(),
+  if (UNLIKELY(!CheckIndex(item->proto_idx_.index_, dex_file_->NumProtoIds(),
                            "inter_method_id_item proto_idx"))) {
     return false;
   }
@@ -2889,11 +2878,14 @@
 }
 
 // Fields and methods may have only one of public/protected/private.
-static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) {
-  size_t count = (((flags & kAccPublic) == 0) ? 0 : 1) +
-                 (((flags & kAccProtected) == 0) ? 0 : 1) +
-                 (((flags & kAccPrivate) == 0) ? 0 : 1);
-  return count <= 1;
+ALWAYS_INLINE
+static constexpr bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) {
+  // Semantically we want 'return POPCOUNT(flags & kAcc) <= 1;'.
+  static_assert(IsPowerOfTwo(0), "0 not marked as power of two");
+  static_assert(IsPowerOfTwo(kAccPublic), "kAccPublic not marked as power of two");
+  static_assert(IsPowerOfTwo(kAccProtected), "kAccProtected not marked as power of two");
+  static_assert(IsPowerOfTwo(kAccPrivate), "kAccPrivate not marked as power of two");
+  return IsPowerOfTwo(flags & (kAccPublic | kAccProtected | kAccPrivate));
 }
 
 // Helper functions to retrieve names from the dex file. We do not want to rely on DexFile
@@ -3052,6 +3044,55 @@
   return true;
 }
 
+void DexFileVerifier::FindStringRangesForMethodNames() {
+  // Use DexFile::StringId* as RandomAccessIterator.
+  const DexFile::StringId* first = reinterpret_cast<const DexFile::StringId*>(
+      begin_ + header_->string_ids_off_);
+  const DexFile::StringId* last = first + header_->string_ids_size_;
+
+  auto get_string = [begin = begin_](const DexFile::StringId& id) {
+    const uint8_t* str_data_ptr = begin + id.string_data_off_;
+    DecodeUnsignedLeb128(&str_data_ptr);
+    return reinterpret_cast<const char*>(str_data_ptr);
+  };
+  auto compare = [&get_string](const DexFile::StringId& lhs, const char* rhs) {
+    return strcmp(get_string(lhs), rhs) < 0;
+  };
+
+  // '=' follows '<'
+  static_assert('<' + 1 == '=', "Unexpected character relation");
+  const auto angle_end = std::lower_bound(first, last, "=", compare);
+  angle_bracket_end_index_ = angle_end - first;
+
+  const auto angle_start = std::lower_bound(first, angle_end, "<", compare);
+  angle_bracket_start_index_ = angle_start - first;
+  if (angle_start == angle_end) {
+    // No strings starting with '<'.
+    angle_init_angle_index_ = std::numeric_limits<size_t>::max();
+    angle_clinit_angle_index_ = std::numeric_limits<size_t>::max();
+    return;
+  }
+
+  {
+    constexpr const char* kClinit = "<clinit>";
+    const auto it = std::lower_bound(angle_start, angle_end, kClinit, compare);
+    if (it != angle_end && strcmp(get_string(*it), kClinit) == 0) {
+      angle_clinit_angle_index_ = it - first;
+    } else {
+      angle_clinit_angle_index_ = std::numeric_limits<size_t>::max();
+    }
+  }
+  {
+    constexpr const char* kInit = "<init>";
+    const auto it = std::lower_bound(angle_start, angle_end, kInit, compare);
+    if (it != angle_end && strcmp(get_string(*it), kInit) == 0) {
+      angle_init_angle_index_ = it - first;
+    } else {
+      angle_init_angle_index_ = std::numeric_limits<size_t>::max();
+    }
+  }
+}
+
 bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
                                              uint32_t method_access_flags,
                                              uint32_t class_access_flags,
diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h
index a80a9d5..43d1093 100644
--- a/libdexfile/dex/dex_file_verifier.h
+++ b/libdexfile/dex/dex_file_verifier.h
@@ -17,6 +17,7 @@
 #ifndef ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_
 #define ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_
 
+#include <limits>
 #include <unordered_set>
 
 #include "base/hash_map.h"
@@ -52,7 +53,11 @@
         verify_checksum_(verify_checksum),
         header_(&dex_file->GetHeader()),
         ptr_(nullptr),
-        previous_item_(nullptr)  {
+        previous_item_(nullptr),
+        angle_bracket_start_index_(std::numeric_limits<size_t>::max()),
+        angle_bracket_end_index_(std::numeric_limits<size_t>::max()),
+        angle_init_angle_index_(std::numeric_limits<size_t>::max()),
+        angle_clinit_angle_index_(std::numeric_limits<size_t>::max()) {
   }
 
   bool Verify();
@@ -85,15 +90,10 @@
                                 uint32_t class_access_flags,
                                 dex::TypeIndex class_type_index,
                                 uint32_t code_offset,
-                                std::unordered_set<uint32_t>* direct_method_indexes,
+                                ClassDataItemIterator* direct_it,
                                 bool expect_direct);
-  bool CheckOrderAndGetClassDef(bool is_field,
-                                const char* type_descr,
-                                uint32_t curr_index,
-                                uint32_t prev_index,
-                                bool* have_class,
-                                dex::TypeIndex* class_type_index,
-                                const DexFile::ClassDef** class_def);
+  ALWAYS_INLINE
+  bool CheckOrder(const char* type_descr, uint32_t curr_index, uint32_t prev_index);
   bool CheckStaticFieldTypes(const DexFile::ClassDef* class_def);
 
   bool CheckPadding(size_t offset, uint32_t aligned_offset, DexFile::MapItemType type);
@@ -113,7 +113,7 @@
   // method, if necessary (and return it), or use the given values.
   template <bool kDirect>
   bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it,
-                                      std::unordered_set<uint32_t>* direct_method_indexes,
+                                      ClassDataItemIterator* direct_it,
                                       bool* have_class,
                                       dex::TypeIndex* class_type_index,
                                       const DexFile::ClassDef** class_def);
@@ -124,9 +124,12 @@
   bool CheckIntraAnnotationItem();
   bool CheckIntraAnnotationsDirectoryItem();
 
-  bool CheckIntraSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type);
-  bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type);
-  bool CheckIntraDataSection(size_t offset, uint32_t count, DexFile::MapItemType type);
+  template <DexFile::MapItemType kType>
+  bool CheckIntraSectionIterate(size_t offset, uint32_t count);
+  template <DexFile::MapItemType kType>
+  bool CheckIntraIdSection(size_t offset, uint32_t count);
+  template <DexFile::MapItemType kType>
+  bool CheckIntraDataSection(size_t offset, uint32_t count);
   bool CheckIntraSection();
 
   bool CheckOffsetToTypeMap(size_t offset, uint16_t type);
@@ -161,7 +164,7 @@
   // error if not. If there is an error, null is returned.
   const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt);
   const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt);
-  const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt);
+  const DexFile::ProtoId* CheckLoadProtoId(dex::ProtoIndex idx, const char* error_fmt);
 
   void ErrorStringPrintf(const char* fmt, ...)
       __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
@@ -197,6 +200,8 @@
   // Check validity of given method if it's a constructor or class initializer.
   bool CheckConstructorProperties(uint32_t method_index, uint32_t constructor_flags);
 
+  void FindStringRangesForMethodNames();
+
   const DexFile* const dex_file_;
   const uint8_t* const begin_;
   const size_t size_;
@@ -239,6 +244,20 @@
 
   // Set of type ids for which there are ClassDef elements in the dex file.
   std::unordered_set<decltype(DexFile::ClassDef::class_idx_)> defined_classes_;
+
+  // Cached string indices for "interesting" entries wrt/ method names. Will be populated by
+  // FindStringRangesForMethodNames (which is automatically called before verifying the
+  // classdataitem section).
+  //
+  // Strings starting with '<' are in the range
+  //    [angle_bracket_start_index_,angle_bracket_end_index_).
+  // angle_init_angle_index_ and angle_clinit_angle_index_ denote the indices of "<init>" and
+  // angle_clinit_angle_index_, respectively. If any value is not found, the corresponding
+  // index will be larger than any valid string index for this dex file.
+  size_t angle_bracket_start_index_;
+  size_t angle_bracket_end_index_;
+  size_t angle_init_angle_index_;
+  size_t angle_clinit_angle_index_;
 };
 
 }  // namespace art
diff --git a/libdexfile/dex/dex_file_verifier_test.cc b/libdexfile/dex/dex_file_verifier_test.cc
index 4c3cf77..c9bac0f 100644
--- a/libdexfile/dex/dex_file_verifier_test.cc
+++ b/libdexfile/dex/dex_file_verifier_test.cc
@@ -161,7 +161,7 @@
       "method_id_proto_idx",
       [](DexFile* dex_file) {
         DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
-        method_id->proto_idx_ = 0xFF;
+        method_id->proto_idx_ = dex::ProtoIndex(0xFF);
       },
       "inter_method_id_item proto_idx");
 
@@ -173,7 +173,7 @@
         DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
         method_id->name_idx_ = dex::StringIndex(0xFF);
       },
-      "String index not available for method flags verification");
+      "Bad index for method flags verification");
 }
 
 // Method flags test class generated from the following smali code. The declared-synchronized
@@ -1425,12 +1425,13 @@
           CHECK_LT(method_idx + 1u, dex_file->NumMethodIds());
           CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_,
                    dex_file->GetMethodId(method_idx + 1).name_idx_);
-          CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_ + 1u,
-                   dex_file->GetMethodId(method_idx + 1).proto_idx_);
+          CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_.index_ + 1u,
+                   dex_file->GetMethodId(method_idx + 1).proto_idx_.index_);
           // Their return types should be the same.
-          uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_;
+          dex::ProtoIndex proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_;
           const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx);
-          const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u);
+          dex::ProtoIndex proto2_idx(proto1_idx.index_ + 1u);
+          const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto2_idx);
           CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_);
           // And the first should not have any parameters while the second should have some.
           CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext());
diff --git a/libdexfile/dex/dex_instruction-inl.h b/libdexfile/dex/dex_instruction-inl.h
index 6bef18c..e0cffdd 100644
--- a/libdexfile/dex/dex_instruction-inl.h
+++ b/libdexfile/dex/dex_instruction-inl.h
@@ -508,7 +508,7 @@
   return (FormatOf(Opcode()) == k35c) || (FormatOf(Opcode()) == k45cc);
 }
 
-inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const {
+inline uint32_t Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const {
   DCHECK(HasVarArgs());
 
   /*
@@ -551,6 +551,7 @@
     default:  // case 0
       break;  // Valid, but no need to do anything.
   }
+  return count;
 }
 
 }  // namespace art
diff --git a/libdexfile/dex/dex_instruction.cc b/libdexfile/dex/dex_instruction.cc
index 8862181..8378211 100644
--- a/libdexfile/dex/dex_instruction.cc
+++ b/libdexfile/dex/dex_instruction.cc
@@ -467,10 +467,10 @@
     case k45cc: {
       uint32_t arg[kMaxVarArgRegs];
       GetVarArgs(arg);
-      uint32_t method_idx = VRegB_45cc();
-      uint32_t proto_idx = VRegH_45cc();
+      uint16_t method_idx = VRegB_45cc();
+      dex::ProtoIndex proto_idx(VRegH_45cc());
       os << opcode << " {";
-      for (int i = 0; i < VRegA_45cc(); ++i) {
+      for (uint32_t i = 0; i < VRegA_45cc(); ++i) {
         if (i != 0) {
           os << ", ";
         }
@@ -478,7 +478,8 @@
       }
       os << "}";
       if (file != nullptr) {
-        os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
+        os << ", " << file->PrettyMethod(method_idx)
+           << ", " << file->GetShorty(proto_idx)
            << " // ";
       } else {
         os << ", ";
@@ -490,18 +491,19 @@
       switch (Opcode()) {
         case INVOKE_POLYMORPHIC_RANGE: {
           if (file != nullptr) {
-            uint32_t method_idx = VRegB_4rcc();
-            uint32_t proto_idx = VRegH_4rcc();
+            uint16_t method_idx = VRegB_4rcc();
+            dex::ProtoIndex proto_idx(VRegH_4rcc());
             os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
-               << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
+               << "}, " << file->PrettyMethod(method_idx)
+               << ", " << file->GetShorty(dex::ProtoIndex(proto_idx))
                << " // method@" << method_idx << ", proto@" << proto_idx;
             break;
           }
         }
         FALLTHROUGH_INTENDED;
         default: {
-          uint32_t method_idx = VRegB_4rcc();
-          uint32_t proto_idx = VRegH_4rcc();
+          uint16_t method_idx = VRegB_4rcc();
+          dex::ProtoIndex proto_idx(VRegH_4rcc());
           os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
              << "}, method@" << method_idx << ", proto@" << proto_idx;
         }
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
index bf50836..6807025 100644
--- a/libdexfile/dex/dex_instruction.h
+++ b/libdexfile/dex/dex_instruction.h
@@ -462,8 +462,8 @@
 
   // Fills the given array with the 'arg' array of the instruction.
   bool HasVarArgs() const;
-  void GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const;
-  void GetVarArgs(uint32_t args[kMaxVarArgRegs]) const {
+  uint32_t GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const;
+  uint32_t GetVarArgs(uint32_t args[kMaxVarArgRegs]) const {
     return GetVarArgs(args, Fetch16(0));
   }
 
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
index b62d044..1aaeabd 100644
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ b/libdexfile/dex/hidden_api_access_flags.h
@@ -86,12 +86,10 @@
   }
 
   static ALWAYS_INLINE ApiList DecodeFromRuntime(uint32_t runtime_access_flags) {
-    if ((runtime_access_flags & kAccIntrinsic) != 0) {
-      return kWhitelist;
-    } else {
-      uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift;
-      return static_cast<ApiList>(int_value);
-    }
+    // This is used in the fast path, only DCHECK here.
+    DCHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u);
+    uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift;
+    return static_cast<ApiList>(int_value);
   }
 
   static ALWAYS_INLINE uint32_t EncodeForRuntime(uint32_t runtime_access_flags, ApiList value) {
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index 2425a58..be82fff 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -58,6 +58,7 @@
 static constexpr uint32_t kAccSkipAccessChecks =      0x00080000;  // method (runtime, not native)
 // Used by a class to denote that the verifier has attempted to check it at least once.
 static constexpr uint32_t kAccVerificationAttempted = 0x00080000;  // class (runtime)
+static constexpr uint32_t kAccSkipHiddenApiChecks =   0x00100000;  // class (runtime)
 // This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
 // that it was copied from its declaring class into another class. All methods marked kAccMiranda
 // and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
diff --git a/libdexfile/dex/type_lookup_table_test.cc b/libdexfile/dex/type_lookup_table_test.cc
index b6ab6da..6c3d291 100644
--- a/libdexfile/dex/type_lookup_table_test.cc
+++ b/libdexfile/dex/type_lookup_table_test.cc
@@ -18,7 +18,7 @@
 
 #include <memory>
 
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
 #include "dex/dex_file-inl.h"
 #include "dex/utf-inl.h"
 #include "scoped_thread_state_change-inl.h"
@@ -26,10 +26,9 @@
 namespace art {
 
 using DescriptorClassDefIdxPair = std::pair<const char*, uint32_t>;
-class TypeLookupTableTest : public CommonRuntimeTestWithParam<DescriptorClassDefIdxPair> {};
+class TypeLookupTableTest : public CommonArtTestWithParam<DescriptorClassDefIdxPair> {};
 
 TEST_F(TypeLookupTableTest, CreateLookupTable) {
-  ScopedObjectAccess soa(Thread::Current());
   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Lookup"));
   std::unique_ptr<TypeLookupTable> table(TypeLookupTable::Create(*dex_file));
   ASSERT_NE(nullptr, table.get());
@@ -38,7 +37,6 @@
 }
 
 TEST_P(TypeLookupTableTest, Find) {
-  ScopedObjectAccess soa(Thread::Current());
   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Lookup"));
   std::unique_ptr<TypeLookupTable> table(TypeLookupTable::Create(*dex_file));
   ASSERT_NE(nullptr, table.get());
diff --git a/libprofile/Android.bp b/libprofile/Android.bp
new file mode 100644
index 0000000..b9883f6
--- /dev/null
+++ b/libprofile/Android.bp
@@ -0,0 +1,104 @@
+//
+// Copyright (C) 2018 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.
+//
+
+cc_defaults {
+    name: "libprofile_defaults",
+    defaults: ["art_defaults"],
+    host_supported: true,
+    srcs: [
+        "profile/profile_compilation_info.cc",
+    ],
+    target: {
+        android: {
+            static_libs: [
+                // ZipArchive support, the order matters here to get all symbols.
+                "libziparchive",
+                "libz",
+            ],
+	    shared_libs: [
+	        // For android::FileMap used by libziparchive.
+                "libutils",
+	    ],
+        },
+        host: {
+            shared_libs: [
+                "libziparchive",
+                "libz",
+            ],
+        },
+    },
+    //generated_sources: ["art_libartbase_operator_srcs"],
+    cflags: ["-DBUILDING_LIBART=1"],
+    shared_libs: [
+        "libartbase",
+        "libdexfile",
+        "libartbase",
+	// For atrace.
+        "libcutils",
+    ],
+    export_include_dirs: ["."],
+    // ART's macros.h depends on libbase's macros.h.
+    // Note: runtime_options.h depends on cmdline. But we don't really want to export this
+    //       generically. dex2oat takes care of it itself.
+    export_shared_lib_headers: ["libbase"],
+}
+
+art_cc_library {
+    name: "libprofile",
+    defaults: ["libprofile_defaults"],
+    // Leave the symbols in the shared library so that stack unwinders can
+    // produce meaningful name resolution.
+    strip: {
+        keep_symbols: true,
+    },
+    shared_libs: [
+        "libbase",
+        "libziparchive",
+    ],
+    export_shared_lib_headers: ["libbase"],
+}
+
+art_cc_library {
+    name: "libprofiled",
+    defaults: [
+        "art_debug_defaults",
+        "libprofile_defaults",
+    ],
+    shared_libs: [
+        "libbase",
+        "libziparchive",
+    ],
+    export_shared_lib_headers: ["libbase"],
+}
+
+// For now many of these tests still use CommonRuntimeTest, almost universally because of
+// ScratchFile and related.
+// TODO: Remove CommonRuntimeTest dependency from these tests.
+art_cc_test {
+    name: "art_libprofile_tests",
+    defaults: [
+        "art_gtest_defaults",
+    ],
+    srcs: [
+        "profile/profile_compilation_info_test.cc",
+    ],
+    shared_libs: [
+        "libartbased",
+        "libdexfiled",
+        "libartbased",
+        "libziparchive",
+    ],
+}
diff --git a/runtime/jit/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
similarity index 98%
rename from runtime/jit/profile_compilation_info.cc
rename to libprofile/profile/profile_compilation_info.cc
index 7c21916..748e24e 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -46,7 +46,6 @@
 #include "base/utils.h"
 #include "base/zip_archive.h"
 #include "dex/dex_file_loader.h"
-#include "jit/profiling_info.h"
 
 namespace art {
 
@@ -70,12 +69,12 @@
 static constexpr uint8_t kIsMissingTypesEncoding = 6;
 static constexpr uint8_t kIsMegamorphicEncoding = 7;
 
-static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
-              "InlineCache::kIndividualCacheSize does not have the expect type size");
-static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding,
-              "InlineCache::kIndividualCacheSize is larger than expected");
-static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding,
-              "InlineCache::kIndividualCacheSize is larger than expected");
+static_assert(sizeof(ProfileCompilationInfo::kIndividualInlineCacheSize) == sizeof(uint8_t),
+              "InlineCache::kIndividualInlineCacheSize does not have the expect type size");
+static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize < kIsMegamorphicEncoding,
+              "InlineCache::kIndividualInlineCacheSize is larger than expected");
+static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize < kIsMissingTypesEncoding,
+              "InlineCache::kIndividualInlineCacheSize is larger than expected");
 
 static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
   return kDebugIgnoreChecksum || dex_file_checksum == checksum;
@@ -97,9 +96,7 @@
 
 ProfileCompilationInfo::~ProfileCompilationInfo() {
   VLOG(profiler) << Dumpable<MemStats>(allocator_.GetMemStats());
-  for (DexFileData* data : info_) {
-    delete data;
-  }
+  ClearData();
 }
 
 void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
@@ -120,7 +117,7 @@
   }
 
   // Check if the adding the type will cause the cache to become megamorphic.
-  if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) {
+  if (classes.size() + 1 >= ProfileCompilationInfo::kIndividualInlineCacheSize) {
     is_megamorphic = true;
     classes.clear();
     return;
@@ -503,7 +500,7 @@
       continue;
     }
 
-    DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize);
+    DCHECK_LT(classes.size(), ProfileCompilationInfo::kIndividualInlineCacheSize);
     DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
 
     SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
@@ -1351,7 +1348,7 @@
     if (!filter_fn(profile_line_headers[k].dex_location, profile_line_headers[k].checksum)) {
       // We have to skip the line. Advanced the current pointer of the buffer.
       size_t profile_line_size =
-           profile_line_headers[k].class_set_size +
+           profile_line_headers[k].class_set_size * sizeof(uint16_t) +
            profile_line_headers[k].method_region_size_bytes +
            DexFileData::ComputeBitmapStorage(profile_line_headers[k].num_method_ids);
       uncompressed_data.Advance(profile_line_size);
@@ -2107,4 +2104,12 @@
   return true;
 }
 
+void ProfileCompilationInfo::ClearData() {
+  for (DexFileData* data : info_) {
+    delete data;
+  }
+  info_.clear();
+  profile_key_map_.clear();
+}
+
 }  // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h
similarity index 98%
rename from runtime/jit/profile_compilation_info.h
rename to libprofile/profile/profile_compilation_info.h
index f4c8c72..e28c5f1 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/libprofile/profile/profile_compilation_info.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
-#define ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
+#ifndef ART_LIBPROFILE_PROFILE_PROFILE_COMPILATION_INFO_H_
+#define ART_LIBPROFILE_PROFILE_PROFILE_COMPILATION_INFO_H_
 
 #include <set>
 #include <vector>
@@ -75,6 +75,8 @@
 
   static const char* kDexMetadataProfileEntry;
 
+  static constexpr uint8_t kIndividualInlineCacheSize = 5;
+
   // Data structures for encoding the offline representation of inline caches.
   // This is exposed as public in order to make it available to dex2oat compilations
   // (see compiler/optimizing/inliner.cc).
@@ -443,6 +445,9 @@
   // Checks if the profile is empty.
   bool IsEmpty() const;
 
+  // Clears all the data from the profile.
+  void ClearData();
+
  private:
   enum ProfileLoadStatus {
     kProfileLoadWouldOverwiteData,
@@ -809,4 +814,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
+#endif  // ART_LIBPROFILE_PROFILE_PROFILE_COMPILATION_INFO_H_
diff --git a/runtime/jit/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc
similarity index 95%
rename from runtime/jit/profile_compilation_info_test.cc
rename to libprofile/profile/profile_compilation_info_test.cc
index 4e3774e..b3262a7 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/libprofile/profile/profile_compilation_info_test.cc
@@ -26,10 +26,10 @@
 #include "dex/method_reference.h"
 #include "dex/type_reference.h"
 #include "handle_scope-inl.h"
-#include "jit/profile_compilation_info.h"
 #include "linear_alloc.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
+#include "profile/profile_compilation_info.h"
 #include "scoped_thread_state_change-inl.h"
 #include "ziparchive/zip_writer.h"
 
@@ -1160,7 +1160,7 @@
   ProfileCompilationInfo loaded_info;
   ASSERT_TRUE(profile.GetFile()->ResetOffset());
 
-  // Filter out dex locations. Keep only dex_location1 and dex_location2.
+  // Filter out dex locations. Keep only dex_location1 and dex_location3.
   ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
       [](const std::string& dex_location, uint32_t checksum) -> bool {
           return (dex_location == "dex_location1" && checksum == 1)
@@ -1303,4 +1303,69 @@
   }
 }
 
+// Regression test: we were failing to do a filtering loading when the filtered dex file
+// contained profiled classes.
+TEST_F(ProfileCompilationInfoTest, FilteredLoadingWithClasses) {
+  ScratchFile profile;
+
+  // Save a profile with 2 dex files containing just classes.
+  ProfileCompilationInfo saved_info;
+  uint16_t item_count = 1000;
+  for (uint16_t i = 0; i < item_count; i++) {
+    ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, dex::TypeIndex(i), &saved_info));
+    ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &saved_info));
+  }
+
+  ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+  ASSERT_EQ(0, profile.GetFile()->Flush());
+
+
+  // Filter out dex locations: kepp only dex_location2.
+  ProfileCompilationInfo loaded_info;
+  ASSERT_TRUE(profile.GetFile()->ResetOffset());
+  ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
+      [](const std::string& dex_location, uint32_t checksum) -> bool {
+          return (dex_location == "dex_location2" && checksum == 2);
+        };
+  ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
+
+  // Compute the expectation.
+  ProfileCompilationInfo expected_info;
+  for (uint16_t i = 0; i < item_count; i++) {
+    ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &expected_info));
+  }
+
+  // Validate the expectation.
+  ASSERT_TRUE(loaded_info.Equals(expected_info));
+}
+
+
+TEST_F(ProfileCompilationInfoTest, ClearData) {
+  ProfileCompilationInfo info;
+  for (uint16_t i = 0; i < 10; i++) {
+    ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
+  }
+  ASSERT_FALSE(IsEmpty(info));
+  info.ClearData();
+  ASSERT_TRUE(IsEmpty(info));
+}
+
+TEST_F(ProfileCompilationInfoTest, ClearDataAndSave) {
+  ProfileCompilationInfo info;
+  for (uint16_t i = 0; i < 10; i++) {
+    ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
+  }
+  info.ClearData();
+
+  ScratchFile profile;
+  ASSERT_TRUE(info.Save(GetFd(profile)));
+  ASSERT_EQ(0, profile.GetFile()->Flush());
+
+  // Check that we get back what we saved.
+  ProfileCompilationInfo loaded_info;
+  ASSERT_TRUE(profile.GetFile()->ResetOffset());
+  ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+  ASSERT_TRUE(loaded_info.Equals(info));
+}
+
 }  // namespace art
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 8c21538..77dede3 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -24,6 +24,8 @@
             shared_libs: ["libcutils"],
         },
     },
+    // b/79417743, oatdump 32-bit tests failed with clang lld
+    use_clang_lld: false,
     header_libs: [
         "art_cmdlineparser_headers",
     ],
@@ -37,6 +39,8 @@
         "libart-compiler",
         "libart-disassembler",
         "libdexfile",
+        "libartbase",
+        "libprofile",
         "libbase",
     ],
 }
@@ -52,6 +56,8 @@
         "libartd-compiler",
         "libartd-disassembler",
         "libdexfiled",
+        "libartbased",
+        "libprofiled",
         "libbase",
     ],
 }
@@ -77,6 +83,8 @@
     static_libs: [
         "libart",
         "libdexfile",
+        "libprofile",
+        "libartbase",
         "libart-compiler",
         "libart-disassembler",
         "libvixl-arm",
@@ -111,6 +119,8 @@
     static_libs: [
         "libartd",
         "libdexfiled",
+        "libprofiled",
+        "libartbased",
         "libartd-compiler",
         "libartd-disassembler",
         "libvixld-arm",
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 6a68b55..5c20efa 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -753,7 +753,7 @@
       kByteKindQuickMethodHeader,
       kByteKindCodeInfoLocationCatalog,
       kByteKindCodeInfoDexRegisterMap,
-      kByteKindCodeInfoEncoding,
+      kByteKindCodeInfo,
       kByteKindCodeInfoInvokeInfo,
       kByteKindCodeInfoStackMasks,
       kByteKindCodeInfoRegisterMasks,
@@ -800,7 +800,7 @@
       if (sum > 0) {
         Dump(os, "Code                            ", bits[kByteKindCode], sum);
         Dump(os, "QuickMethodHeader               ", bits[kByteKindQuickMethodHeader], sum);
-        Dump(os, "CodeInfoEncoding                ", bits[kByteKindCodeInfoEncoding], sum);
+        Dump(os, "CodeInfo                        ", bits[kByteKindCodeInfo], sum);
         Dump(os, "CodeInfoLocationCatalog         ", bits[kByteKindCodeInfoLocationCatalog], sum);
         Dump(os, "CodeInfoDexRegisterMap          ", bits[kByteKindCodeInfoDexRegisterMap], sum);
         Dump(os, "CodeInfoStackMasks              ", bits[kByteKindCodeInfoStackMasks], sum);
@@ -819,7 +819,7 @@
                stack_map_bits,
                "stack map");
           Dump(os,
-               "StackMapDexPcEncoding         ",
+               "StackMapDexPc                 ",
                bits[kByteKindStackMapDexPc],
                stack_map_bits,
                "stack map");
@@ -1732,8 +1732,7 @@
    public:
     explicit StackMapsHelper(const uint8_t* raw_code_info, InstructionSet instruction_set)
         : code_info_(raw_code_info),
-          encoding_(code_info_.ExtractEncoding()),
-          number_of_stack_maps_(code_info_.GetNumberOfStackMaps(encoding_)),
+          number_of_stack_maps_(code_info_.GetNumberOfStackMaps()),
           indexes_(),
           offset_(static_cast<uint32_t>(-1)),
           stack_map_index_(0u),
@@ -1741,11 +1740,11 @@
       if (number_of_stack_maps_ != 0u) {
         // Check if native PCs are ordered.
         bool ordered = true;
-        StackMap last = code_info_.GetStackMapAt(0u, encoding_);
+        StackMap last = code_info_.GetStackMapAt(0u);
         for (size_t i = 1; i != number_of_stack_maps_; ++i) {
-          StackMap current = code_info_.GetStackMapAt(i, encoding_);
-          if (last.GetNativePcOffset(encoding_.stack_map.encoding, instruction_set) >
-              current.GetNativePcOffset(encoding_.stack_map.encoding, instruction_set)) {
+          StackMap current = code_info_.GetStackMapAt(i);
+          if (last.GetNativePcOffset(instruction_set) >
+              current.GetNativePcOffset(instruction_set)) {
             ordered = false;
             break;
           }
@@ -1760,18 +1759,15 @@
           std::sort(indexes_.begin(),
                     indexes_.end(),
                     [this](size_t lhs, size_t rhs) {
-                      StackMap left = code_info_.GetStackMapAt(lhs, encoding_);
-                      uint32_t left_pc = left.GetNativePcOffset(encoding_.stack_map.encoding,
-                                                                instruction_set_);
-                      StackMap right = code_info_.GetStackMapAt(rhs, encoding_);
-                      uint32_t right_pc = right.GetNativePcOffset(encoding_.stack_map.encoding,
-                                                                  instruction_set_);
+                      StackMap left = code_info_.GetStackMapAt(lhs);
+                      uint32_t left_pc = left.GetNativePcOffset(instruction_set_);
+                      StackMap right = code_info_.GetStackMapAt(rhs);
+                      uint32_t right_pc = right.GetNativePcOffset(instruction_set_);
                       // If the PCs are the same, compare indexes to preserve the original order.
                       return (left_pc < right_pc) || (left_pc == right_pc && lhs < rhs);
                     });
         }
-        offset_ = GetStackMapAt(0).GetNativePcOffset(encoding_.stack_map.encoding,
-                                                     instruction_set_);
+        offset_ = GetStackMapAt(0).GetNativePcOffset(instruction_set_);
       }
     }
 
@@ -1779,10 +1775,6 @@
       return code_info_;
     }
 
-    const CodeInfoEncoding& GetEncoding() const {
-      return encoding_;
-    }
-
     uint32_t GetOffset() const {
       return offset_;
     }
@@ -1795,8 +1787,7 @@
       ++stack_map_index_;
       offset_ = (stack_map_index_ == number_of_stack_maps_)
           ? static_cast<uint32_t>(-1)
-          : GetStackMapAt(stack_map_index_).GetNativePcOffset(encoding_.stack_map.encoding,
-                                                              instruction_set_);
+          : GetStackMapAt(stack_map_index_).GetNativePcOffset(instruction_set_);
     }
 
    private:
@@ -1805,11 +1796,10 @@
         i = indexes_[i];
       }
       DCHECK_LT(i, number_of_stack_maps_);
-      return code_info_.GetStackMapAt(i, encoding_);
+      return code_info_.GetStackMapAt(i);
     }
 
     const CodeInfo code_info_;
-    const CodeInfoEncoding encoding_;
     const size_t number_of_stack_maps_;
     dchecked_vector<size_t> indexes_;  // Used if stack map native PCs are not ordered.
     uint32_t offset_;
@@ -1835,79 +1825,75 @@
       StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_);
       MethodInfo method_info(oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo());
       {
-        CodeInfoEncoding encoding(helper.GetEncoding());
-        StackMapEncoding stack_map_encoding(encoding.stack_map.encoding);
-        const size_t num_stack_maps = encoding.stack_map.num_entries;
-        if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfoEncoding,
-                                   encoding.HeaderSize() * kBitsPerByte,
+        const CodeInfo code_info = helper.GetCodeInfo();
+        const BitTable<StackMap::kCount>& stack_maps = code_info.stack_maps_;
+        const size_t num_stack_maps = stack_maps.NumRows();
+        if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfo,
+                                   code_info.size_ * kBitsPerByte,
                                    oat_method.GetVmapTable())) {
           // Stack maps
           stats_.AddBits(
               Stats::kByteKindStackMapNativePc,
-              stack_map_encoding.GetNativePcEncoding().BitSize() * num_stack_maps);
+              stack_maps.NumColumnBits(StackMap::kNativePcOffset) * num_stack_maps);
           stats_.AddBits(
               Stats::kByteKindStackMapDexPc,
-              stack_map_encoding.GetDexPcEncoding().BitSize() * num_stack_maps);
+              stack_maps.NumColumnBits(StackMap::kDexPc) * num_stack_maps);
           stats_.AddBits(
               Stats::kByteKindStackMapDexRegisterMap,
-              stack_map_encoding.GetDexRegisterMapEncoding().BitSize() * num_stack_maps);
+              stack_maps.NumColumnBits(StackMap::kDexRegisterMapOffset) * num_stack_maps);
           stats_.AddBits(
               Stats::kByteKindStackMapInlineInfoIndex,
-              stack_map_encoding.GetInlineInfoEncoding().BitSize() * num_stack_maps);
+              stack_maps.NumColumnBits(StackMap::kInlineInfoIndex) * num_stack_maps);
           stats_.AddBits(
               Stats::kByteKindStackMapRegisterMaskIndex,
-              stack_map_encoding.GetRegisterMaskIndexEncoding().BitSize() * num_stack_maps);
+              stack_maps.NumColumnBits(StackMap::kRegisterMaskIndex) * num_stack_maps);
           stats_.AddBits(
               Stats::kByteKindStackMapStackMaskIndex,
-              stack_map_encoding.GetStackMaskIndexEncoding().BitSize() * num_stack_maps);
+              stack_maps.NumColumnBits(StackMap::kStackMaskIndex) * num_stack_maps);
 
           // Stack masks
           stats_.AddBits(
               Stats::kByteKindCodeInfoStackMasks,
-              encoding.stack_mask.encoding.BitSize() * encoding.stack_mask.num_entries);
+              code_info.stack_masks_.size_in_bits());
 
           // Register masks
           stats_.AddBits(
               Stats::kByteKindCodeInfoRegisterMasks,
-              encoding.register_mask.encoding.BitSize() * encoding.register_mask.num_entries);
+              code_info.register_masks_.DataBitSize());
 
           // Invoke infos
-          if (encoding.invoke_info.num_entries > 0u) {
-            stats_.AddBits(
-                Stats::kByteKindCodeInfoInvokeInfo,
-                encoding.invoke_info.encoding.BitSize() * encoding.invoke_info.num_entries);
-          }
+          stats_.AddBits(
+              Stats::kByteKindCodeInfoInvokeInfo,
+              code_info.invoke_infos_.DataBitSize());
 
           // Location catalog
           const size_t location_catalog_bytes =
-              helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding);
+              helper.GetCodeInfo().GetDexRegisterLocationCatalogSize();
           stats_.AddBits(Stats::kByteKindCodeInfoLocationCatalog,
                          kBitsPerByte * location_catalog_bytes);
           // Dex register bytes.
           const size_t dex_register_bytes =
-              helper.GetCodeInfo().GetDexRegisterMapsSize(encoding,
-                                                          code_item_accessor.RegistersSize());
+              helper.GetCodeInfo().GetDexRegisterMapsSize(code_item_accessor.RegistersSize());
           stats_.AddBits(
               Stats::kByteKindCodeInfoDexRegisterMap,
               kBitsPerByte * dex_register_bytes);
 
           // Inline infos.
-          const size_t num_inline_infos = encoding.inline_info.num_entries;
+          const BitTable<InlineInfo::kCount>& inline_infos = code_info.inline_infos_;
+          const size_t num_inline_infos = inline_infos.NumRows();
           if (num_inline_infos > 0u) {
             stats_.AddBits(
                 Stats::kByteKindInlineInfoMethodIndexIdx,
-                encoding.inline_info.encoding.GetMethodIndexIdxEncoding().BitSize() *
-                    num_inline_infos);
+                inline_infos.NumColumnBits(InlineInfo::kMethodIndexIdx) * num_inline_infos);
             stats_.AddBits(
                 Stats::kByteKindInlineInfoDexPc,
-                encoding.inline_info.encoding.GetDexPcEncoding().BitSize() * num_inline_infos);
+                inline_infos.NumColumnBits(InlineInfo::kDexPc) * num_inline_infos);
             stats_.AddBits(
                 Stats::kByteKindInlineInfoExtraData,
-                encoding.inline_info.encoding.GetExtraDataEncoding().BitSize() * num_inline_infos);
+                inline_infos.NumColumnBits(InlineInfo::kExtraData) * num_inline_infos);
             stats_.AddBits(
                 Stats::kByteKindInlineInfoDexRegisterMap,
-                encoding.inline_info.encoding.GetDexRegisterMapEncoding().BitSize() *
-                    num_inline_infos);
+                inline_infos.NumColumnBits(InlineInfo::kDexRegisterMapOffset) * num_inline_infos);
             stats_.AddBits(Stats::kByteKindInlineInfoIsLast, num_inline_infos);
           }
         }
@@ -1922,7 +1908,6 @@
           DCHECK(stack_map.IsValid());
           stack_map.Dump(vios,
                          helper.GetCodeInfo(),
-                         helper.GetEncoding(),
                          method_info,
                          oat_method.GetCodeOffset(),
                          code_item_accessor.RegistersSize(),
@@ -2156,7 +2141,8 @@
       oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(oat_location);
     }
     if (oat_file == nullptr) {
-      oat_file = OatFile::Open(oat_location,
+      oat_file = OatFile::Open(/* zip_fd */ -1,
+                               oat_location,
                                oat_location,
                                nullptr,
                                nullptr,
@@ -3044,7 +3030,8 @@
     // We need to map the oat file in the low 4gb or else the fixup wont be able to fit oat file
     // pointers into 32 bit pointer sized ArtMethods.
     std::string error_msg;
-    std::unique_ptr<OatFile> oat_file(OatFile::Open(options->app_oat_,
+    std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                    options->app_oat_,
                                                     options->app_oat_,
                                                     nullptr,
                                                     nullptr,
@@ -3167,7 +3154,8 @@
                  << "oatdump might fail if the oat file does not contain the dex code.";
   }
   std::string error_msg;
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_filename,
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                  oat_filename,
                                                   oat_filename,
                                                   nullptr,
                                                   nullptr,
@@ -3192,7 +3180,8 @@
                         std::string& output_name,
                         bool no_bits) {
   std::string error_msg;
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_filename,
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                  oat_filename,
                                                   oat_filename,
                                                   nullptr,
                                                   nullptr,
@@ -3239,7 +3228,8 @@
 
     if (oat_filename != nullptr) {
       std::string error_msg;
-      std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_filename,
+      std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                      oat_filename,
                                                       oat_filename,
                                                       nullptr,
                                                       nullptr,
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index b85730d..bbe89ca 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -162,7 +162,6 @@
         // Code and dex code do not show up if list only.
         expected_prefixes.push_back("DEX CODE:");
         expected_prefixes.push_back("CODE:");
-        expected_prefixes.push_back("CodeInfoEncoding");
         expected_prefixes.push_back("CodeInfoInlineInfo");
       }
       if (mode == kModeArt) {
diff --git a/openjdkjvm/Android.bp b/openjdkjvm/Android.bp
index a178993..907315e 100644
--- a/openjdkjvm/Android.bp
+++ b/openjdkjvm/Android.bp
@@ -29,7 +29,10 @@
 art_cc_library {
     name: "libopenjdkjvm",
     defaults: ["libopenjdkjvm_defaults"],
-    shared_libs: ["libart"],
+    shared_libs: [
+        "libart",
+        "libartbase",
+    ],
 }
 
 art_cc_library {
@@ -38,5 +41,8 @@
         "art_debug_defaults",
         "libopenjdkjvm_defaults",
     ],
-    shared_libs: ["libartd"],
+    shared_libs: [
+        "libartd",
+        "libartbased",
+    ],
 }
diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc
index 975d194..be1ab78 100644
--- a/openjdkjvm/OpenjdkJvm.cc
+++ b/openjdkjvm/OpenjdkJvm.cc
@@ -48,8 +48,8 @@
 #include "common_throws.h"
 #include "gc/heap.h"
 #include "handle_scope-inl.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/string-inl.h"
 #include "monitor.h"
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index 81b69e8..d8902d6 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -71,6 +71,7 @@
         "libart-compiler",
         "libart-dexlayout",
         "libdexfile",
+        "libartbase",
     ],
 }
 
@@ -85,5 +86,6 @@
         "libartd-compiler",
         "libartd-dexlayout",
         "libdexfiled",
+        "libartbased",
     ],
 }
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index ef51519..59f61e2 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -43,7 +43,7 @@
 #include "base/logging.h"  // For gLogVerbosity.
 #include "base/mutex.h"
 #include "events-inl.h"
-#include "jni_env_ext-inl.h"
+#include "jni/jni_env_ext-inl.h"
 #include "obj_ptr-inl.h"
 #include "object_tagging.h"
 #include "runtime.h"
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 73cc601..82f3866 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -47,8 +47,8 @@
 #include "base/strlcpy.h"
 #include "base/mutex.h"
 #include "events.h"
-#include "java_vm_ext.h"
-#include "jni_env_ext.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_env_ext.h"
 #include "jvmti.h"
 #include "ti_breakpoint.h"
 
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index d4e5df1..2f24d7e 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -41,7 +41,7 @@
 #include "dex/modifiers.h"
 #include "events-inl.h"
 #include "jit/jit.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/object_array-inl.h"
 #include "nativehelper/scoped_local_ref.h"
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index 74ffb84..6a8ba48 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -23,7 +23,7 @@
 
 #include "base/mutex-inl.h"
 #include "events.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "scoped_thread_state_change-inl.h"
 #include "ti_breakpoint.h"
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index de67871..5cb4299 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -44,8 +44,8 @@
 #include "gc/scoped_gc_critical_section.h"
 #include "handle_scope-inl.h"
 #include "instrumentation.h"
-#include "jni_env_ext-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_env_ext-inl.h"
+#include "jni/jni_internal.h"
 #include "mirror/class.h"
 #include "mirror/object-inl.h"
 #include "monitor.h"
diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h
index 6990042..d9b8a84 100644
--- a/openjdkjvmti/jvmti_weak_table-inl.h
+++ b/openjdkjvmti/jvmti_weak_table-inl.h
@@ -41,7 +41,7 @@
 #include "art_jvmti.h"
 #include "gc/allocation_listener.h"
 #include "instrumentation.h"
-#include "jni_env_ext-inl.h"
+#include "jni/jni_env_ext-inl.h"
 #include "jvmti_allocator.h"
 #include "mirror/class.h"
 #include "mirror/object.h"
diff --git a/openjdkjvmti/jvmti_weak_table.h b/openjdkjvmti/jvmti_weak_table.h
index 5a821c9..cba8ef0 100644
--- a/openjdkjvmti/jvmti_weak_table.h
+++ b/openjdkjvmti/jvmti_weak_table.h
@@ -34,11 +34,11 @@
 
 #include <unordered_map>
 
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "gc/system_weak.h"
 #include "gc_root-inl.h"
-#include "globals.h"
 #include "jvmti.h"
 #include "jvmti_allocator.h"
 #include "mirror/object.h"
diff --git a/openjdkjvmti/object_tagging.h b/openjdkjvmti/object_tagging.h
index b474845..1b8366a 100644
--- a/openjdkjvmti/object_tagging.h
+++ b/openjdkjvmti/object_tagging.h
@@ -34,8 +34,8 @@
 
 #include <unordered_map>
 
+#include "base/globals.h"
 #include "base/mutex.h"
-#include "globals.h"
 #include "jvmti.h"
 #include "jvmti_weak_table.h"
 #include "mirror/object.h"
diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc
index 136b1d3..813aa8e 100644
--- a/openjdkjvmti/ti_breakpoint.cc
+++ b/openjdkjvmti/ti_breakpoint.cc
@@ -41,7 +41,7 @@
 #include "dex/dex_file_annotations.h"
 #include "dex/modifiers.h"
 #include "events-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/object_array-inl.h"
 #include "nativehelper/scoped_local_ref.h"
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 261fe3e..c9d71b4 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -54,8 +54,8 @@
 #include "gc/heap.h"
 #include "gc_root.h"
 #include "handle.h"
-#include "jni_env_ext-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_env_ext-inl.h"
+#include "jni/jni_internal.h"
 #include "mirror/array-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_ext.h"
diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h
index 95278f4..9b04841 100644
--- a/openjdkjvmti/ti_class_loader-inl.h
+++ b/openjdkjvmti/ti_class_loader-inl.h
@@ -36,7 +36,7 @@
 #include "art_field-inl.h"
 #include "handle.h"
 #include "handle_scope.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/object.h"
 #include "mirror/object_array-inl.h"
 #include "well_known_classes.h"
diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc
index 3df5de9..9a32849 100644
--- a/openjdkjvmti/ti_class_loader.cc
+++ b/openjdkjvmti/ti_class_loader.cc
@@ -46,7 +46,7 @@
 #include "instrumentation.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
-#include "jni_env_ext-inl.h"
+#include "jni/jni_env_ext-inl.h"
 #include "jvmti_allocator.h"
 #include "mirror/class.h"
 #include "mirror/class_ext.h"
diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h
index dbe30da..a3857e5 100644
--- a/openjdkjvmti/ti_class_loader.h
+++ b/openjdkjvmti/ti_class_loader.h
@@ -39,13 +39,13 @@
 #include "art_jvmti.h"
 #include "art_method.h"
 #include "base/array_slice.h"
+#include "base/globals.h"
 #include "base/mem_map.h"
 #include "class_linker.h"
 #include "dex/dex_file.h"
 #include "dex/utf.h"
 #include "gc_root-inl.h"
-#include "globals.h"
-#include "jni_env_ext-inl.h"
+#include "jni/jni_env_ext-inl.h"
 #include "jvmti.h"
 #include "linear_alloc.h"
 #include "mirror/array-inl.h"
diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc
index c016966..328e2a1 100644
--- a/openjdkjvmti/ti_field.cc
+++ b/openjdkjvmti/ti_field.cc
@@ -36,7 +36,7 @@
 #include "base/enums.h"
 #include "dex/dex_file_annotations.h"
 #include "dex/modifiers.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/object_array-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index d0a7cf0..d23370b 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -26,8 +26,8 @@
 #include "gc/heap.h"
 #include "gc_root-inl.h"
 #include "java_frame_root_info.h"
-#include "jni_env_ext.h"
-#include "jni_internal.h"
+#include "jni/jni_env_ext.h"
+#include "jni/jni_internal.h"
 #include "jvmti_weak_table-inl.h"
 #include "mirror/class.h"
 #include "mirror/object-inl.h"
diff --git a/openjdkjvmti/ti_jni.cc b/openjdkjvmti/ti_jni.cc
index dd2dda1..b655d6a 100644
--- a/openjdkjvmti/ti_jni.cc
+++ b/openjdkjvmti/ti_jni.cc
@@ -35,8 +35,8 @@
 
 #include "art_jvmti.h"
 #include "base/mutex.h"
-#include "java_vm_ext.h"
-#include "jni_env_ext.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_env_ext.h"
 #include "runtime.h"
 #include "thread-current-inl.h"
 
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index b83310d..c0c312c 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -44,7 +44,7 @@
 #include "events-inl.h"
 #include "gc_root-inl.h"
 #include "jit/jit.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index a23baa5..8a726bc 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -58,7 +58,7 @@
 #include "jdwp/object_registry.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
-#include "jni_env_ext-inl.h"
+#include "jni/jni_env_ext-inl.h"
 #include "jvmti_allocator.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_ext.h"
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index 0323856..227eacd 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -39,13 +39,13 @@
 #include "art_jvmti.h"
 #include "art_method.h"
 #include "base/array_ref.h"
+#include "base/globals.h"
 #include "base/mem_map.h"
 #include "class_linker.h"
 #include "dex/dex_file.h"
 #include "dex/utf.h"
 #include "gc_root-inl.h"
-#include "globals.h"
-#include "jni_env_ext-inl.h"
+#include "jni/jni_env_ext-inl.h"
 #include "jvmti.h"
 #include "linear_alloc.h"
 #include "mirror/array-inl.h"
diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc
index cbb7b53..bcbab14 100644
--- a/openjdkjvmti/ti_search.cc
+++ b/openjdkjvmti/ti_search.cc
@@ -41,7 +41,7 @@
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/object.h"
 #include "mirror/string.h"
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 4526be4..eee8108 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -50,8 +50,8 @@
 #include "dex/dex_file_types.h"
 #include "gc_root.h"
 #include "handle_scope-inl.h"
-#include "jni_env_ext.h"
-#include "jni_internal.h"
+#include "jni/jni_env_ext.h"
+#include "jni/jni_internal.h"
 #include "mirror/class.h"
 #include "mirror/dex_cache.h"
 #include "nativehelper/scoped_local_ref.h"
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index 414139c..cabf9e8 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -43,7 +43,7 @@
 #include "gc/gc_cause.h"
 #include "gc/scoped_gc_critical_section.h"
 #include "gc_root-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class.h"
 #include "mirror/object-inl.h"
 #include "mirror/string.h"
diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc
index c0597ad..e17e61f 100644
--- a/openjdkjvmti/ti_threadgroup.cc
+++ b/openjdkjvmti/ti_threadgroup.cc
@@ -37,7 +37,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "handle_scope-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class.h"
 #include "mirror/object-inl.h"
 #include "mirror/string.h"
diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc
index f31486b..8797553 100644
--- a/openjdkjvmti/transform.cc
+++ b/openjdkjvmti/transform.cc
@@ -39,6 +39,7 @@
 
 #include "art_method.h"
 #include "base/array_ref.h"
+#include "base/globals.h"
 #include "base/mem_map.h"
 #include "class_linker.h"
 #include "dex/dex_file.h"
@@ -47,8 +48,7 @@
 #include "events-inl.h"
 #include "fault_handler.h"
 #include "gc_root-inl.h"
-#include "globals.h"
-#include "jni_env_ext-inl.h"
+#include "jni/jni_env_ext-inl.h"
 #include "jvalue.h"
 #include "jvmti.h"
 #include "linear_alloc.h"
diff --git a/patchoat/Android.bp b/patchoat/Android.bp
index 0e8e517..13c8f47 100644
--- a/patchoat/Android.bp
+++ b/patchoat/Android.bp
@@ -25,6 +25,7 @@
         },
     },
     shared_libs: [
+        "libartbase",
         "libbase",
         "libcrypto", // For computing the digest of image file
     ],
@@ -58,7 +59,6 @@
         "patchoat_test.cc",
     ],
     shared_libs: [
-        "libartd",
         "libcrypto", // For computing the digest of image file
     ],
 }
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index dc9d990..0889a8e 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -610,7 +610,7 @@
     }
   }
 
-  if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+  if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
     // We want to just exit on non-debug builds, not bringing the runtime down
     // in an orderly fashion. So release the following fields.
     runtime.release();
@@ -690,7 +690,7 @@
     }
   }
 
-  if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+  if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
     // We want to just exit on non-debug builds, not bringing the runtime down
     // in an orderly fashion. So release the following fields.
     runtime.release();
diff --git a/profman/Android.bp b/profman/Android.bp
index 163be2b..5aaccb0 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -40,7 +40,9 @@
     defaults: ["profman-defaults"],
     shared_libs: [
         "libart",
+        "libprofile",
         "libdexfile",
+        "libartbase",
     ],
 }
 
@@ -52,7 +54,9 @@
     ],
     shared_libs: [
         "libartd",
+        "libprofiled",
         "libdexfiled",
+        "libartbased",
     ],
 }
 
@@ -61,5 +65,8 @@
     defaults: [
         "art_gtest_defaults",
     ],
+    shared_libs: [
+        "libprofiled",
+    ],
     srcs: ["profile_assistant_test.cc"],
 }
diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc
index 60238e2..89c9eb8 100644
--- a/profman/boot_image_profile.cc
+++ b/profman/boot_image_profile.cc
@@ -21,7 +21,7 @@
 #include "dex/dex_file-inl.h"
 #include "dex/method_reference.h"
 #include "dex/type_reference.h"
-#include "jit/profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 
 namespace art {
 
diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h
index ee55584..c1d6f8e 100644
--- a/profman/profile_assistant.h
+++ b/profman/profile_assistant.h
@@ -21,7 +21,7 @@
 #include <vector>
 
 #include "base/scoped_flock.h"
-#include "jit/profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 
 namespace art {
 
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 72c285a..bd44e49 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -23,10 +23,10 @@
 #include "common_runtime_test.h"
 #include "dex/descriptors_names.h"
 #include "exec_utils.h"
-#include "jit/profile_compilation_info.h"
 #include "linear_alloc.h"
 #include "mirror/class-inl.h"
 #include "obj_ptr-inl.h"
+#include "profile/profile_compilation_info.h"
 #include "profile_assistant.h"
 #include "scoped_thread_state_change-inl.h"
 
@@ -243,8 +243,7 @@
 
   bool CreateProfile(const std::string& profile_file_contents,
                      const std::string& filename,
-                     const std::string& dex_location,
-                     bool skip_verification) {
+                     const std::string& dex_location) {
     ScratchFile class_names_file;
     File* file = class_names_file.GetFile();
     EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length()));
@@ -257,9 +256,6 @@
     argv_str.push_back("--reference-profile-file=" + filename);
     argv_str.push_back("--apk=" + dex_location);
     argv_str.push_back("--dex-location=" + dex_location);
-    if (skip_verification) {
-      argv_str.push_back("--skip-apk-verification");
-    }
     std::string error;
     EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
     return true;
@@ -276,7 +272,6 @@
     argv_str.push_back("--profile-file=" + filename);
     argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
     argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
-    argv_str.push_back("--skip-apk-verification");
     argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file)));
     std::string error;
     EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
@@ -307,8 +302,7 @@
     ScratchFile profile_file;
     EXPECT_TRUE(CreateProfile(input_file_contents,
                               profile_file.GetFilename(),
-                              GetLibCoreDexFileNames()[0],
-                              /* skip_verification */ true));
+                              GetLibCoreDexFileNames()[0]));
     profile_file.GetFile()->ResetOffset();
     EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents));
     return true;
@@ -715,8 +709,7 @@
   ScratchFile profile_file;
   EXPECT_TRUE(CreateProfile(input_file_contents,
                             profile_file.GetFilename(),
-                            GetLibCoreDexFileNames()[0],
-                            /* skip_verification */ true));
+                            GetLibCoreDexFileNames()[0]));
   ProfileCompilationInfo info;
   profile_file.GetFile()->ResetOffset();
   ASSERT_TRUE(info.Load(GetFd(profile_file)));
@@ -773,7 +766,7 @@
       kUncommonDirtyClass;
   profiles.emplace_back(ScratchFile());
   EXPECT_TRUE(CreateProfile(
-      dex1, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
+      dex1, profiles.back().GetFilename(), core_dex));
 
   // Create a bunch of boot profiles.
   std::string dex2 =
@@ -784,7 +777,7 @@
       kUncommonDirtyClass;
   profiles.emplace_back(ScratchFile());
   EXPECT_TRUE(CreateProfile(
-      dex2, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
+      dex2, profiles.back().GetFilename(), core_dex));
 
   // Create a bunch of boot profiles.
   std::string dex3 =
@@ -794,7 +787,7 @@
       kDirtyClass + "\n";
   profiles.emplace_back(ScratchFile());
   EXPECT_TRUE(CreateProfile(
-      dex3, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
+      dex3, profiles.back().GetFilename(), core_dex));
 
   // Generate the boot profile.
   ScratchFile out_profile;
@@ -807,7 +800,6 @@
   args.push_back("--reference-profile-file=" + out_profile.GetFilename());
   args.push_back("--apk=" + core_dex);
   args.push_back("--dex-location=" + core_dex);
-  args.push_back("--skip-apk-verification");
   for (const ScratchFile& profile : profiles) {
     args.push_back("--profile-file=" + profile.GetFilename());
   }
@@ -903,8 +895,7 @@
   ScratchFile profile_file;
   ASSERT_TRUE(CreateProfile(input_file_contents,
                             profile_file.GetFilename(),
-                            GetTestDexFileName("ProfileTestMultiDex"),
-                            /* skip_verification */ false));
+                            GetTestDexFileName("ProfileTestMultiDex")));
 
   // Load the profile from disk.
   ProfileCompilationInfo info;
@@ -1054,8 +1045,7 @@
   std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
   ASSERT_TRUE(CreateProfile(input_file_contents,
                             profile_file.GetFilename(),
-                            dex_filename,
-                            /* skip_verification */ false));
+                            dex_filename));
 
   // Load the profile from disk.
   ProfileCompilationInfo info;
diff --git a/profman/profman.cc b/profman/profman.cc
index f2cec47..1f77239 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -33,7 +33,7 @@
 
 #include "base/dumpable.h"
 #include "base/logging.h"  // For InitLogging.
-#include "base/mutex.h"
+#include "base/mem_map.h"
 #include "base/scoped_flock.h"
 #include "base/stringpiece.h"
 #include "base/time_utils.h"
@@ -48,9 +48,8 @@
 #include "dex/dex_file_loader.h"
 #include "dex/dex_file_types.h"
 #include "dex/type_reference.h"
-#include "jit/profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 #include "profile_assistant.h"
-#include "runtime.h"
 
 namespace art {
 
@@ -178,6 +177,11 @@
 static constexpr char kMethodFlagStringStartup = 'S';
 static constexpr char kMethodFlagStringPostStartup = 'P';
 
+NO_RETURN static void Abort(const char* msg) {
+  LOG(ERROR) << msg;
+  exit(1);
+}
+
 // TODO(calin): This class has grown too much from its initial design. Split the functionality
 // into smaller, more contained pieces.
 class ProfMan FINAL {
@@ -187,7 +191,6 @@
       dump_only_(false),
       dump_classes_and_methods_(false),
       generate_boot_image_profile_(false),
-      skip_apk_verification_(false),
       dump_output_to_fd_(kInvalidFd),
       test_profile_num_dex_(kDefaultTestProfileNumDex),
       test_profile_method_percerntage_(kDefaultTestProfileMethodPercentage),
@@ -204,8 +207,8 @@
     original_argc = argc;
     original_argv = argv;
 
-    Locks::Init();
-    InitLogging(argv, Runtime::Abort);
+    MemMap::Init();
+    InitLogging(argv, Abort);
 
     // Skip over the command name.
     argv++;
@@ -231,8 +234,6 @@
         ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage);
       } else if (option == "--generate-boot-image-profile") {
         generate_boot_image_profile_ = true;
-      } else if (option == "--skip-apk-verification") {
-        skip_apk_verification_ = true;
       } else if (option.starts_with("--boot-image-class-threshold=")) {
         ParseUintOption(option,
                         "--boot-image-class-threshold",
@@ -369,10 +370,6 @@
     return result;
   }
 
-  bool ShouldSkipApkVerification() const {
-    return skip_apk_verification_;
-  }
-
   bool GetProfileFilterKeyFromApks(std::set<ProfileFilterKey>* profile_filter_keys) {
     auto process_fn = [profile_filter_keys](std::unique_ptr<const DexFile>&& dex_file) {
       // Store the profile key of the location instead of the location itself.
@@ -424,10 +421,11 @@
       std::string error_msg;
       const ArtDexFileLoader dex_file_loader;
       std::vector<std::unique_ptr<const DexFile>> dex_files_for_location;
+      // We do not need to verify the apk for processing profiles.
       if (use_apk_fd_list) {
         if (dex_file_loader.OpenZip(apks_fd_[i],
                                     dex_locations_[i],
-                                    /* verify */ !ShouldSkipApkVerification(),
+                                    /* verify */ false,
                                     kVerifyChecksum,
                                     &error_msg,
                                     &dex_files_for_location)) {
@@ -438,7 +436,7 @@
       } else {
         if (dex_file_loader.Open(apk_files_[i].c_str(),
                                  dex_locations_[i],
-                                 /* verify */ !ShouldSkipApkVerification(),
+                                 /* verify */ false,
                                  kVerifyChecksum,
                                  &error_msg,
                                  &dex_files_for_location)) {
@@ -841,7 +839,8 @@
 
     bool found_invoke = false;
     for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(*dex_file, code_item)) {
-      if (inst->Opcode() == Instruction::INVOKE_VIRTUAL) {
+      if (inst->Opcode() == Instruction::INVOKE_VIRTUAL ||
+          inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) {
         if (found_invoke) {
           LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: "
                      << dex_file->PrettyMethod(method_index);
@@ -1260,7 +1259,6 @@
   bool dump_only_;
   bool dump_classes_and_methods_;
   bool generate_boot_image_profile_;
-  bool skip_apk_verification_;
   int dump_output_to_fd_;
   BootImageOptions boot_image_options_;
   std::string test_profile_;
@@ -1314,4 +1312,3 @@
 int main(int argc, char **argv) {
   return art::profman(argc, argv);
 }
-
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 6b43205..92607f5 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -23,7 +23,7 @@
     "-Wl,--keep-unique,__dex_debug_register_code"
 ]
 
-cc_defaults {
+libart_cc_defaults {
     name: "libart_defaults",
     defaults: ["art_defaults"],
     host_supported: true,
@@ -32,7 +32,6 @@
         "art_field.cc",
         "art_method.cc",
         "barrier.cc",
-        "base/file_utils.cc",
         "base/mem_map_arena_pool.cc",
         "base/mutex.cc",
         "base/quasi_atomic.cc",
@@ -46,7 +45,6 @@
         "compiler_filter.cc",
         "debug_print.cc",
         "debugger.cc",
-        "dex/art_dex_file_loader.cc",
         "dex/dex_file_annotations.cc",
         "dex_to_dex_decompiler.cc",
         "elf_file.cc",
@@ -100,7 +98,6 @@
         "interpreter/shadow_frame.cc",
         "interpreter/unstarted_runtime.cc",
         "java_frame_root_info.cc",
-        "java_vm_ext.cc",
         "jdwp/jdwp_event.cc",
         "jdwp/jdwp_expand_buf.cc",
         "jdwp/jdwp_handler.cc",
@@ -108,15 +105,14 @@
         "jdwp/jdwp_request.cc",
         "jdwp/jdwp_socket.cc",
         "jdwp/object_registry.cc",
-        "jni_env_ext.cc",
         "jit/debugger_interface.cc",
         "jit/jit.cc",
         "jit/jit_code_cache.cc",
-        "jit/profile_compilation_info.cc",
         "jit/profiling_info.cc",
         "jit/profile_saver.cc",
-        "jni_internal.cc",
-        "jobject_comparator.cc",
+        "jni/java_vm_ext.cc",
+        "jni/jni_env_ext.cc",
+        "jni/jni_internal.cc",
         "linear_alloc.cc",
         "managed_stack.cc",
         "method_handles.cc",
@@ -198,6 +194,7 @@
         "ti/agent.cc",
         "trace.cc",
         "transaction.cc",
+        "var_handles.cc",
         "vdex_file.cc",
         "verifier/instruction_flags.cc",
         "verifier/method_verifier.cc",
@@ -209,7 +206,6 @@
         "well_known_classes.cc",
 
         "arch/context.cc",
-        "arch/instruction_set.cc",
         "arch/instruction_set_features.cc",
         "arch/memcmp16.cc",
         "arch/arm/instruction_set_features_arm.cc",
@@ -421,8 +417,8 @@
     cmd: "$(location generate_operator_out) art/runtime $(in) > $(out)",
     tools: ["generate_operator_out"],
     srcs: [
-        "arch/instruction_set.h",
         "base/mutex.h",
+        "class_loader_context.h",
         "class_status.h",
         "debugger.h",
         "gc_root.h",
@@ -467,10 +463,11 @@
         keep_symbols: true,
     },
     whole_static_libs: [
-        "libartbase",
     ],
     shared_libs: [
+        "libartbase",
         "libdexfile",
+        "libprofile",
     ],
     export_shared_lib_headers: [
         "libdexfile",
@@ -491,10 +488,11 @@
         "libart_defaults",
     ],
     whole_static_libs: [
-        "libartbased",
     ],
     shared_libs: [
+        "libartbased",
         "libdexfiled",
+        "libprofiled",
     ],
     export_shared_lib_headers: [
         "libdexfiled",
@@ -510,6 +508,7 @@
     ],
     shared_libs: [
         "libartd",
+	"libartbase-art-gtest",
         "libbase",
         "libbacktrace",
     ],
@@ -528,7 +527,6 @@
     ],
     srcs: [
         "arch/arch_test.cc",
-        "arch/instruction_set_test.cc",
         "arch/instruction_set_features_test.cc",
         "arch/memcmp16_test.cc",
         "arch/stub_test.cc",
@@ -539,7 +537,6 @@
         "arch/x86/instruction_set_features_x86_test.cc",
         "arch/x86_64/instruction_set_features_x86_64_test.cc",
         "barrier_test.cc",
-        "base/file_utils_test.cc",
         "base/mutex_test.cc",
         "base/timing_logger_test.cc",
         "cha_test.cc",
@@ -547,7 +544,6 @@
         "class_loader_context_test.cc",
         "class_table_test.cc",
         "compiler_filter_test.cc",
-        "dex/art_dex_file_loader_test.cc",
         "entrypoints/math_entrypoints_test.cc",
         "entrypoints/quick/quick_trampoline_entrypoints_test.cc",
         "entrypoints_order_test.cc",
@@ -578,8 +574,7 @@
         "interpreter/safe_math_test.cc",
         "interpreter/unstarted_runtime_test.cc",
         "jdwp/jdwp_options_test.cc",
-        "java_vm_ext_test.cc",
-        "jit/profile_compilation_info_test.cc",
+        "jni/java_vm_ext_test.cc",
         "method_handles_test.cc",
         "mirror/dex_cache_test.cc",
         "mirror/method_type_test.cc",
@@ -619,7 +614,7 @@
         "art_gtest_defaults",
     ],
     srcs: [
-        "jni_internal_test.cc",
+        "jni/jni_internal_test.cc",
         "proxy_test.cc",
         "reflection_test.cc",
     ],
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index 1ba4070..d4ceede 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -18,6 +18,7 @@
 
 #include "art_method-inl.h"
 #include "base/callee_save_type.h"
+#include "entrypoints/quick/callee_save_frame.h"
 #include "common_runtime_test.h"
 #include "quick/quick_method_frame_info.h"
 
@@ -57,21 +58,6 @@
   void FinalizeSetup() OVERRIDE {
     ASSERT_EQ(InstructionSet::kX86_64, Runtime::Current()->GetInstructionSet());
   }
-
-  static void CheckFrameSize(InstructionSet isa, CalleeSaveType type, uint32_t save_size)
-      NO_THREAD_SAFETY_ANALYSIS {
-    Runtime* const runtime = Runtime::Current();
-    Thread* const self = Thread::Current();
-    ScopedObjectAccess soa(self);  // So we can create callee-save methods.
-
-    runtime->SetInstructionSet(isa);
-    ArtMethod* save_method = runtime->CreateCalleeSaveMethod();
-    runtime->SetCalleeSaveMethod(save_method, type);
-    QuickMethodFrameInfo frame_info =  runtime->GetRuntimeMethodFrameInfo(save_method);
-    EXPECT_EQ(frame_info.FrameSizeInBytes(), save_size) << "Expected and real size differs for "
-        << type << " core spills=" << std::hex << frame_info.CoreSpillMask() << " fp spills="
-        << frame_info.FpSpillMask() << std::dec;
-  }
 };
 
 TEST_F(ArchTest, CheckCommonOffsetsAndSizes) {
@@ -205,26 +191,20 @@
 }  // namespace x86_64
 
 // Check architecture specific constants are sound.
-#define TEST_ARCH(Arch, arch)                                       \
-  TEST_F(ArchTest, Arch) {                                          \
-    CheckFrameSize(InstructionSet::k##Arch,                         \
-                   CalleeSaveType::kSaveAllCalleeSaves,             \
-                   arch::kFrameSizeSaveAllCalleeSaves);             \
-    CheckFrameSize(InstructionSet::k##Arch,                         \
-                   CalleeSaveType::kSaveRefsOnly,                   \
-                   arch::kFrameSizeSaveRefsOnly);                   \
-    CheckFrameSize(InstructionSet::k##Arch,                         \
-                   CalleeSaveType::kSaveRefsAndArgs,                \
-                   arch::kFrameSizeSaveRefsAndArgs);                \
-    CheckFrameSize(InstructionSet::k##Arch,                         \
-                   CalleeSaveType::kSaveEverything,                 \
-                   arch::kFrameSizeSaveEverything);                 \
-    CheckFrameSize(InstructionSet::k##Arch,                         \
-                   CalleeSaveType::kSaveEverythingForClinit,        \
-                   arch::kFrameSizeSaveEverythingForClinit);        \
-    CheckFrameSize(InstructionSet::k##Arch,                         \
-                   CalleeSaveType::kSaveEverythingForSuspendCheck,  \
-                   arch::kFrameSizeSaveEverythingForSuspendCheck);  \
+// We expect the return PC to be stored at the highest address slot in the frame.
+#define TEST_ARCH_TYPE(Arch, arch, type)                                              \
+  EXPECT_EQ(arch::Arch##CalleeSaveFrame::GetFrameSize(CalleeSaveType::k##type),       \
+            arch::kFrameSize##type);                                                  \
+  EXPECT_EQ(arch::Arch##CalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::k##type),  \
+            arch::kFrameSize##type - static_cast<size_t>(k##Arch##PointerSize))
+#define TEST_ARCH(Arch, arch)                                   \
+  TEST_F(ArchTest, Arch) {                                      \
+    TEST_ARCH_TYPE(Arch, arch, SaveAllCalleeSaves);             \
+    TEST_ARCH_TYPE(Arch, arch, SaveRefsOnly);                   \
+    TEST_ARCH_TYPE(Arch, arch, SaveRefsAndArgs);                \
+    TEST_ARCH_TYPE(Arch, arch, SaveEverything);                 \
+    TEST_ARCH_TYPE(Arch, arch, SaveEverythingForClinit);        \
+    TEST_ARCH_TYPE(Arch, arch, SaveEverythingForSuspendCheck);  \
   }
 TEST_ARCH(Arm, arm)
 TEST_ARCH(Arm64, arm64)
diff --git a/runtime/arch/arm/callee_save_frame_arm.h b/runtime/arch/arm/callee_save_frame_arm.h
new file mode 100644
index 0000000..11eefb9
--- /dev/null
+++ b/runtime/arch/arm/callee_save_frame_arm.h
@@ -0,0 +1,111 @@
+/*
+ * 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_ARCH_ARM_CALLEE_SAVE_FRAME_ARM_H_
+#define ART_RUNTIME_ARCH_ARM_CALLEE_SAVE_FRAME_ARM_H_
+
+#include "arch/instruction_set.h"
+#include "base/bit_utils.h"
+#include "base/callee_save_type.h"
+#include "base/enums.h"
+#include "base/globals.h"
+#include "quick/quick_method_frame_info.h"
+#include "registers_arm.h"
+
+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);
+static constexpr uint32_t kArmCalleeSaveArgSpills =
+    (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 kArmCalleeSaveEverythingSpills =
+    (1 << art::arm::R0) | (1 << art::arm::R1) | (1 << art::arm::R2) | (1 << art::arm::R3) |
+    (1 << art::arm::R4) | (1 << art::arm::R9) | (1 << art::arm::R12);
+
+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);
+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);
+static constexpr uint32_t kArmCalleeSaveFpEverythingSpills =
+    kArmCalleeSaveFpArgSpills | kArmCalleeSaveFpAllSpills;
+
+class ArmCalleeSaveFrame {
+ public:
+  static constexpr uint32_t GetCoreSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kArmCalleeSaveAlwaysSpills | kArmCalleeSaveRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveArgSpills : 0) |
+        (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveAllSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveEverythingSpills : 0);
+  }
+
+  static constexpr uint32_t GetFpSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) |
+        (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveFpEverythingSpills : 0);
+  }
+
+  static constexpr uint32_t GetFrameSize(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ +
+                    POPCOUNT(GetFpSpills(type)) /* fprs */ +
+                    1 /* Method* */) * static_cast<size_t>(kArmPointerSize), kStackAlignment);
+  }
+
+  static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type));
+  }
+
+  static constexpr size_t GetFpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           (POPCOUNT(GetCoreSpills(type)) +
+            POPCOUNT(GetFpSpills(type))) * static_cast<size_t>(kArmPointerSize);
+  }
+
+  static constexpr size_t GetGpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           POPCOUNT(GetCoreSpills(type)) * static_cast<size_t>(kArmPointerSize);
+  }
+
+  static constexpr size_t GetReturnPcOffset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) - static_cast<size_t>(kArmPointerSize);
+  }
+};
+
+}  // namespace arm
+}  // namespace art
+
+#endif  // ART_RUNTIME_ARCH_ARM_CALLEE_SAVE_FRAME_ARM_H_
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 80080e9..b4e9036 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -90,30 +90,34 @@
   qpoints->pReadBarrierMarkReg10 = is_active ? art_quick_read_barrier_mark_reg10 : nullptr;
   qpoints->pReadBarrierMarkReg11 = is_active ? art_quick_read_barrier_mark_reg11 : nullptr;
 
-  // For the alignment check, strip the Thumb mode bit.
-  DCHECK_ALIGNED(reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection) - 1u, 256u);
-  // Check the field narrow entrypoint offset from the introspection entrypoint.
-  intptr_t narrow_diff =
-      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_narrow) -
-      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
-  DCHECK_EQ(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET, narrow_diff);
-  // Check array switch cases offsets from the introspection entrypoint.
-  intptr_t array_diff =
-      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_arrays) -
-      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
-  DCHECK_EQ(BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET, array_diff);
-  // Check the GC root entrypoint offsets from the introspection entrypoint.
-  intptr_t gc_roots_wide_diff =
-      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_gc_roots_wide) -
-      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
-  DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET, gc_roots_wide_diff);
-  intptr_t gc_roots_narrow_diff =
-      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_gc_roots_narrow) -
-      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
-  DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET, gc_roots_narrow_diff);
-  // The register 12, i.e. IP, is reserved, so there is no art_quick_read_barrier_mark_reg12.
-  // We're using the entry to hold a pointer to the introspection entrypoint instead.
-  qpoints->pReadBarrierMarkReg12 = is_active ? art_quick_read_barrier_mark_introspection : nullptr;
+  if (kUseReadBarrier && kUseBakerReadBarrier) {
+    // For the alignment check, strip the Thumb mode bit.
+    DCHECK_ALIGNED(reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection) - 1u,
+                   256u);
+    // Check the field narrow entrypoint offset from the introspection entrypoint.
+    intptr_t narrow_diff =
+        reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_narrow) -
+        reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
+    DCHECK_EQ(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET, narrow_diff);
+    // Check array switch cases offsets from the introspection entrypoint.
+    intptr_t array_diff =
+        reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_arrays) -
+        reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
+    DCHECK_EQ(BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET, array_diff);
+    // Check the GC root entrypoint offsets from the introspection entrypoint.
+    intptr_t gc_roots_wide_diff =
+        reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_gc_roots_wide) -
+        reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
+    DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET, gc_roots_wide_diff);
+    intptr_t gc_roots_narrow_diff =
+        reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_gc_roots_narrow) -
+        reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
+    DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET, gc_roots_narrow_diff);
+    // The register 12, i.e. IP, is reserved, so there is no art_quick_read_barrier_mark_reg12.
+    // We're using the entry to hold a pointer to the introspection entrypoint instead.
+    qpoints->pReadBarrierMarkReg12 =
+        is_active ? art_quick_read_barrier_mark_introspection : nullptr;
+  }
 }
 
 void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index 315bf95..bb33a27 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -20,10 +20,10 @@
 
 #include "art_method.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "base/hex_dump.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
-#include "globals.h"
 #include "thread-current-inl.h"
 
 //
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index 801254f..608999b 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -46,9 +46,11 @@
       "cortex-a53",
       "cortex-a53.a57",
       "cortex-a53.a72",
+      "cortex-a55",
       "cortex-a57",
       "cortex-a72",
       "cortex-a73",
+      "cortex-a75",
       "exynos-m1",
       "denver",
       "kryo"
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 526960b..311e838 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -55,7 +55,7 @@
     @ Load kSaveAllCalleeSaves Method* into rTemp.
     ldr \rTemp, [\rTemp, #RUNTIME_SAVE_ALL_CALLEE_SAVES_METHOD_OFFSET]
     str \rTemp, [sp, #0]                          @ Place Method* at bottom of stack.
-    str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
+    str sp, [rSELF, #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_SAVES != 36 + 64 + 12)
@@ -86,7 +86,7 @@
     @ Load kSaveRefsOnly Method* into rTemp.
     ldr \rTemp, [\rTemp, #RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET]
     str \rTemp, [sp, #0]                          @ Place Method* at bottom of stack.
-    str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
+    str sp, [rSELF, #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_REFS_ONLY != 28 + 4)
@@ -147,13 +147,13 @@
     @ Load kSaveRefsAndArgs Method* into rTemp.
     ldr \rTemp, [\rTemp, #RUNTIME_SAVE_REFS_AND_ARGS_METHOD_OFFSET]
     str \rTemp, [sp, #0]                          @ Place Method* at bottom of stack.
-    str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
+    str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
 .endm
 
 .macro SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_R0
     SETUP_SAVE_REFS_AND_ARGS_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.
+    str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
 .endm
 
 .macro RESTORE_SAVE_REFS_AND_ARGS_FRAME
@@ -193,7 +193,7 @@
     @ Load kSaveEverything Method* into rTemp.
     ldr \rTemp, [\rTemp, #\runtime_method_offset]
     str \rTemp, [sp, #0]                @ Place Method* at bottom of stack.
-    str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
+    str sp, [rSELF, #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_EVERYTHING != 56 + 128 + 8)
@@ -301,7 +301,7 @@
      * exception is Thread::Current()->exception_ when the runtime method frame is ready.
      */
 .macro DELIVER_PENDING_EXCEPTION_FRAME_READY
-    mov    r0, r9                              @ pass Thread::Current
+    mov    r0, rSELF                           @ pass Thread::Current
     bl     artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*)
 .endm
 
@@ -318,7 +318,7 @@
     .extern \cxx_name
 ENTRY \c_name
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r0       @ save all registers as basis for long jump context
-    mov r0, r9                      @ pass Thread::Current
+    mov r0, rSELF                   @ pass Thread::Current
     bl  \cxx_name                   @ \cxx_name(Thread*)
 END \c_name
 .endm
@@ -327,7 +327,7 @@
     .extern \cxx_name
 ENTRY \c_name
     SETUP_SAVE_EVERYTHING_FRAME r0  @ save all registers as basis for long jump context
-    mov r0, r9                      @ pass Thread::Current
+    mov r0, rSELF                   @ pass Thread::Current
     bl  \cxx_name                   @ \cxx_name(Thread*)
 END \c_name
 .endm
@@ -336,7 +336,7 @@
     .extern \cxx_name
 ENTRY \c_name
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r1       @ save all registers as basis for long jump context
-    mov r1, r9                      @ pass Thread::Current
+    mov r1, rSELF                   @ pass Thread::Current
     bl  \cxx_name                   @ \cxx_name(Thread*)
 END \c_name
 .endm
@@ -345,13 +345,13 @@
     .extern \cxx_name
 ENTRY \c_name
     SETUP_SAVE_EVERYTHING_FRAME r2  @ save all registers as basis for long jump context
-    mov r2, r9                      @ pass Thread::Current
+    mov r2, rSELF                   @ pass Thread::Current
     bl  \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.
+    ldr \reg, [rSELF, #THREAD_EXCEPTION_OFFSET]  @ Get exception field.
     cbnz \reg, 1f
     bx lr
 1:
@@ -377,7 +377,7 @@
     .extern \entrypoint
 ENTRY \name
     SETUP_SAVE_REFS_ONLY_FRAME r1        @ save callee saves in case of GC
-    mov    r1, r9                        @ pass Thread::Current
+    mov    r1, rSELF                     @ pass Thread::Current
     bl     \entrypoint                   @ (uint32_t field_idx, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
@@ -389,7 +389,7 @@
     .extern \entrypoint
 ENTRY \name
     SETUP_SAVE_REFS_ONLY_FRAME r2        @ save callee saves in case of GC
-    mov    r2, r9                        @ pass Thread::Current
+    mov    r2, rSELF                     @ pass Thread::Current
     bl     \entrypoint                   @ (field_idx, Object*, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
@@ -401,7 +401,7 @@
     .extern \entrypoint
 ENTRY \name
     SETUP_SAVE_REFS_ONLY_FRAME r3        @ save callee saves in case of GC
-    mov    r3, r9                        @ pass Thread::Current
+    mov    r3, rSELF                     @ pass Thread::Current
     bl     \entrypoint                   @ (field_idx, Object*, new_val, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME         @ TODO: we can clearly save an add here
     REFRESH_MARKING_REGISTER
@@ -448,7 +448,7 @@
     @ save all registers as basis for long jump context
     SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED r1
     mov r0, lr                      @ pass the fault address stored in LR by the fault handler.
-    mov r1, r9                      @ pass Thread::Current
+    mov r1, rSELF                   @ pass Thread::Current
     bl  artThrowNullPointerExceptionFromSignal  @ (Thread*)
 END art_quick_throw_null_pointer_exception_from_signal
 
@@ -494,7 +494,7 @@
 .macro INVOKE_TRAMPOLINE_BODY cxx_name
     .extern \cxx_name
     SETUP_SAVE_REFS_AND_ARGS_FRAME r2     @ save callee saves in case allocation triggers GC
-    mov    r2, r9                         @ pass Thread::Current
+    mov    r2, rSELF                      @ pass Thread::Current
     mov    r3, sp
     bl     \cxx_name                      @ (method_idx, this, Thread*, SP)
     mov    r12, r1                        @ save Method*->code_
@@ -682,50 +682,48 @@
      */
     .extern artLockObjectFromCode
 ENTRY art_quick_lock_object
+    ldr    r1, [rSELF, #THREAD_ID_OFFSET]
     cbz    r0, .Lslow_lock
 .Lretry_lock:
-    ldr    r2, [r9, #THREAD_ID_OFFSET]
-    ldrex  r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
-    mov    r3, r1
-    and    r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  @ zero the gc bits
-    cbnz   r3, .Lnot_unlocked         @ already thin locked
-    @ unlocked case - r1: original lock word that's zero except for the read barrier bits.
-    orr    r2, r1, r2                 @ r2 holds thread id with count of 0 with preserved read barrier bits
-    strex  r3, r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
-    cbnz   r3, .Llock_strex_fail      @ store failed, retry
-    dmb    ish                        @ full (LoadLoad|LoadStore) memory barrier
+    ldrex  r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
+    eor    r3, r2, r1                 @ Prepare the value to store if unlocked
+                                      @   (thread id, count of 0 and preserved read barrier bits),
+                                      @ or prepare to compare thread id for recursive lock check
+                                      @   (lock_word.ThreadId() ^ self->ThreadId()).
+    ands   ip, r2, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  @ Test the non-gc bits.
+    bne    .Lnot_unlocked             @ Check if unlocked.
+    @ unlocked case - store r3: original lock word plus thread id, preserved read barrier bits.
+    strex  r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
+    cbnz   r2, .Llock_strex_fail      @ If store failed, retry.
+    dmb    ish                        @ Full (LoadLoad|LoadStore) memory barrier.
     bx lr
-.Lnot_unlocked:  @ r1: original lock word, r2: thread_id with count of 0 and zero read barrier bits
-    lsr    r3, r1, LOCK_WORD_STATE_SHIFT
-    cbnz   r3, .Lslow_lock            @ if either of the top two bits are set, go slow path
-    eor    r2, r1, r2                 @ lock_word.ThreadId() ^ self->ThreadId()
-    uxth   r2, r2                     @ zero top 16 bits
-    cbnz   r2, .Lslow_lock            @ lock word and self thread id's match -> recursive lock
-                                      @ else contention, go to slow path
-    mov    r3, r1                     @ copy the lock word to check count overflow.
-    and    r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  @ zero the gc bits.
-    add    r2, r3, #LOCK_WORD_THIN_LOCK_COUNT_ONE  @ increment count in lock word placing in r2 to check overflow
-    lsr    r3, r2, #LOCK_WORD_GC_STATE_SHIFT    @ if the first gc state bit is set, we overflowed.
-    cbnz   r3, .Lslow_lock            @ if we overflow the count go slow path
-    add    r2, r1, #LOCK_WORD_THIN_LOCK_COUNT_ONE  @ increment count for real
-    strex  r3, r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits
-    cbnz   r3, .Llock_strex_fail      @ strex failed, retry
+.Lnot_unlocked:  @ r2: original lock word, r1: thread_id, r3: r2 ^ r1
+#if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT
+#error "Expecting thin lock count and gc state in consecutive bits."
+#endif
+                                      @ Check lock word state and thread id together,
+    bfc    r3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE)
+    cbnz   r3, .Lslow_lock            @ if either of the top two bits are set, or the lock word's
+                                      @ thread id did not match, go slow path.
+    add    r3, r2, #LOCK_WORD_THIN_LOCK_COUNT_ONE  @ Increment the recursive lock count.
+                                      @ Extract the new thin lock count for overflow check.
+    ubfx   r2, r3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #LOCK_WORD_THIN_LOCK_COUNT_SIZE
+    cbz    r2, .Lslow_lock            @ Zero as the new count indicates overflow, go slow path.
+    strex  r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]  @ strex necessary for read barrier bits.
+    cbnz   r2, .Llock_strex_fail      @ If strex failed, retry.
     bx lr
 .Llock_strex_fail:
     b      .Lretry_lock               @ retry
-.Lslow_lock:
-    SETUP_SAVE_REFS_ONLY_FRAME r1     @ save callee saves in case we block
-    mov    r1, r9                     @ pass Thread::Current
-    bl     artLockObjectFromCode      @ (Object* obj, Thread*)
-    RESTORE_SAVE_REFS_ONLY_FRAME
-    REFRESH_MARKING_REGISTER
-    RETURN_IF_RESULT_IS_ZERO
-    DELIVER_PENDING_EXCEPTION
+// Note: the slow path is actually the art_quick_lock_object_no_inline (tail call).
 END art_quick_lock_object
 
 ENTRY art_quick_lock_object_no_inline
+    // This is also the slow path for art_quick_lock_object. Note that we
+    // need a local label, the assembler complains about target being out of
+    // range if we try to jump to `art_quick_lock_object_no_inline`.
+.Lslow_lock:
     SETUP_SAVE_REFS_ONLY_FRAME r1     @ save callee saves in case we block
-    mov    r1, r9                     @ pass Thread::Current
+    mov    r1, rSELF                  @ pass Thread::Current
     bl     artLockObjectFromCode      @ (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
@@ -739,62 +737,59 @@
      */
     .extern artUnlockObjectFromCode
 ENTRY art_quick_unlock_object
+    ldr    r1, [rSELF, #THREAD_ID_OFFSET]
     cbz    r0, .Lslow_unlock
 .Lretry_unlock:
 #ifndef USE_READ_BARRIER
-    ldr    r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
+    ldr    r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
 #else
-    ldrex  r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]  @ Need to use atomic instructions for read barrier
+                                      @ Need to use atomic instructions for read barrier.
+    ldrex  r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
 #endif
-    lsr    r2, r1, #LOCK_WORD_STATE_SHIFT
-    cbnz   r2, .Lslow_unlock          @ if either of the top two bits are set, go slow path
-    ldr    r2, [r9, #THREAD_ID_OFFSET]
-    mov    r3, r1                     @ copy lock word to check thread id equality
-    and    r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  @ zero the gc bits
-    eor    r3, r3, r2                 @ lock_word.ThreadId() ^ self->ThreadId()
-    uxth   r3, r3                     @ zero top 16 bits
-    cbnz   r3, .Lslow_unlock          @ do lock word and self thread id's match?
-    mov    r3, r1                     @ copy lock word to detect transition to unlocked
-    and    r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  @ zero the gc bits
-    cmp    r3, #LOCK_WORD_THIN_LOCK_COUNT_ONE
-    bpl    .Lrecursive_thin_unlock
-    @ transition to unlocked
-    mov    r3, r1
-    and    r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED  @ r3: zero except for the preserved gc bits
-    dmb    ish                        @ full (LoadStore|StoreStore) memory barrier
+    eor    r3, r2, r1                 @ Prepare the value to store if simply locked
+                                      @   (mostly 0s, and preserved read barrier bits),
+                                      @ or prepare to compare thread id for recursive lock check
+                                      @   (lock_word.ThreadId() ^ self->ThreadId()).
+    ands   ip, r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  @ Test the non-gc bits.
+    bne    .Lnot_simply_locked        @ Locked recursively or by other thread?
+    @ Transition to unlocked.
+    dmb    ish                        @ Full (LoadStore|StoreStore) memory barrier.
 #ifndef USE_READ_BARRIER
     str    r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
 #else
     strex  r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]  @ strex necessary for read barrier bits
-    cbnz   r2, .Lunlock_strex_fail    @ store failed, retry
+    cbnz   r2, .Lunlock_strex_fail    @ If the store failed, retry.
 #endif
     bx     lr
-.Lrecursive_thin_unlock:  @ r1: original lock word
-    sub    r1, r1, #LOCK_WORD_THIN_LOCK_COUNT_ONE  @ decrement count
+.Lnot_simply_locked:  @ r2: original lock word, r1: thread_id, r3: r2 ^ r1
+#if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT
+#error "Expecting thin lock count and gc state in consecutive bits."
+#endif
+                                      @ Check lock word state and thread id together,
+    bfc    r3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE)
+    cbnz   r3, .Lslow_unlock          @ if either of the top two bits are set, or the lock word's
+                                      @ thread id did not match, go slow path.
+    sub    r3, r2, #LOCK_WORD_THIN_LOCK_COUNT_ONE  @ Decrement recursive lock count.
 #ifndef USE_READ_BARRIER
-    str    r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
+    str    r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
 #else
-    strex  r2, r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]  @ strex necessary for read barrier bits
-    cbnz   r2, .Lunlock_strex_fail    @ store failed, retry
+    strex  r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]  @ strex necessary for read barrier bits.
+    cbnz   r2, .Lunlock_strex_fail    @ If the store failed, retry.
 #endif
     bx     lr
 .Lunlock_strex_fail:
     b      .Lretry_unlock             @ retry
-.Lslow_unlock:
-    @ save callee saves in case exception allocation triggers GC
-    SETUP_SAVE_REFS_ONLY_FRAME r1
-    mov    r1, r9                     @ pass Thread::Current
-    bl     artUnlockObjectFromCode    @ (Object* obj, Thread*)
-    RESTORE_SAVE_REFS_ONLY_FRAME
-    REFRESH_MARKING_REGISTER
-    RETURN_IF_RESULT_IS_ZERO
-    DELIVER_PENDING_EXCEPTION
+// Note: the slow path is actually the art_quick_unlock_object_no_inline (tail call).
 END art_quick_unlock_object
 
 ENTRY art_quick_unlock_object_no_inline
+    // This is also the slow path for art_quick_unlock_object. Note that we
+    // need a local label, the assembler complains about target being out of
+    // range if we try to jump to `art_quick_unlock_object_no_inline`.
+.Lslow_unlock:
     @ save callee saves in case exception allocation triggers GC
     SETUP_SAVE_REFS_ONLY_FRAME r1
-    mov    r1, r9                     @ pass Thread::Current
+    mov    r1, rSELF                  @ pass Thread::Current
     bl     artUnlockObjectFromCode    @ (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
@@ -832,7 +827,7 @@
 
 .Lthrow_class_cast_exception_for_bitstring_check:
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2       @ save all registers as basis for long jump context
-    mov r2, r9                      @ pass Thread::Current
+    mov r2, rSELF                   @ pass Thread::Current
     bl  artThrowClassCastExceptionForObject  @ (Object*, Class*, Thread*)
     bkpt
 END art_quick_check_instance_of
@@ -917,7 +912,7 @@
     add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
     POISON_HEAP_REF r2
     str r2, [r3, r1, lsl #2]
-    ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
+    ldr r3, [rSELF, #THREAD_CARD_TABLE_OFFSET]
     lsr r0, r0, #CARD_TABLE_CARD_SHIFT
     strb r3, [r3, r0]
     blx lr
@@ -945,7 +940,7 @@
     add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
     POISON_HEAP_REF r2
     str r2, [r3, r1, lsl #2]
-    ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
+    ldr r3, [rSELF, #THREAD_CARD_TABLE_OFFSET]
     lsr r0, r0, #CARD_TABLE_CARD_SHIFT
     strb r3, [r3, r0]
     blx lr
@@ -954,7 +949,7 @@
     /* No need to repeat restore cfi directives, the ones above apply here. */
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r3
     mov r1, r2
-    mov r2, r9                     @ pass Thread::Current
+    mov r2, rSELF                  @ pass Thread::Current
     bl artThrowArrayStoreException @ (Class*, Class*, Thread*)
     bkpt                           @ unreached
 END art_quick_aput_obj
@@ -964,7 +959,7 @@
     .extern \entrypoint
 ENTRY \name
     SETUP_SAVE_REFS_ONLY_FRAME r1     @ save callee saves in case of GC
-    mov    r1, r9                     @ pass Thread::Current
+    mov    r1, rSELF                  @ pass Thread::Current
     bl     \entrypoint     @ (uint32_t type_idx, Method* method, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
@@ -977,7 +972,7 @@
     .extern \entrypoint
 ENTRY \name
     SETUP_SAVE_REFS_ONLY_FRAME r2     @ save callee saves in case of GC
-    mov    r2, r9                     @ pass Thread::Current
+    mov    r2, rSELF                  @ pass Thread::Current
     bl     \entrypoint     @ (uint32_t type_idx, Method* method, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
@@ -990,7 +985,7 @@
     .extern \entrypoint
 ENTRY \name
     SETUP_SAVE_REFS_ONLY_FRAME r3     @ save callee saves in case of GC
-    mov    r3, r9                     @ pass Thread::Current
+    mov    r3, rSELF                  @ pass Thread::Current
     @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*)
     bl     \entrypoint
     RESTORE_SAVE_REFS_ONLY_FRAME
@@ -1004,7 +999,7 @@
     .extern \entrypoint
 ENTRY \name
     SETUP_SAVE_REFS_ONLY_FRAME r12    @ save callee saves in case of GC
-    str    r9, [sp, #-16]!            @ expand the frame and pass Thread::Current
+    str    rSELF, [sp, #-16]!         @ expand the frame and pass Thread::Current
     .cfi_adjust_cfa_offset 16
     bl     \entrypoint
     add    sp, #16                    @ strip the extra frame
@@ -1015,12 +1010,15 @@
 END \name
 .endm
 
-// Macro for string and type resolution and initialization.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings.
+     */
 .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
     .extern \entrypoint
 ENTRY \name
     SETUP_SAVE_EVERYTHING_FRAME r1, \runtime_method_offset    @ save everything in case of GC
-    mov    r1, r9                     @ pass Thread::Current
+    mov    r1, rSELF                  @ pass Thread::Current
     bl     \entrypoint                @ (uint32_t index, Thread*)
     cbz    r0, 1f                     @ If result is null, deliver the OOME.
     .cfi_remember_state
@@ -1040,6 +1038,8 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
 // Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are
@@ -1060,9 +1060,9 @@
     .extern artGet64StaticFromCompiledCode
 ENTRY art_quick_get64_static
     SETUP_SAVE_REFS_ONLY_FRAME r2        @ save callee saves in case of GC
-    mov    r1, r9                        @ pass Thread::Current
-    bl     artGet64StaticFromCompiledCode        @ (uint32_t field_idx, Thread*)
-    ldr    r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    mov    r1, rSELF                     @ pass Thread::Current
+    bl     artGet64StaticFromCompiledCode  @ (uint32_t field_idx, Thread*)
+    ldr    r2, [rSELF, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
     cbnz   r2, 1f                        @ success if no exception pending
@@ -1086,9 +1086,9 @@
     .extern artGet64InstanceFromCompiledCode
 ENTRY art_quick_get64_instance
     SETUP_SAVE_REFS_ONLY_FRAME r2        @ save callee saves in case of GC
-    mov    r2, r9                        @ pass Thread::Current
-    bl     artGet64InstanceFromCompiledCode      @ (field_idx, Object*, Thread*)
-    ldr    r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    mov    r2, rSELF                     @ pass Thread::Current
+    bl     artGet64InstanceFromCompiledCode  @ (field_idx, Object*, Thread*)
+    ldr    r2, [rSELF, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
     cbnz   r2, 1f                        @ success if no exception pending
@@ -1120,7 +1120,7 @@
 ENTRY art_quick_set64_instance
     SETUP_SAVE_REFS_ONLY_FRAME r12       @ save callee saves in case of GC
                                          @ r2:r3 contain the wide argument
-    str    r9, [sp, #-16]!               @ expand the frame and pass Thread::Current
+    str    rSELF, [sp, #-16]!            @ expand the frame and pass Thread::Current
     .cfi_adjust_cfa_offset 16
     bl     artSet64InstanceFromCompiledCode      @ (field_idx, Object*, new_val, Thread*)
     add    sp, #16                       @ release out args
@@ -1135,7 +1135,7 @@
 ENTRY art_quick_set64_static
     SETUP_SAVE_REFS_ONLY_FRAME r12        @ save callee saves in case of GC
                                           @ r2:r3 contain the wide argument
-    str    r9, [sp, #-16]!                @ expand the frame and pass Thread::Current
+    str    rSELF, [sp, #-16]!             @ expand the frame and pass Thread::Current
     .cfi_adjust_cfa_offset 16
     bl     artSet64StaticFromCompiledCode @ (field_idx, new_val, Thread*)
     add    sp, #16                        @ release out args
@@ -1180,12 +1180,12 @@
 .macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name, isInitialized
 ENTRY \c_name
     // Fast path rosalloc allocation.
-    // r0: type/return value, r9: Thread::Current
+    // r0: type/return value, rSELF (r9): Thread::Current
     // r1, r2, r3, r12: free.
-    ldr    r3, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]     // Check if the thread local
+    ldr    r3, [rSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]  // Check if the thread local
                                                               // allocation stack has room.
                                                               // TODO: consider using ldrd.
-    ldr    r12, [r9, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET]
+    ldr    r12, [rSELF, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET]
     cmp    r3, r12
     bhs    .Lslow_path\c_name
 
@@ -1203,7 +1203,7 @@
                                                               // from the size. Since the size is
                                                               // already aligned we can combine the
                                                               // two shifts together.
-    add    r12, r9, r3, lsr #(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT)
+    add    r12, rSELF, r3, lsr #(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT)
                                                               // Subtract pointer size since ther
                                                               // are no runs for 0 byte allocations
                                                               // and the size is already aligned.
@@ -1231,9 +1231,9 @@
                                                               // local allocation stack and
                                                               // increment the thread local
                                                               // allocation stack top.
-    ldr    r1, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
+    ldr    r1, [rSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
     str    r3, [r1], #COMPRESSED_REFERENCE_SIZE               // (Increment r1 as a side effect.)
-    str    r1, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
+    str    r1, [rSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
                                                               // Decrement the size of the free list
 
     // After this "STR" the object is published to the thread local allocation stack,
@@ -1282,7 +1282,7 @@
 
 .Lslow_path\c_name:
     SETUP_SAVE_REFS_ONLY_FRAME r2     @ save callee saves in case of GC
-    mov    r1, r9                     @ pass Thread::Current
+    mov    r1, rSELF                  @ pass Thread::Current
     bl     \cxx_name                  @ (mirror::Class* cls, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
@@ -1296,7 +1296,7 @@
 // The common fast path code for art_quick_alloc_object_resolved/initialized_tlab
 // and art_quick_alloc_object_resolved/initialized_region_tlab.
 //
-// r0: type r9: Thread::Current, r1, r2, r3, r12: free.
+// r0: type, rSELF (r9): Thread::Current, r1, r2, r3, r12: free.
 // Need to preserve r0 to the slow path.
 //
 // If isInitialized=1 then the compiler assumes the object's class has already been initialized.
@@ -1308,7 +1308,7 @@
 #if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0))
 #error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance"
 #endif
-    ldrd   r12, r3, [r9, #THREAD_LOCAL_POS_OFFSET]
+    ldrd   r12, r3, [rSELF, #THREAD_LOCAL_POS_OFFSET]
     sub    r12, r3, r12                                       // Compute the remaining buf size.
     ldr    r3, [r0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET]  // Load the object size (r3).
     cmp    r3, r12                                            // Check if it fits.
@@ -1321,9 +1321,9 @@
     // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1.
                                                               // Reload old thread_local_pos (r0)
                                                               // for the return value.
-    ldr    r2, [r9, #THREAD_LOCAL_POS_OFFSET]
+    ldr    r2, [rSELF, #THREAD_LOCAL_POS_OFFSET]
     add    r1, r2, r3
-    str    r1, [r9, #THREAD_LOCAL_POS_OFFSET]                 // Store new thread_local_pos.
+    str    r1, [rSELF, #THREAD_LOCAL_POS_OFFSET]              // Store new thread_local_pos.
     // After this "STR" the object is published to the thread local allocation stack,
     // and it will be observable from a runtime internal (eg. Heap::VisitObjects) point of view.
     // It is not yet visible to the running (user) compiled code until after the return.
@@ -1341,9 +1341,9 @@
     //
     // (Note: The actual check is done by checking that the object's class pointer is non-null.
     // Also, unlike rosalloc, the object can never be observed as null).
-    ldr    r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET]             // Increment thread_local_objects.
+    ldr    r1, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET]          // Increment thread_local_objects.
     add    r1, r1, #1
-    str    r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET]
+    str    r1, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET]
     POISON_HEAP_REF r0
     str    r0, [r2, #MIRROR_OBJECT_CLASS_OFFSET]              // Store the class pointer.
                                                               // Fence. This is "ish" not "ishst" so
@@ -1370,12 +1370,12 @@
 .macro GENERATE_ALLOC_OBJECT_RESOLVED_TLAB name, entrypoint, isInitialized
 ENTRY \name
     // Fast path tlab allocation.
-    // r0: type, r9: Thread::Current
+    // r0: type, rSELF (r9): Thread::Current
     // r1, r2, r3, r12: free.
     ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lslow_path\name, \isInitialized
 .Lslow_path\name:
     SETUP_SAVE_REFS_ONLY_FRAME r2                             // Save callee saves in case of GC.
-    mov    r1, r9                                             // Pass Thread::Current.
+    mov    r1, rSELF                                          // Pass Thread::Current.
     bl     \entrypoint                                        // (mirror::Class* klass, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
@@ -1392,7 +1392,7 @@
 // The common fast path code for art_quick_alloc_array_resolved/initialized_tlab
 // and art_quick_alloc_array_resolved/initialized_region_tlab.
 //
-// r0: type r1: component_count r2: total_size r9: Thread::Current, r3, r12: free.
+// r0: type, r1: component_count, r2: total_size, rSELF (r9): Thread::Current, r3, r12: free.
 // Need to preserve r0 and r1 to the slow path.
 .macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE slowPathLabel
     and    r2, r2, #OBJECT_ALIGNMENT_MASK_TOGGLED             // Apply alignment mask
@@ -1404,7 +1404,7 @@
 #if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0))
 #error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance"
 #endif
-    ldrd   r3, r12, [r9, #THREAD_LOCAL_POS_OFFSET]
+    ldrd   r3, r12, [rSELF, #THREAD_LOCAL_POS_OFFSET]
     sub    r12, r12, r3                                       // Compute the remaining buf size.
     cmp    r2, r12                                            // Check if the total_size fits.
     // The array class is always initialized here. Unlike new-instance,
@@ -1412,10 +1412,10 @@
     bhi    \slowPathLabel
     // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1.
     add    r2, r2, r3
-    str    r2, [r9, #THREAD_LOCAL_POS_OFFSET]                 // Store new thread_local_pos.
-    ldr    r2, [r9, #THREAD_LOCAL_OBJECTS_OFFSET]             // Increment thread_local_objects.
+    str    r2, [rSELF, #THREAD_LOCAL_POS_OFFSET]              // Store new thread_local_pos.
+    ldr    r2, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET]          // Increment thread_local_objects.
     add    r2, r2, #1
-    str    r2, [r9, #THREAD_LOCAL_OBJECTS_OFFSET]
+    str    r2, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET]
     POISON_HEAP_REF r0
     str    r0, [r3, #MIRROR_OBJECT_CLASS_OFFSET]              // Store the class pointer.
     str    r1, [r3, #MIRROR_ARRAY_LENGTH_OFFSET]              // Store the array length.
@@ -1438,7 +1438,7 @@
     // Fast path array allocation for region tlab allocation.
     // r0: mirror::Class* type
     // r1: int32_t component_count
-    // r9: thread
+    // rSELF (r9): thread
     // r2, r3, r12: free.
     \size_setup .Lslow_path\name
     ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE .Lslow_path\name
@@ -1447,7 +1447,7 @@
     // r1: int32_t component_count
     // r2: Thread* self
     SETUP_SAVE_REFS_ONLY_FRAME r2  // save callee saves in case of GC
-    mov    r2, r9                  // pass Thread::Current
+    mov    r2, rSELF               // pass Thread::Current
     bl     \entrypoint
     RESTORE_SAVE_REFS_ONLY_FRAME
     REFRESH_MARKING_REGISTER
@@ -1570,10 +1570,10 @@
      .extern artQuickProxyInvokeHandler
 ENTRY art_quick_proxy_invoke_handler
     SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_R0
-    mov     r2, r9                 @ pass Thread::Current
+    mov     r2, rSELF              @ 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_
+    ldr     r2, [rSELF, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     // Tear down the callee-save frame. Skip arg registers.
     add     sp, #(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY)
     .cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY)
@@ -1701,7 +1701,7 @@
     .extern artQuickResolutionTrampoline
 ENTRY art_quick_resolution_trampoline
     SETUP_SAVE_REFS_AND_ARGS_FRAME r2
-    mov     r2, r9                 @ pass Thread::Current
+    mov     r2, rSELF              @ pass Thread::Current
     mov     r3, sp                 @ pass SP
     blx     artQuickResolutionTrampoline  @ (Method* called, receiver, Thread*, SP)
     cbz     r0, 1f                 @ is code pointer null? goto exception
@@ -1775,10 +1775,10 @@
     blx artQuickGenericJniEndTrampoline
 
     // Restore self pointer.
-    mov r9, r11
+    mov rSELF, r11
 
     // Pending exceptions possible.
-    ldr r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     cbnz r2, .Lexception_in_native
 
     // Tear down the alloca.
@@ -1799,7 +1799,7 @@
     .cfi_adjust_cfa_offset FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY
 
 .Lexception_in_native:
-    ldr ip, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]
+    ldr ip, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET]
     add ip, ip, #-1  // Remove the GenericJNI tag. ADD/SUB writing directly to SP is UNPREDICTABLE.
     mov sp, ip
     .cfi_def_cfa_register sp
@@ -1810,10 +1810,10 @@
     .extern artQuickToInterpreterBridge
 ENTRY art_quick_to_interpreter_bridge
     SETUP_SAVE_REFS_AND_ARGS_FRAME r1
-    mov     r1, r9                 @ pass Thread::Current
+    mov     r1, rSELF              @ pass Thread::Current
     mov     r2, sp                 @ pass SP
     blx     artQuickToInterpreterBridge    @ (Method* method, Thread*, SP)
-    ldr     r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    ldr     r2, [rSELF, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     // Tear down the callee-save frame. Skip arg registers.
     add     sp, #(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY)
     .cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY)
@@ -1841,7 +1841,7 @@
     SETUP_SAVE_REFS_AND_ARGS_FRAME r2
     @ preserve r0 (not normally an arg) knowing there is a spare slot in kSaveRefsAndArgs.
     str   r0, [sp, #4]
-    mov   r2, r9         @ pass Thread::Current
+    mov   r2, rSELF      @ pass Thread::Current
     mov   r3, sp         @ pass SP
     blx   artInstrumentationMethodEntryFromCode  @ (Method*, Object*, Thread*, SP)
     cbz   r0, .Ldeliver_instrumentation_entry_exception
@@ -1867,7 +1867,7 @@
     add   r3, sp, #8     @ store fpr_res pointer, in kSaveEverything frame
     add   r2, sp, #136   @ store gpr_res pointer, in kSaveEverything frame
     mov   r1, sp         @ pass SP
-    mov   r0, r9         @ pass Thread::Current
+    mov   r0, rSELF      @ pass Thread::Current
     blx   artInstrumentationMethodExitFromCode  @ (Thread*, SP, gpr_res*, fpr_res*)
 
     cbz   r0, .Ldo_deliver_instrumentation_exception
@@ -1896,7 +1896,7 @@
     .extern artDeoptimize
 ENTRY art_quick_deoptimize
     SETUP_SAVE_EVERYTHING_FRAME r0
-    mov    r0, r9         @ pass Thread::Current
+    mov    r0, rSELF      @ pass Thread::Current
     blx    artDeoptimize  @ (Thread*)
 END art_quick_deoptimize
 
@@ -1907,7 +1907,7 @@
     .extern artDeoptimizeFromCompiledCode
 ENTRY art_quick_deoptimize_from_compiled_code
     SETUP_SAVE_EVERYTHING_FRAME r1
-    mov    r1, r9                         @ pass Thread::Current
+    mov    r1, rSELF                      @ pass Thread::Current
     blx    artDeoptimizeFromCompiledCode  @ (DeoptimizationKind, Thread*)
 END art_quick_deoptimize_from_compiled_code
 
@@ -2610,6 +2610,7 @@
      *   art_quick_read_barrier_mark_introspection_arrays:            // @0x100
      *     Exactly 128 bytes for array load switch cases (16x2 instructions).
      */
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
     .balign 512
 ENTRY art_quick_read_barrier_mark_introspection
     // At this point, IP contains the reference, rMR is clobbered by the thunk
@@ -2676,11 +2677,16 @@
 art_quick_read_barrier_mark_introspection_arrays:
     BRBMI_FOR_REGISTERS BRBMI_ARRAY_LOAD, BRBMI_BKPT_FILL_8B
 END art_quick_read_barrier_mark_introspection
+#else  // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+ENTRY art_quick_read_barrier_mark_introspection
+    bkpt                              // Unreachable.
+END art_quick_read_barrier_mark_introspection
+#endif  // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
 
 .extern artInvokePolymorphic
 ENTRY art_quick_invoke_polymorphic
     SETUP_SAVE_REFS_AND_ARGS_FRAME r2
-    mov     r2, r9                 @ pass Thread::Current
+    mov     r2, rSELF              @ pass Thread::Current
     mov     r3, sp                 @ pass SP
     mov     r0, #0                 @ initialize 64-bit JValue as zero.
     str     r0, [sp, #-4]!
diff --git a/runtime/arch/arm/quick_method_frame_info_arm.h b/runtime/arch/arm/quick_method_frame_info_arm.h
deleted file mode 100644
index 5c5b81b..0000000
--- a/runtime/arch/arm/quick_method_frame_info_arm.h
+++ /dev/null
@@ -1,110 +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_RUNTIME_ARCH_ARM_QUICK_METHOD_FRAME_INFO_ARM_H_
-#define ART_RUNTIME_ARCH_ARM_QUICK_METHOD_FRAME_INFO_ARM_H_
-
-#include "arch/instruction_set.h"
-#include "base/bit_utils.h"
-#include "base/callee_save_type.h"
-#include "base/enums.h"
-#include "quick/quick_method_frame_info.h"
-#include "registers_arm.h"
-
-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);
-static constexpr uint32_t kArmCalleeSaveArgSpills =
-    (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 kArmCalleeSaveEverythingSpills =
-    (1 << art::arm::R0) | (1 << art::arm::R1) | (1 << art::arm::R2) | (1 << art::arm::R3) |
-    (1 << art::arm::R4) | (1 << art::arm::R9) | (1 << art::arm::R12);
-
-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);
-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);
-static constexpr uint32_t kArmCalleeSaveFpEverythingSpills =
-    kArmCalleeSaveFpArgSpills | kArmCalleeSaveFpAllSpills;
-
-constexpr uint32_t ArmCalleeSaveCoreSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kArmCalleeSaveAlwaysSpills | kArmCalleeSaveRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveArgSpills : 0) |
-      (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveAllSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveEverythingSpills : 0);
-}
-
-constexpr uint32_t ArmCalleeSaveFpSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) |
-      (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveFpEverythingSpills : 0);
-}
-
-constexpr uint32_t ArmCalleeSaveFrameSize(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return RoundUp((POPCOUNT(ArmCalleeSaveCoreSpills(type)) /* gprs */ +
-                  POPCOUNT(ArmCalleeSaveFpSpills(type)) /* fprs */ +
-                  1 /* Method* */) * static_cast<size_t>(kArmPointerSize), kStackAlignment);
-}
-
-constexpr QuickMethodFrameInfo ArmCalleeSaveMethodFrameInfo(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return QuickMethodFrameInfo(ArmCalleeSaveFrameSize(type),
-                              ArmCalleeSaveCoreSpills(type),
-                              ArmCalleeSaveFpSpills(type));
-}
-
-constexpr size_t ArmCalleeSaveFpr1Offset(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return ArmCalleeSaveFrameSize(type) -
-         (POPCOUNT(ArmCalleeSaveCoreSpills(type)) +
-          POPCOUNT(ArmCalleeSaveFpSpills(type))) * static_cast<size_t>(kArmPointerSize);
-}
-
-constexpr size_t ArmCalleeSaveGpr1Offset(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return ArmCalleeSaveFrameSize(type) -
-         POPCOUNT(ArmCalleeSaveCoreSpills(type)) * static_cast<size_t>(kArmPointerSize);
-}
-
-constexpr size_t ArmCalleeSaveLrOffset(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return ArmCalleeSaveFrameSize(type) -
-      POPCOUNT(ArmCalleeSaveCoreSpills(type) & (-(1 << LR))) * static_cast<size_t>(kArmPointerSize);
-}
-
-}  // namespace arm
-}  // namespace art
-
-#endif  // ART_RUNTIME_ARCH_ARM_QUICK_METHOD_FRAME_INFO_ARM_H_
diff --git a/runtime/arch/arm64/quick_method_frame_info_arm64.h b/runtime/arch/arm64/callee_save_frame_arm64.h
similarity index 61%
rename from runtime/arch/arm64/quick_method_frame_info_arm64.h
rename to runtime/arch/arm64/callee_save_frame_arm64.h
index 3e6f6c6..bc36bfa 100644
--- a/runtime/arch/arm64/quick_method_frame_info_arm64.h
+++ b/runtime/arch/arm64/callee_save_frame_arm64.h
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_ARCH_ARM64_QUICK_METHOD_FRAME_INFO_ARM64_H_
-#define ART_RUNTIME_ARCH_ARM64_QUICK_METHOD_FRAME_INFO_ARM64_H_
+#ifndef ART_RUNTIME_ARCH_ARM64_CALLEE_SAVE_FRAME_ARM64_H_
+#define ART_RUNTIME_ARCH_ARM64_CALLEE_SAVE_FRAME_ARM64_H_
 
 #include "arch/instruction_set.h"
 #include "base/bit_utils.h"
 #include "base/callee_save_type.h"
 #include "base/enums.h"
-#include "globals.h"
+#include "base/globals.h"
 #include "quick/quick_method_frame_info.h"
 #include "registers_arm64.h"
 
@@ -79,57 +79,56 @@
     (1 << art::arm64::D27) | (1 << art::arm64::D28) | (1 << art::arm64::D29) |
     (1 << art::arm64::D30) | (1 << art::arm64::D31);
 
-constexpr uint32_t Arm64CalleeSaveCoreSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kArm64CalleeSaveAlwaysSpills | kArm64CalleeSaveRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveArgSpills : 0) |
-      (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveAllSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveEverythingSpills : 0);
-}
+class Arm64CalleeSaveFrame {
+ public:
+  static constexpr uint32_t GetCoreSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kArm64CalleeSaveAlwaysSpills | kArm64CalleeSaveRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveArgSpills : 0) |
+        (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveAllSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveEverythingSpills : 0);
+  }
 
-constexpr uint32_t Arm64CalleeSaveFpSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) |
-      (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveFpEverythingSpills : 0);
-}
+  static constexpr uint32_t GetFpSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) |
+        (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveFpEverythingSpills : 0);
+  }
 
-constexpr uint32_t Arm64CalleeSaveFrameSize(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return RoundUp((POPCOUNT(Arm64CalleeSaveCoreSpills(type)) /* gprs */ +
-                  POPCOUNT(Arm64CalleeSaveFpSpills(type)) /* fprs */ +
-                  1 /* Method* */) * static_cast<size_t>(kArm64PointerSize), kStackAlignment);
-}
+  static constexpr uint32_t GetFrameSize(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ +
+                    POPCOUNT(GetFpSpills(type)) /* fprs */ +
+                    1 /* Method* */) * static_cast<size_t>(kArm64PointerSize), kStackAlignment);
+  }
 
-constexpr QuickMethodFrameInfo Arm64CalleeSaveMethodFrameInfo(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return QuickMethodFrameInfo(Arm64CalleeSaveFrameSize(type),
-                              Arm64CalleeSaveCoreSpills(type),
-                              Arm64CalleeSaveFpSpills(type));
-}
+  static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type));
+  }
 
-constexpr size_t Arm64CalleeSaveFpr1Offset(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return Arm64CalleeSaveFrameSize(type) -
-         (POPCOUNT(Arm64CalleeSaveCoreSpills(type)) +
-          POPCOUNT(Arm64CalleeSaveFpSpills(type))) * static_cast<size_t>(kArm64PointerSize);
-}
+  static constexpr size_t GetFpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           (POPCOUNT(GetCoreSpills(type)) +
+            POPCOUNT(GetFpSpills(type))) * static_cast<size_t>(kArm64PointerSize);
+  }
 
-constexpr size_t Arm64CalleeSaveGpr1Offset(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return Arm64CalleeSaveFrameSize(type) -
-         POPCOUNT(Arm64CalleeSaveCoreSpills(type)) * static_cast<size_t>(kArm64PointerSize);
-}
+  static constexpr size_t GetGpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           POPCOUNT(GetCoreSpills(type)) * static_cast<size_t>(kArm64PointerSize);
+  }
 
-constexpr size_t Arm64CalleeSaveLrOffset(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return Arm64CalleeSaveFrameSize(type) -
-      POPCOUNT(Arm64CalleeSaveCoreSpills(type) & (-(1 << LR))) *
-      static_cast<size_t>(kArm64PointerSize);
-}
+  static constexpr size_t GetReturnPcOffset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) - static_cast<size_t>(kArm64PointerSize);
+  }
+};
 
 }  // namespace arm64
 }  // namespace art
 
-#endif  // ART_RUNTIME_ARCH_ARM64_QUICK_METHOD_FRAME_INFO_ARM64_H_
+#endif  // ART_RUNTIME_ARCH_ARM64_CALLEE_SAVE_FRAME_ARM64_H_
diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index d282c8c..e8b4627 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -20,10 +20,10 @@
 
 #include "art_method.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "base/hex_dump.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
-#include "globals.h"
 #include "registers_arm64.h"
 #include "thread-current-inl.h"
 
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 42c9a84..d0f61c9 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -52,6 +52,8 @@
     // Check to see if this is an expected variant.
     static const char* arm64_known_variants[] = {
         "cortex-a35",
+        "cortex-a55",
+        "cortex-a75",
         "exynos-m1",
         "exynos-m2",
         "exynos-m3",
diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
index 7fd39b6..b946f4f 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
@@ -64,6 +64,26 @@
   EXPECT_FALSE(kryo_features->Equals(cortex_a57_features.get()));
   EXPECT_STREQ("-a53", kryo_features->GetFeatureString().c_str());
   EXPECT_EQ(kryo_features->AsBitmap(), 0U);
+
+  std::unique_ptr<const InstructionSetFeatures> cortex_a55_features(
+      InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a55", &error_msg));
+  ASSERT_TRUE(cortex_a55_features.get() != nullptr) << error_msg;
+  EXPECT_EQ(cortex_a55_features->GetInstructionSet(), InstructionSet::kArm64);
+  EXPECT_TRUE(cortex_a55_features->Equals(cortex_a55_features.get()));
+  EXPECT_TRUE(cortex_a55_features->Equals(cortex_a35_features.get()));
+  EXPECT_FALSE(cortex_a55_features->Equals(cortex_a57_features.get()));
+  EXPECT_STREQ("-a53", cortex_a55_features->GetFeatureString().c_str());
+  EXPECT_EQ(cortex_a55_features->AsBitmap(), 0U);
+
+  std::unique_ptr<const InstructionSetFeatures> cortex_a75_features(
+      InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a75", &error_msg));
+  ASSERT_TRUE(cortex_a75_features.get() != nullptr) << error_msg;
+  EXPECT_EQ(cortex_a75_features->GetInstructionSet(), InstructionSet::kArm64);
+  EXPECT_TRUE(cortex_a75_features->Equals(cortex_a75_features.get()));
+  EXPECT_TRUE(cortex_a75_features->Equals(cortex_a35_features.get()));
+  EXPECT_FALSE(cortex_a75_features->Equals(cortex_a57_features.get()));
+  EXPECT_STREQ("-a53", cortex_a75_features->GetFeatureString().c_str());
+  EXPECT_EQ(cortex_a75_features->AsBitmap(), 0U);
 }
 
 }  // namespace art
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 9ff5ebe..14d0cc7 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1151,45 +1151,36 @@
      */
     .extern artLockObjectFromCode
 ENTRY art_quick_lock_object
-    cbz    w0, .Lslow_lock
-    add    x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET  // exclusive load/store has no immediate anymore
+    ldr    w1, [xSELF, #THREAD_ID_OFFSET]
+    cbz    w0, art_quick_lock_object_no_inline
+                                      // Exclusive load/store has no immediate anymore.
+    add    x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET
 .Lretry_lock:
-    ldr    w2, [xSELF, #THREAD_ID_OFFSET] // TODO: Can the thread ID really change during the loop?
-    ldaxr  w1, [x4]                   // acquire needed only in most common case
-    and    w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  // zero the gc bits
-    cbnz   w3, .Lnot_unlocked         // already thin locked
-    // unlocked case - x1: original lock word that's zero except for the read barrier bits.
-    orr    x2, x1, x2                 // x2 holds thread id with count of 0 with preserved read barrier bits
-    stxr   w3, w2, [x4]
-    cbnz   w3, .Llock_stxr_fail       // store failed, retry
+    ldaxr  w2, [x4]                   // Acquire needed only in most common case.
+    eor    w3, w2, w1                 // Prepare the value to store if unlocked
+                                      //   (thread id, count of 0 and preserved read barrier bits),
+                                      // or prepare to compare thread id for recursive lock check
+                                      //   (lock_word.ThreadId() ^ self->ThreadId()).
+    tst    w2, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  // Test the non-gc bits.
+    b.ne   .Lnot_unlocked             // Check if unlocked.
+    // unlocked case - store w3: original lock word plus thread id, preserved read barrier bits.
+    stxr   w2, w3, [x4]
+    cbnz   w2, .Lretry_lock           // If the store failed, retry.
     ret
-.Lnot_unlocked:  // x1: original lock word
-    lsr    w3, w1, LOCK_WORD_STATE_SHIFT
-    cbnz   w3, .Lslow_lock            // if either of the top two bits are set, go slow path
-    eor    w2, w1, w2                 // lock_word.ThreadId() ^ self->ThreadId()
-    uxth   w2, w2                     // zero top 16 bits
-    cbnz   w2, .Lslow_lock            // lock word and self thread id's match -> recursive lock
-                                      // else contention, go to slow path
-    and    w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  // zero the gc bits.
-    add    w2, w3, #LOCK_WORD_THIN_LOCK_COUNT_ONE  // increment count in lock word placing in w2 to check overflow
-    lsr    w3, w2, #LOCK_WORD_GC_STATE_SHIFT     // if the first gc state bit is set, we overflowed.
-    cbnz   w3, .Lslow_lock            // if we overflow the count go slow path
-    add    w2, w1, #LOCK_WORD_THIN_LOCK_COUNT_ONE  // increment count for real
-    stxr   w3, w2, [x4]
-    cbnz   w3, .Llock_stxr_fail       // store failed, retry
+.Lnot_unlocked:  // w2: original lock word, w1: thread id, w3: w2 ^ w1
+                                      // Check lock word state and thread id together,
+    tst    w3, #(LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED)
+    b.ne   art_quick_lock_object_no_inline
+    add    w3, w2, #LOCK_WORD_THIN_LOCK_COUNT_ONE  // Increment the recursive lock count.
+    tst    w3, #LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED  // Test the new thin lock count.
+    b.eq   art_quick_lock_object_no_inline  // Zero as the new count indicates overflow, go slow path.
+    stxr   w2, w3, [x4]
+    cbnz   w2, .Lretry_lock           // If the store failed, retry.
     ret
-.Llock_stxr_fail:
-    b      .Lretry_lock               // retry
-.Lslow_lock:
-    SETUP_SAVE_REFS_ONLY_FRAME        // save callee saves in case we block
-    mov    x1, xSELF                  // pass Thread::Current
-    bl     artLockObjectFromCode      // (Object* obj, Thread*)
-    RESTORE_SAVE_REFS_ONLY_FRAME
-    REFRESH_MARKING_REGISTER
-    RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_lock_object
 
 ENTRY art_quick_lock_object_no_inline
+    // This is also the slow path for art_quick_lock_object.
     SETUP_SAVE_REFS_ONLY_FRAME        // save callee saves in case we block
     mov    x1, xSELF                  // pass Thread::Current
     bl     artLockObjectFromCode      // (Object* obj, Thread*)
@@ -1206,54 +1197,46 @@
      */
     .extern artUnlockObjectFromCode
 ENTRY art_quick_unlock_object
-    cbz    x0, .Lslow_unlock
-    add    x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET  // exclusive load/store has no immediate anymore
+    ldr    w1, [xSELF, #THREAD_ID_OFFSET]
+    cbz    x0, art_quick_unlock_object_no_inline
+                                      // Exclusive load/store has no immediate anymore.
+    add    x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET
 .Lretry_unlock:
 #ifndef USE_READ_BARRIER
-    ldr    w1, [x4]
+    ldr    w2, [x4]
 #else
-    ldxr   w1, [x4]                   // Need to use atomic instructions for read barrier
+    ldxr   w2, [x4]                   // Need to use atomic instructions for read barrier.
 #endif
-    lsr    w2, w1, LOCK_WORD_STATE_SHIFT
-    cbnz   w2, .Lslow_unlock          // if either of the top two bits are set, go slow path
-    ldr    w2, [xSELF, #THREAD_ID_OFFSET]
-    and    w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  // zero the gc bits
-    eor    w3, w3, w2                 // lock_word.ThreadId() ^ self->ThreadId()
-    uxth   w3, w3                     // zero top 16 bits
-    cbnz   w3, .Lslow_unlock          // do lock word and self thread id's match?
-    and    w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  // zero the gc bits
-    cmp    w3, #LOCK_WORD_THIN_LOCK_COUNT_ONE
-    bpl    .Lrecursive_thin_unlock
-    // transition to unlocked
-    and    w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED  // w3: zero except for the preserved read barrier bits
+    eor    w3, w2, w1                 // Prepare the value to store if simply locked
+                                      //   (mostly 0s, and preserved read barrier bits),
+                                      // or prepare to compare thread id for recursive lock check
+                                      //   (lock_word.ThreadId() ^ self->ThreadId()).
+    tst    w3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  // Test the non-gc bits.
+    b.ne   .Lnot_simply_locked        // Locked recursively or by other thread?
+    // Transition to unlocked.
 #ifndef USE_READ_BARRIER
     stlr   w3, [x4]
 #else
-    stlxr  w2, w3, [x4]               // Need to use atomic instructions for read barrier
-    cbnz   w2, .Lunlock_stxr_fail     // store failed, retry
+    stlxr  w2, w3, [x4]               // Need to use atomic instructions for read barrier.
+    cbnz   w2, .Lretry_unlock         // If the store failed, retry.
 #endif
     ret
-.Lrecursive_thin_unlock:  // w1: original lock word
-    sub    w1, w1, #LOCK_WORD_THIN_LOCK_COUNT_ONE  // decrement count
+.Lnot_simply_locked:
+                                      // Check lock word state and thread id together,
+    tst    w3, #(LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED)
+    b.ne   art_quick_unlock_object_no_inline
+    sub    w3, w2, #LOCK_WORD_THIN_LOCK_COUNT_ONE  // decrement count
 #ifndef USE_READ_BARRIER
-    str    w1, [x4]
+    str    w3, [x4]
 #else
-    stxr   w2, w1, [x4]               // Need to use atomic instructions for read barrier
-    cbnz   w2, .Lunlock_stxr_fail     // store failed, retry
+    stxr   w2, w3, [x4]               // Need to use atomic instructions for read barrier.
+    cbnz   w2, .Lretry_unlock         // If the store failed, retry.
 #endif
     ret
-.Lunlock_stxr_fail:
-    b      .Lretry_unlock             // retry
-.Lslow_unlock:
-    SETUP_SAVE_REFS_ONLY_FRAME        // save callee saves in case exception allocation triggers GC
-    mov    x1, xSELF                  // pass Thread::Current
-    bl     artUnlockObjectFromCode    // (Object* obj, Thread*)
-    RESTORE_SAVE_REFS_ONLY_FRAME
-    REFRESH_MARKING_REGISTER
-    RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_unlock_object
 
 ENTRY art_quick_unlock_object_no_inline
+    // This is also the slow path for art_quick_unlock_object.
     SETUP_SAVE_REFS_ONLY_FRAME        // save callee saves in case exception allocation triggers GC
     mov    x1, xSELF                  // pass Thread::Current
     bl     artUnlockObjectFromCode    // (Object* obj, Thread*)
@@ -1533,7 +1516,10 @@
 END \name
 .endm
 
-// Macro for string and type resolution and initialization.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings.
+     */
 .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
     .extern \entrypoint
 ENTRY \name
@@ -1577,6 +1563,8 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
 // Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are
diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h
index 8e8dde4..f0c6d22 100644
--- a/runtime/arch/code_offset.h
+++ b/runtime/arch/code_offset.h
@@ -21,9 +21,9 @@
 
 #include <android-base/logging.h>
 
+#include "arch/instruction_set.h"
 #include "base/bit_utils.h"
 #include "base/macros.h"
-#include "instruction_set.h"
 
 namespace art {
 
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index 5f1a507..c31c927 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -21,8 +21,8 @@
 #include <ostream>
 #include <vector>
 
+#include "arch/instruction_set.h"
 #include "base/macros.h"
-#include "instruction_set.h"
 
 namespace art {
 
diff --git a/runtime/arch/mips/quick_method_frame_info_mips.h b/runtime/arch/mips/callee_save_frame_mips.h
similarity index 66%
rename from runtime/arch/mips/quick_method_frame_info_mips.h
rename to runtime/arch/mips/callee_save_frame_mips.h
index 8c86252..6e88d08 100644
--- a/runtime/arch/mips/quick_method_frame_info_mips.h
+++ b/runtime/arch/mips/callee_save_frame_mips.h
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_ARCH_MIPS_QUICK_METHOD_FRAME_INFO_MIPS_H_
-#define ART_RUNTIME_ARCH_MIPS_QUICK_METHOD_FRAME_INFO_MIPS_H_
+#ifndef ART_RUNTIME_ARCH_MIPS_CALLEE_SAVE_FRAME_MIPS_H_
+#define ART_RUNTIME_ARCH_MIPS_CALLEE_SAVE_FRAME_MIPS_H_
 
 #include "arch/instruction_set.h"
 #include "base/bit_utils.h"
 #include "base/callee_save_type.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "quick/quick_method_frame_info.h"
 #include "registers_mips.h"
 
@@ -80,37 +81,56 @@
     (1 << art::mips::F24) | (1 << art::mips::F25) | (1 << art::mips::F26) | (1 << art::mips::F27) |
     (1 << art::mips::F28) | (1 << art::mips::F29) | (1 << art::mips::F30) | (1u << art::mips::F31);
 
-constexpr uint32_t MipsCalleeSaveCoreSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kMipsCalleeSaveAlwaysSpills | kMipsCalleeSaveRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveArgSpills : 0) |
-      (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveEverythingSpills : 0);
-}
+class MipsCalleeSaveFrame {
+ public:
+  static constexpr uint32_t GetCoreSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kMipsCalleeSaveAlwaysSpills | kMipsCalleeSaveRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveArgSpills : 0) |
+        (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveEverythingSpills : 0);
+  }
 
-constexpr uint32_t MipsCalleeSaveFPSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kMipsCalleeSaveFpAlwaysSpills | kMipsCalleeSaveFpRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveFpArgSpills : 0) |
-      (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllFPSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveFpEverythingSpills : 0);
-}
+  static constexpr uint32_t GetFpSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kMipsCalleeSaveFpAlwaysSpills | kMipsCalleeSaveFpRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveFpArgSpills : 0) |
+        (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllFPSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveFpEverythingSpills : 0);
+  }
 
-constexpr uint32_t MipsCalleeSaveFrameSize(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return RoundUp((POPCOUNT(MipsCalleeSaveCoreSpills(type)) /* gprs */ +
-                  POPCOUNT(MipsCalleeSaveFPSpills(type))   /* fprs */ +
-                  1 /* Method* */) * static_cast<size_t>(kMipsPointerSize), kStackAlignment);
-}
+  static constexpr uint32_t GetFrameSize(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ +
+                    POPCOUNT(GetFpSpills(type))   /* fprs */ +
+                    1 /* Method* */) * static_cast<size_t>(kMipsPointerSize), kStackAlignment);
+  }
 
-constexpr QuickMethodFrameInfo MipsCalleeSaveMethodFrameInfo(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return QuickMethodFrameInfo(MipsCalleeSaveFrameSize(type),
-                              MipsCalleeSaveCoreSpills(type),
-                              MipsCalleeSaveFPSpills(type));
-}
+  static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type));
+  }
+
+  static constexpr size_t GetFpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           (POPCOUNT(GetCoreSpills(type)) +
+            POPCOUNT(GetFpSpills(type))) * static_cast<size_t>(kMipsPointerSize);
+  }
+
+  static constexpr size_t GetGpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           POPCOUNT(GetCoreSpills(type)) * static_cast<size_t>(kMipsPointerSize);
+  }
+
+  static constexpr size_t GetReturnPcOffset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) - static_cast<size_t>(kMipsPointerSize);
+  }
+};
 
 }  // namespace mips
 }  // namespace art
 
-#endif  // ART_RUNTIME_ARCH_MIPS_QUICK_METHOD_FRAME_INFO_MIPS_H_
+#endif  // ART_RUNTIME_ARCH_MIPS_CALLEE_SAVE_FRAME_MIPS_H_
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 9418caf..5d6e410 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -203,6 +203,10 @@
   static_assert(!IsDirectEntrypoint(kQuickInitializeType), "Non-direct C stub marked direct.");
   qpoints->pResolveString = art_quick_resolve_string;
   static_assert(!IsDirectEntrypoint(kQuickResolveString), "Non-direct C stub marked direct.");
+  qpoints->pResolveMethodHandle = art_quick_resolve_method_handle;
+  static_assert(!IsDirectEntrypoint(kQuickResolveMethodHandle), "Non-direct C stub marked direct.");
+  qpoints->pResolveMethodType = art_quick_resolve_method_type;
+  static_assert(!IsDirectEntrypoint(kQuickResolveMethodType), "Non-direct C stub marked direct.");
 
   // Field
   qpoints->pSet8Instance = art_quick_set8_instance;
diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc
index f82dc08..7c8ac28 100644
--- a/runtime/arch/mips/fault_handler_mips.cc
+++ b/runtime/arch/mips/fault_handler_mips.cc
@@ -17,13 +17,13 @@
 #include <sys/ucontext.h>
 #include "fault_handler.h"
 
+#include "arch/mips/callee_save_frame_mips.h"
 #include "art_method.h"
 #include "base/callee_save_type.h"
+#include "base/globals.h"
 #include "base/hex_dump.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
-#include "globals.h"
-#include "quick_method_frame_info_mips.h"
 #include "registers_mips.h"
 #include "thread-current-inl.h"
 
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index d8fe480..c367ea6 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -2027,8 +2027,11 @@
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
 
-// Macro for string and type resolution and initialization.
-// $a0 is both input and output.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings. $a0 is both input and
+     * output.
+     */
 .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
     .extern \entrypoint
 ENTRY_NO_GP \name
@@ -2053,6 +2056,18 @@
 .endm
 
     /*
+     * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle
+     * index. On success the MethodHandle is returned, otherwise an exception is raised.
+     */
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode
+
+    /*
+     * Entry from managed code to resolve a method type. On entry, A0 holds the method type index.
+     * On success the MethodType is returned, otherwise an exception is raised.
+     */
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
+
+    /*
      * 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. A0 holds the string index. The fast
      * path check for hit in strings cache has already been performed.
diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h
index c7f9a3e..34f2f96 100644
--- a/runtime/arch/mips/registers_mips.h
+++ b/runtime/arch/mips/registers_mips.h
@@ -21,8 +21,8 @@
 
 #include <android-base/logging.h>
 
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 namespace art {
 namespace mips {
diff --git a/runtime/arch/mips64/quick_method_frame_info_mips64.h b/runtime/arch/mips64/callee_save_frame_mips64.h
similarity index 61%
rename from runtime/arch/mips64/quick_method_frame_info_mips64.h
rename to runtime/arch/mips64/callee_save_frame_mips64.h
index 520f631..59529a0 100644
--- a/runtime/arch/mips64/quick_method_frame_info_mips64.h
+++ b/runtime/arch/mips64/callee_save_frame_mips64.h
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_
-#define ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_
+#ifndef ART_RUNTIME_ARCH_MIPS64_CALLEE_SAVE_FRAME_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_CALLEE_SAVE_FRAME_MIPS64_H_
 
 #include "arch/instruction_set.h"
 #include "base/bit_utils.h"
 #include "base/callee_save_type.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "quick/quick_method_frame_info.h"
 #include "registers_mips64.h"
 
@@ -71,37 +72,56 @@
     (1 << art::mips64::F27) | (1 << art::mips64::F28) | (1 << art::mips64::F29) |
     (1 << art::mips64::F30) | (1 << art::mips64::F31);
 
-constexpr uint32_t Mips64CalleeSaveCoreSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kMips64CalleeSaveAlwaysSpills | kMips64CalleeSaveRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) |
-      (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveAllSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveEverythingSpills : 0);
-}
+class Mips64CalleeSaveFrame {
+ public:
+  static constexpr uint32_t GetCoreSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kMips64CalleeSaveAlwaysSpills | kMips64CalleeSaveRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) |
+        (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveAllSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveEverythingSpills : 0);
+  }
 
-constexpr uint32_t Mips64CalleeSaveFpSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kMips64CalleeSaveFpRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) |
-      (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveFpEverythingSpills : 0);
-}
+  static constexpr uint32_t GetFpSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kMips64CalleeSaveFpRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) |
+        (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveFpEverythingSpills : 0);
+  }
 
-constexpr uint32_t Mips64CalleeSaveFrameSize(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return RoundUp((POPCOUNT(Mips64CalleeSaveCoreSpills(type)) /* gprs */ +
-                  POPCOUNT(Mips64CalleeSaveFpSpills(type))   /* fprs */ +
-                  + 1 /* Method* */) * static_cast<size_t>(kMips64PointerSize), kStackAlignment);
-}
+  static constexpr uint32_t GetFrameSize(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ +
+                    POPCOUNT(GetFpSpills(type))   /* fprs */ +
+                    + 1 /* Method* */) * static_cast<size_t>(kMips64PointerSize), kStackAlignment);
+  }
 
-constexpr QuickMethodFrameInfo Mips64CalleeSaveMethodFrameInfo(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return QuickMethodFrameInfo(Mips64CalleeSaveFrameSize(type),
-                              Mips64CalleeSaveCoreSpills(type),
-                              Mips64CalleeSaveFpSpills(type));
-}
+  static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type));
+  }
+
+  static constexpr size_t GetFpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           (POPCOUNT(GetCoreSpills(type)) +
+            POPCOUNT(GetFpSpills(type))) * static_cast<size_t>(kMips64PointerSize);
+  }
+
+  static constexpr size_t GetGpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           POPCOUNT(GetCoreSpills(type)) * static_cast<size_t>(kMips64PointerSize);
+  }
+
+  static constexpr size_t GetReturnPcOffset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) - static_cast<size_t>(kMips64PointerSize);
+  }
+};
 
 }  // namespace mips64
 }  // namespace art
 
-#endif  // ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_
+#endif  // ART_RUNTIME_ARCH_MIPS64_CALLEE_SAVE_FRAME_MIPS64_H_
diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc
index ba6fff0..85f3528 100644
--- a/runtime/arch/mips64/fault_handler_mips64.cc
+++ b/runtime/arch/mips64/fault_handler_mips64.cc
@@ -18,13 +18,13 @@
 
 #include <sys/ucontext.h>
 
+#include "arch/mips64/callee_save_frame_mips64.h"
 #include "art_method.h"
 #include "base/callee_save_type.h"
+#include "base/globals.h"
 #include "base/hex_dump.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
-#include "globals.h"
-#include "quick_method_frame_info_mips64.h"
 #include "registers_mips64.h"
 #include "thread-current-inl.h"
 
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 8d2a7bd..1f4f174 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1930,8 +1930,11 @@
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
 
-// Macro for string and type resolution and initialization.
-// $a0 is both input and output.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings. $a0 is both input and
+     * output.
+     */
 .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
     .extern \entrypoint
 ENTRY_NO_GP \name
@@ -1953,6 +1956,18 @@
 .endm
 
     /*
+     * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle
+     * index. On success the MethodHandle is returned, otherwise an exception is raised.
+     */
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode
+
+    /*
+     * Entry from managed code to resolve a method type. On entry, A0 holds the method type index.
+     * On success the MethodType is returned, otherwise an exception is raised.
+     */
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
+
+    /*
      * 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. A0 holds the string index. The fast
      * path check for hit in strings cache has already been performed.
diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h
index d3a24b6..a3fa2ac4 100644
--- a/runtime/arch/mips64/registers_mips64.h
+++ b/runtime/arch/mips64/registers_mips64.h
@@ -21,8 +21,8 @@
 
 #include <android-base/logging.h>
 
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 namespace art {
 namespace mips64 {
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 4be4b12..78516e3 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -24,7 +24,7 @@
 #include "common_runtime_test.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "imt_conflict_table.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "linear_alloc.h"
 #include "mirror/class-inl.h"
 #include "mirror/string-inl.h"
diff --git a/runtime/arch/x86/callee_save_frame_x86.h b/runtime/arch/x86/callee_save_frame_x86.h
new file mode 100644
index 0000000..f336f43
--- /dev/null
+++ b/runtime/arch/x86/callee_save_frame_x86.h
@@ -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.
+ */
+
+#ifndef ART_RUNTIME_ARCH_X86_CALLEE_SAVE_FRAME_X86_H_
+#define ART_RUNTIME_ARCH_X86_CALLEE_SAVE_FRAME_X86_H_
+
+#include "arch/instruction_set.h"
+#include "base/bit_utils.h"
+#include "base/callee_save_type.h"
+#include "base/enums.h"
+#include "base/globals.h"
+#include "quick/quick_method_frame_info.h"
+#include "registers_x86.h"
+
+namespace art {
+namespace x86 {
+
+static constexpr uint32_t kX86CalleeSaveAlwaysSpills =
+    (1 << art::x86::kNumberOfCpuRegisters);  // Fake return address callee save.
+static constexpr uint32_t kX86CalleeSaveRefSpills =
+    (1 << art::x86::EBP) | (1 << art::x86::ESI) | (1 << art::x86::EDI);
+static constexpr uint32_t kX86CalleeSaveArgSpills =
+    (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX);
+static constexpr uint32_t kX86CalleeSaveEverythingSpills =
+    (1 << art::x86::EAX) | (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX);
+
+static constexpr uint32_t kX86CalleeSaveFpArgSpills =
+    (1 << art::x86::XMM0) | (1 << art::x86::XMM1) |
+    (1 << art::x86::XMM2) | (1 << art::x86::XMM3);
+static constexpr uint32_t kX86CalleeSaveFpEverythingSpills =
+    (1 << art::x86::XMM0) | (1 << art::x86::XMM1) |
+    (1 << art::x86::XMM2) | (1 << art::x86::XMM3) |
+    (1 << art::x86::XMM4) | (1 << art::x86::XMM5) |
+    (1 << art::x86::XMM6) | (1 << art::x86::XMM7);
+
+class X86CalleeSaveFrame {
+ public:
+  static constexpr uint32_t GetCoreSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kX86CalleeSaveAlwaysSpills | kX86CalleeSaveRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveArgSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveEverythingSpills : 0);
+  }
+
+  static constexpr uint32_t GetFpSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) |
+           (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0);
+  }
+
+  static constexpr uint32_t GetFrameSize(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ +
+                    2 * POPCOUNT(GetFpSpills(type)) /* fprs */ +
+                    1 /* Method* */) * static_cast<size_t>(kX86PointerSize), kStackAlignment);
+  }
+
+  static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type));
+  }
+
+  static constexpr size_t GetFpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           (POPCOUNT(GetCoreSpills(type)) +
+            2 * POPCOUNT(GetFpSpills(type))) * static_cast<size_t>(kX86PointerSize);
+  }
+
+  static constexpr size_t GetGpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           POPCOUNT(GetCoreSpills(type)) * static_cast<size_t>(kX86PointerSize);
+  }
+
+  static constexpr size_t GetReturnPcOffset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) - static_cast<size_t>(kX86PointerSize);
+  }
+};
+
+}  // namespace x86
+}  // namespace art
+
+#endif  // ART_RUNTIME_ARCH_X86_CALLEE_SAVE_FRAME_X86_H_
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index e6a9124..8b24334 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -20,11 +20,11 @@
 
 #include "art_method.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "base/hex_dump.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
 #include "base/safe_copy.h"
-#include "globals.h"
 #include "thread-current-inl.h"
 
 #if defined(__APPLE__)
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index df43aef..b89d45f 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -923,7 +923,10 @@
     END_FUNCTION VAR(c_name)
 END_MACRO
 
-// Macro for string and type resolution and initialization.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings.
+     */
 MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
     DEFINE_FUNCTION VAR(c_name)
     SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, \runtime_method_offset  // save ref containing registers for GC
@@ -932,7 +935,7 @@
     CFI_ADJUST_CFA_OFFSET(8)
     pushl %fs:THREAD_SELF_OFFSET                      // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    PUSH eax                                          // pass arg1
+    PUSH eax                                          // pass the index of the constant as arg1
     call CALLVAR(cxx_name)                            // cxx_name(arg1, Thread*)
     addl MACRO_LITERAL(16), %esp                      // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
@@ -1278,6 +1281,8 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
 TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
@@ -1287,7 +1292,7 @@
     jz   .Lslow_lock
 .Lretry_lock:
     movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx  // ecx := lock word
-    test LITERAL(LOCK_WORD_STATE_MASK), %ecx         // test the 2 high bits.
+    test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx  // test the 2 high bits.
     jne  .Lslow_lock                      // slow path if either of the two high bits are set.
     movl %ecx, %edx                       // save lock word (edx) to keep read barrier bits.
     andl LITERAL(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED), %ecx  // zero the gc bits.
@@ -1357,7 +1362,7 @@
 .Lretry_unlock:
     movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx  // ecx := lock word
     movl %fs:THREAD_ID_OFFSET, %edx       // edx := thread id
-    test LITERAL(LOCK_WORD_STATE_MASK), %ecx
+    test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx
     jnz  .Lslow_unlock                    // lock word contains a monitor
     cmpw %cx, %dx                         // does the thread id match?
     jne  .Lslow_unlock
diff --git a/runtime/arch/x86/quick_method_frame_info_x86.h b/runtime/arch/x86/quick_method_frame_info_x86.h
deleted file mode 100644
index 9a66333..0000000
--- a/runtime/arch/x86/quick_method_frame_info_x86.h
+++ /dev/null
@@ -1,89 +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_RUNTIME_ARCH_X86_QUICK_METHOD_FRAME_INFO_X86_H_
-#define ART_RUNTIME_ARCH_X86_QUICK_METHOD_FRAME_INFO_X86_H_
-
-#include "arch/instruction_set.h"
-#include "base/bit_utils.h"
-#include "base/callee_save_type.h"
-#include "base/enums.h"
-#include "quick/quick_method_frame_info.h"
-#include "registers_x86.h"
-
-namespace art {
-namespace x86 {
-
-enum XMM {
-  XMM0 = 0,
-  XMM1 = 1,
-  XMM2 = 2,
-  XMM3 = 3,
-  XMM4 = 4,
-  XMM5 = 5,
-  XMM6 = 6,
-  XMM7 = 7,
-};
-
-static constexpr uint32_t kX86CalleeSaveAlwaysSpills =
-    (1 << art::x86::kNumberOfCpuRegisters);  // Fake return address callee save.
-static constexpr uint32_t kX86CalleeSaveRefSpills =
-    (1 << art::x86::EBP) | (1 << art::x86::ESI) | (1 << art::x86::EDI);
-static constexpr uint32_t kX86CalleeSaveArgSpills =
-    (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX);
-static constexpr uint32_t kX86CalleeSaveEverythingSpills =
-    (1 << art::x86::EAX) | (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX);
-
-static constexpr uint32_t kX86CalleeSaveFpArgSpills =
-    (1 << art::x86::XMM0) | (1 << art::x86::XMM1) |
-    (1 << art::x86::XMM2) | (1 << art::x86::XMM3);
-static constexpr uint32_t kX86CalleeSaveFpEverythingSpills =
-    (1 << art::x86::XMM0) | (1 << art::x86::XMM1) |
-    (1 << art::x86::XMM2) | (1 << art::x86::XMM3) |
-    (1 << art::x86::XMM4) | (1 << art::x86::XMM5) |
-    (1 << art::x86::XMM6) | (1 << art::x86::XMM7);
-
-constexpr uint32_t X86CalleeSaveCoreSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kX86CalleeSaveAlwaysSpills | kX86CalleeSaveRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveArgSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveEverythingSpills : 0);
-}
-
-constexpr uint32_t X86CalleeSaveFpSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) |
-         (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0);
-}
-
-constexpr uint32_t X86CalleeSaveFrameSize(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return RoundUp((POPCOUNT(X86CalleeSaveCoreSpills(type)) /* gprs */ +
-                  2 * POPCOUNT(X86CalleeSaveFpSpills(type)) /* fprs */ +
-                  1 /* Method* */) * static_cast<size_t>(kX86PointerSize), kStackAlignment);
-}
-
-constexpr QuickMethodFrameInfo X86CalleeSaveMethodFrameInfo(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return QuickMethodFrameInfo(X86CalleeSaveFrameSize(type),
-                              X86CalleeSaveCoreSpills(type),
-                              X86CalleeSaveFpSpills(type));
-}
-
-}  // namespace x86
-}  // namespace art
-
-#endif  // ART_RUNTIME_ARCH_X86_QUICK_METHOD_FRAME_INFO_X86_H_
diff --git a/runtime/arch/x86/registers_x86.h b/runtime/arch/x86/registers_x86.h
index ded3520..d3b959f 100644
--- a/runtime/arch/x86/registers_x86.h
+++ b/runtime/arch/x86/registers_x86.h
@@ -21,8 +21,8 @@
 
 #include <android-base/logging.h>
 
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 namespace art {
 namespace x86 {
@@ -42,6 +42,20 @@
 };
 std::ostream& operator<<(std::ostream& os, const Register& rhs);
 
+enum XmmRegister {
+  XMM0 = 0,
+  XMM1 = 1,
+  XMM2 = 2,
+  XMM3 = 3,
+  XMM4 = 4,
+  XMM5 = 5,
+  XMM6 = 6,
+  XMM7 = 7,
+  kNumberOfXmmRegisters = 8,
+  kNoXmmRegister = -1  // Signals an illegal register.
+};
+std::ostream& operator<<(std::ostream& os, const XmmRegister& reg);
+
 }  // namespace x86
 }  // namespace art
 
diff --git a/runtime/arch/x86_64/callee_save_frame_x86_64.h b/runtime/arch/x86_64/callee_save_frame_x86_64.h
new file mode 100644
index 0000000..228a902
--- /dev/null
+++ b/runtime/arch/x86_64/callee_save_frame_x86_64.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_ARCH_X86_64_CALLEE_SAVE_FRAME_X86_64_H_
+#define ART_RUNTIME_ARCH_X86_64_CALLEE_SAVE_FRAME_X86_64_H_
+
+#include "arch/instruction_set.h"
+#include "base/bit_utils.h"
+#include "base/callee_save_type.h"
+#include "base/enums.h"
+#include "base/globals.h"
+#include "quick/quick_method_frame_info.h"
+#include "registers_x86_64.h"
+
+namespace art {
+namespace x86_64 {
+
+static constexpr uint32_t kX86_64CalleeSaveAlwaysSpills =
+    (1 << art::x86_64::kNumberOfCpuRegisters);  // Fake return address callee save.
+static constexpr uint32_t kX86_64CalleeSaveRefSpills =
+    (1 << art::x86_64::RBX) | (1 << art::x86_64::RBP) | (1 << art::x86_64::R12) |
+    (1 << art::x86_64::R13) | (1 << art::x86_64::R14) | (1 << art::x86_64::R15);
+static constexpr uint32_t kX86_64CalleeSaveArgSpills =
+    (1 << art::x86_64::RSI) | (1 << art::x86_64::RDX) | (1 << art::x86_64::RCX) |
+    (1 << art::x86_64::R8) | (1 << art::x86_64::R9);
+static constexpr uint32_t kX86_64CalleeSaveEverythingSpills =
+    (1 << art::x86_64::RAX) | (1 << art::x86_64::RCX) | (1 << art::x86_64::RDX) |
+    (1 << art::x86_64::RSI) | (1 << art::x86_64::RDI) | (1 << art::x86_64::R8) |
+    (1 << art::x86_64::R9) | (1 << art::x86_64::R10) | (1 << art::x86_64::R11);
+
+static constexpr uint32_t kX86_64CalleeSaveFpArgSpills =
+    (1 << art::x86_64::XMM0) | (1 << art::x86_64::XMM1) | (1 << art::x86_64::XMM2) |
+    (1 << art::x86_64::XMM3) | (1 << art::x86_64::XMM4) | (1 << art::x86_64::XMM5) |
+    (1 << art::x86_64::XMM6) | (1 << art::x86_64::XMM7);
+static constexpr uint32_t kX86_64CalleeSaveFpSpills =
+    (1 << art::x86_64::XMM12) | (1 << art::x86_64::XMM13) |
+    (1 << art::x86_64::XMM14) | (1 << art::x86_64::XMM15);
+static constexpr uint32_t kX86_64CalleeSaveFpEverythingSpills =
+    (1 << art::x86_64::XMM0) | (1 << art::x86_64::XMM1) |
+    (1 << art::x86_64::XMM2) | (1 << art::x86_64::XMM3) |
+    (1 << art::x86_64::XMM4) | (1 << art::x86_64::XMM5) |
+    (1 << art::x86_64::XMM6) | (1 << art::x86_64::XMM7) |
+    (1 << art::x86_64::XMM8) | (1 << art::x86_64::XMM9) |
+    (1 << art::x86_64::XMM10) | (1 << art::x86_64::XMM11);
+
+class X86_64CalleeSaveFrame {
+ public:
+  static constexpr uint32_t GetCoreSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kX86_64CalleeSaveAlwaysSpills | kX86_64CalleeSaveRefSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveArgSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveEverythingSpills : 0);
+  }
+
+  static constexpr uint32_t GetFpSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kX86_64CalleeSaveFpSpills |
+        (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0) |
+        (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveFpEverythingSpills : 0);
+  }
+
+  static constexpr uint32_t GetFrameSize(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ +
+                    POPCOUNT(GetFpSpills(type)) /* fprs */ +
+                    1 /* Method* */) * static_cast<size_t>(kX86_64PointerSize), kStackAlignment);
+  }
+
+  static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type));
+  }
+
+  static constexpr size_t GetFpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           (POPCOUNT(GetCoreSpills(type)) +
+            POPCOUNT(GetFpSpills(type))) * static_cast<size_t>(kX86_64PointerSize);
+  }
+
+  static constexpr size_t GetGpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           POPCOUNT(GetCoreSpills(type)) * static_cast<size_t>(kX86_64PointerSize);
+  }
+
+  static constexpr size_t GetReturnPcOffset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) - static_cast<size_t>(kX86_64PointerSize);
+  }
+};
+
+}  // namespace x86_64
+}  // namespace art
+
+#endif  // ART_RUNTIME_ARCH_X86_64_CALLEE_SAVE_FRAME_X86_64_H_
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 4f941e1..c179033 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -951,12 +951,15 @@
     END_FUNCTION VAR(c_name)
 END_MACRO
 
-// Macro for string and type resolution and initialization.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings.
+     */
 MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
     DEFINE_FUNCTION VAR(c_name)
     SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset  // save everything for GC
     // Outgoing argument set up
-    movl %eax, %edi                               // pass string index
+    movl %eax, %edi                               // pass the index of the constant as arg0
     movq %gs:THREAD_SELF_OFFSET, %rsi             // pass Thread::Current()
     call CALLVAR(cxx_name)                        // cxx_name(arg0, Thread*)
     testl %eax, %eax                              // If result is null, deliver the OOME.
@@ -1298,6 +1301,8 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
 TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
@@ -1307,7 +1312,7 @@
     jz   .Lslow_lock
 .Lretry_lock:
     movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx  // ecx := lock word.
-    test LITERAL(LOCK_WORD_STATE_MASK), %ecx         // Test the 2 high bits.
+    test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx  // Test the 2 high bits.
     jne  .Lslow_lock                      // Slow path if either of the two high bits are set.
     movl %ecx, %edx                       // save lock word (edx) to keep read barrier bits.
     andl LITERAL(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED), %ecx  // zero the gc bits.
@@ -1357,7 +1362,7 @@
 .Lretry_unlock:
     movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx  // ecx := lock word
     movl %gs:THREAD_ID_OFFSET, %edx       // edx := thread id
-    test LITERAL(LOCK_WORD_STATE_MASK), %ecx
+    test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx
     jnz  .Lslow_unlock                    // lock word contains a monitor
     cmpw %cx, %dx                         // does the thread id match?
     jne  .Lslow_unlock
diff --git a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h b/runtime/arch/x86_64/quick_method_frame_info_x86_64.h
deleted file mode 100644
index ebf976e..0000000
--- a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h
+++ /dev/null
@@ -1,89 +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_RUNTIME_ARCH_X86_64_QUICK_METHOD_FRAME_INFO_X86_64_H_
-#define ART_RUNTIME_ARCH_X86_64_QUICK_METHOD_FRAME_INFO_X86_64_H_
-
-#include "arch/instruction_set.h"
-#include "base/bit_utils.h"
-#include "base/callee_save_type.h"
-#include "base/enums.h"
-#include "quick/quick_method_frame_info.h"
-#include "registers_x86_64.h"
-
-namespace art {
-namespace x86_64 {
-
-static constexpr uint32_t kX86_64CalleeSaveAlwaysSpills =
-    (1 << art::x86_64::kNumberOfCpuRegisters);  // Fake return address callee save.
-static constexpr uint32_t kX86_64CalleeSaveRefSpills =
-    (1 << art::x86_64::RBX) | (1 << art::x86_64::RBP) | (1 << art::x86_64::R12) |
-    (1 << art::x86_64::R13) | (1 << art::x86_64::R14) | (1 << art::x86_64::R15);
-static constexpr uint32_t kX86_64CalleeSaveArgSpills =
-    (1 << art::x86_64::RSI) | (1 << art::x86_64::RDX) | (1 << art::x86_64::RCX) |
-    (1 << art::x86_64::R8) | (1 << art::x86_64::R9);
-static constexpr uint32_t kX86_64CalleeSaveEverythingSpills =
-    (1 << art::x86_64::RAX) | (1 << art::x86_64::RCX) | (1 << art::x86_64::RDX) |
-    (1 << art::x86_64::RSI) | (1 << art::x86_64::RDI) | (1 << art::x86_64::R8) |
-    (1 << art::x86_64::R9) | (1 << art::x86_64::R10) | (1 << art::x86_64::R11);
-
-static constexpr uint32_t kX86_64CalleeSaveFpArgSpills =
-    (1 << art::x86_64::XMM0) | (1 << art::x86_64::XMM1) | (1 << art::x86_64::XMM2) |
-    (1 << art::x86_64::XMM3) | (1 << art::x86_64::XMM4) | (1 << art::x86_64::XMM5) |
-    (1 << art::x86_64::XMM6) | (1 << art::x86_64::XMM7);
-static constexpr uint32_t kX86_64CalleeSaveFpSpills =
-    (1 << art::x86_64::XMM12) | (1 << art::x86_64::XMM13) |
-    (1 << art::x86_64::XMM14) | (1 << art::x86_64::XMM15);
-static constexpr uint32_t kX86_64CalleeSaveFpEverythingSpills =
-    (1 << art::x86_64::XMM0) | (1 << art::x86_64::XMM1) |
-    (1 << art::x86_64::XMM2) | (1 << art::x86_64::XMM3) |
-    (1 << art::x86_64::XMM4) | (1 << art::x86_64::XMM5) |
-    (1 << art::x86_64::XMM6) | (1 << art::x86_64::XMM7) |
-    (1 << art::x86_64::XMM8) | (1 << art::x86_64::XMM9) |
-    (1 << art::x86_64::XMM10) | (1 << art::x86_64::XMM11);
-
-constexpr uint32_t X86_64CalleeSaveCoreSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kX86_64CalleeSaveAlwaysSpills | kX86_64CalleeSaveRefSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveArgSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveEverythingSpills : 0);
-}
-
-constexpr uint32_t X86_64CalleeSaveFpSpills(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return kX86_64CalleeSaveFpSpills |
-      (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0) |
-      (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveFpEverythingSpills : 0);
-}
-
-constexpr uint32_t X86_64CalleeSaveFrameSize(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return RoundUp((POPCOUNT(X86_64CalleeSaveCoreSpills(type)) /* gprs */ +
-                  POPCOUNT(X86_64CalleeSaveFpSpills(type)) /* fprs */ +
-                  1 /* Method* */) * static_cast<size_t>(kX86_64PointerSize), kStackAlignment);
-}
-
-constexpr QuickMethodFrameInfo X86_64CalleeSaveMethodFrameInfo(CalleeSaveType type) {
-  type = GetCanonicalCalleeSaveType(type);
-  return QuickMethodFrameInfo(X86_64CalleeSaveFrameSize(type),
-                              X86_64CalleeSaveCoreSpills(type),
-                              X86_64CalleeSaveFpSpills(type));
-}
-
-}  // namespace x86_64
-}  // namespace art
-
-#endif  // ART_RUNTIME_ARCH_X86_64_QUICK_METHOD_FRAME_INFO_X86_64_H_
diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h
index 4f22431..66aea70 100644
--- a/runtime/arch/x86_64/registers_x86_64.h
+++ b/runtime/arch/x86_64/registers_x86_64.h
@@ -21,8 +21,8 @@
 
 #include <android-base/logging.h>
 
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 namespace art {
 namespace x86_64 {
diff --git a/runtime/arch/x86_64/thread_x86_64.cc b/runtime/arch/x86_64/thread_x86_64.cc
index 19d25f6..5c0446f 100644
--- a/runtime/arch/x86_64/thread_x86_64.cc
+++ b/runtime/arch/x86_64/thread_x86_64.cc
@@ -25,6 +25,10 @@
 #include <asm/prctl.h>
 #include <sys/prctl.h>
 #include <sys/syscall.h>
+#elif defined(__Fuchsia__)
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
+#include <zircon/syscalls/object.h>
 #endif
 
 namespace art {
@@ -40,6 +44,13 @@
 
 #if defined(__linux__)
   arch_prctl(ARCH_SET_GS, this);
+#elif defined(__Fuchsia__)
+  Thread* thread_ptr = this;
+  zx_status_t status = zx_object_set_property(zx_thread_self(),
+                                              ZX_PROP_REGISTER_GS,
+                                              &thread_ptr,
+                                              sizeof(thread_ptr));
+  CHECK_EQ(status, ZX_OK) << "failed to set GS register";
 #else
   UNIMPLEMENTED(FATAL) << "Need to set GS";
 #endif
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 29d71af..f39af39 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -20,6 +20,7 @@
 #include <jni.h>
 
 #include "dex/dex_file_types.h"
+#include "dex/hidden_api_access_flags.h"
 #include "dex/modifiers.h"
 #include "dex/primitive.h"
 #include "gc_root.h"
@@ -179,6 +180,10 @@
     return (GetAccessFlags() & kAccVolatile) != 0;
   }
 
+  HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags());
+  }
+
   // Returns an instance field with this offset in the given class or null if not found.
   // If kExactOffset is true then we only find the matching offset, not the field containing the
   // offset.
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 1565644..c1fac36 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -384,19 +384,68 @@
   return (GetAccessFlags<kReadBarrierOption>() & kAccSingleImplementation) != 0;
 }
 
-inline bool ArtMethod::IsHiddenIntrinsic(uint32_t ordinal) {
-  switch (static_cast<Intrinsics>(ordinal)) {
-    case Intrinsics::kReferenceGetReferent:
-    case Intrinsics::kSystemArrayCopyChar:
-    case Intrinsics::kStringGetCharsNoCheck:
-    case Intrinsics::kVarHandleFullFence:
-    case Intrinsics::kVarHandleAcquireFence:
-    case Intrinsics::kVarHandleReleaseFence:
-    case Intrinsics::kVarHandleLoadLoadFence:
-    case Intrinsics::kVarHandleStoreStoreFence:
-      return true;
-    default:
-      return false;
+inline HiddenApiAccessFlags::ApiList ArtMethod::GetHiddenApiAccessFlags()
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (UNLIKELY(IsIntrinsic())) {
+    switch (static_cast<Intrinsics>(GetIntrinsic())) {
+      case Intrinsics::kSystemArrayCopyChar:
+      case Intrinsics::kStringGetCharsNoCheck:
+      case Intrinsics::kReferenceGetReferent:
+        // These intrinsics are on the light greylist and will fail a DCHECK in
+        // SetIntrinsic() if their flags change on the respective dex methods.
+        // Note that the DCHECK currently won't fail if the dex methods are
+        // whitelisted, e.g. in the core image (b/77733081). As a result, we
+        // might print warnings but we won't change the semantics.
+        return HiddenApiAccessFlags::kLightGreylist;
+      case Intrinsics::kVarHandleFullFence:
+      case Intrinsics::kVarHandleAcquireFence:
+      case Intrinsics::kVarHandleReleaseFence:
+      case Intrinsics::kVarHandleLoadLoadFence:
+      case Intrinsics::kVarHandleStoreStoreFence:
+      case Intrinsics::kVarHandleCompareAndExchange:
+      case Intrinsics::kVarHandleCompareAndExchangeAcquire:
+      case Intrinsics::kVarHandleCompareAndExchangeRelease:
+      case Intrinsics::kVarHandleCompareAndSet:
+      case Intrinsics::kVarHandleGet:
+      case Intrinsics::kVarHandleGetAcquire:
+      case Intrinsics::kVarHandleGetAndAdd:
+      case Intrinsics::kVarHandleGetAndAddAcquire:
+      case Intrinsics::kVarHandleGetAndAddRelease:
+      case Intrinsics::kVarHandleGetAndBitwiseAnd:
+      case Intrinsics::kVarHandleGetAndBitwiseAndAcquire:
+      case Intrinsics::kVarHandleGetAndBitwiseAndRelease:
+      case Intrinsics::kVarHandleGetAndBitwiseOr:
+      case Intrinsics::kVarHandleGetAndBitwiseOrAcquire:
+      case Intrinsics::kVarHandleGetAndBitwiseOrRelease:
+      case Intrinsics::kVarHandleGetAndBitwiseXor:
+      case Intrinsics::kVarHandleGetAndBitwiseXorAcquire:
+      case Intrinsics::kVarHandleGetAndBitwiseXorRelease:
+      case Intrinsics::kVarHandleGetAndSet:
+      case Intrinsics::kVarHandleGetAndSetAcquire:
+      case Intrinsics::kVarHandleGetAndSetRelease:
+      case Intrinsics::kVarHandleGetOpaque:
+      case Intrinsics::kVarHandleGetVolatile:
+      case Intrinsics::kVarHandleSet:
+      case Intrinsics::kVarHandleSetOpaque:
+      case Intrinsics::kVarHandleSetRelease:
+      case Intrinsics::kVarHandleSetVolatile:
+      case Intrinsics::kVarHandleWeakCompareAndSet:
+      case Intrinsics::kVarHandleWeakCompareAndSetAcquire:
+      case Intrinsics::kVarHandleWeakCompareAndSetPlain:
+      case Intrinsics::kVarHandleWeakCompareAndSetRelease:
+        // These intrinsics are on the blacklist and will fail a DCHECK in
+        // SetIntrinsic() if their flags change on the respective dex methods.
+        // Note that the DCHECK currently won't fail if the dex methods are
+        // whitelisted, e.g. in the core image (b/77733081). Given that they are
+        // exclusively VarHandle intrinsics, they should not be used outside
+        // tests that do not enable hidden API checks.
+        return HiddenApiAccessFlags::kBlacklist;
+      default:
+        // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic().
+        return HiddenApiAccessFlags::kWhitelist;
+    }
+  } else {
+    return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags());
   }
 }
 
@@ -422,7 +471,7 @@
     bool is_default_conflict = IsDefaultConflicting();
     bool is_compilable = IsCompilable();
     bool must_count_locks = MustCountLocks();
-    HiddenApiAccessFlags::ApiList hidden_api_list = GetHiddenApiAccessFlags();
+    HiddenApiAccessFlags::ApiList hidden_api_flags = GetHiddenApiAccessFlags();
     SetAccessFlags(new_value);
     DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask));
     DCHECK_EQ(is_constructor, IsConstructor());
@@ -436,14 +485,14 @@
     DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
     DCHECK_EQ(is_compilable, IsCompilable());
     DCHECK_EQ(must_count_locks, MustCountLocks());
-    if (kIsDebugBuild) {
-      if (IsHiddenIntrinsic(intrinsic)) {
-        // Special case some of our intrinsics because the access flags clash
-        // with the intrinsics ordinal.
-        DCHECK_EQ(HiddenApiAccessFlags::kWhitelist, GetHiddenApiAccessFlags());
-      } else {
-        DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags());
-      }
+    // Only DCHECK that we have preserved the hidden API access flags if the
+    // original method was not on the whitelist. This is because the core image
+    // does not have the access flags set (b/77733081). It is fine to hard-code
+    // these because (a) warnings on greylist do not change semantics, and
+    // (b) only VarHandle intrinsics are blacklisted at the moment and they
+    // should not be used outside tests with disabled API checks.
+    if (hidden_api_flags != HiddenApiAccessFlags::kWhitelist) {
+      DCHECK_EQ(hidden_api_flags, GetHiddenApiAccessFlags());
     }
   } else {
     SetAccessFlags(new_value);
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 41b01c2..608e33c 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -35,7 +35,7 @@
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
 #include "jit/profiling_info.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_ext.h"
 #include "mirror/executable.h"
@@ -710,6 +710,23 @@
   return GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize()) != nullptr;
 }
 
+void ArtMethod::SetNotIntrinsic() {
+  if (!IsIntrinsic()) {
+    return;
+  }
+
+  // Query the hidden API access flags of the intrinsic.
+  HiddenApiAccessFlags::ApiList intrinsic_api_list = GetHiddenApiAccessFlags();
+
+  // Clear intrinsic-related access flags.
+  ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits);
+
+  // Re-apply hidden API access flags now that the method is not an intrinsic.
+  SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(GetAccessFlags(), intrinsic_api_list));
+  DCHECK_EQ(GetHiddenApiAccessFlags(), intrinsic_api_list);
+}
+
+
 void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) {
   memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
          Size(image_pointer_size));
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 64d2932..012d706 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -194,9 +194,7 @@
     return (GetAccessFlags() & kAccIntrinsicBits) >> kAccFlagsShift;
   }
 
-  void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_) {
-    ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits);
-  }
+  void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsCopied() {
     static_assert((kAccCopied & (kAccIntrinsic | kAccIntrinsicBits)) == 0,
@@ -341,9 +339,7 @@
     AddAccessFlags(kAccMustCountLocks);
   }
 
-  HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() {
-    return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags());
-  }
+  HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true if this method could be overridden by a default method.
   bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -873,9 +869,6 @@
     } while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags));
   }
 
-  // Returns true if the given intrinsic is considered hidden.
-  bool IsHiddenIntrinsic(uint32_t ordinal);
-
   DISALLOW_COPY_AND_ASSIGN(ArtMethod);  // Need to use CopyFrom to deal with 32 vs 64 bits.
 };
 
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 2f7d6ab..70ff40d 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -73,7 +73,7 @@
 
 // Offset of field Thread::tlsPtr_.mterp_current_ibase.
 #define THREAD_CURRENT_IBASE_OFFSET \
-    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 162) * __SIZEOF_POINTER__)
+    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
             art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.mterp_default_ibase.
diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc
index 9ac7886..702f0e4 100644
--- a/runtime/base/mem_map_arena_pool.cc
+++ b/runtime/base/mem_map_arena_pool.cc
@@ -125,7 +125,7 @@
 }
 
 void MemMapArenaPool::FreeArenaChain(Arena* first) {
-  if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+  if (kRunningOnMemoryTool) {
     for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
       MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
     }
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 73b4641..da286d7 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -195,8 +195,8 @@
 };
 
 BaseMutex::BaseMutex(const char* name, LockLevel level)
-    : level_(level),
-      name_(name),
+    : name_(name),
+      level_(level),
       should_respond_to_empty_checkpoint_request_(false) {
   if (kLogLockContentions) {
     ScopedAllMutexesLock mu(this);
@@ -386,7 +386,7 @@
 
 
 Mutex::Mutex(const char* name, LockLevel level, bool recursive)
-    : BaseMutex(name, level), exclusive_owner_(0), recursive_(recursive), recursion_count_(0) {
+    : BaseMutex(name, level), exclusive_owner_(0), recursion_count_(0), recursive_(recursive) {
 #if ART_USE_FUTEXES
   DCHECK_EQ(0, state_.load(std::memory_order_relaxed));
   DCHECK_EQ(0, num_contenders_.load(std::memory_order_relaxed));
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 1cf4ddd..602d183 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -57,7 +57,7 @@
 // partial ordering and thereby cause deadlock situations to fail checks.
 //
 // [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163
-enum LockLevel {
+enum LockLevel : uint8_t {
   kLoggingLock = 0,
   kSwapMutexesLock,
   kUnexpectedSignalLock,
@@ -142,20 +142,20 @@
 };
 std::ostream& operator<<(std::ostream& os, const LockLevel& rhs);
 
-const bool kDebugLocking = kIsDebugBuild;
+constexpr bool kDebugLocking = kIsDebugBuild;
 
 // Record Log contention information, dumpable via SIGQUIT.
 #ifdef ART_USE_FUTEXES
 // To enable lock contention logging, set this to true.
-const bool kLogLockContentions = false;
+constexpr bool kLogLockContentions = false;
 #else
 // Keep this false as lock contention logging is supported only with
 // futex.
-const bool kLogLockContentions = false;
+constexpr bool kLogLockContentions = false;
 #endif
-const size_t kContentionLogSize = 4;
-const size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0;
-const size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0;
+constexpr size_t kContentionLogSize = 4;
+constexpr size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0;
+constexpr size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0;
 
 // Base class for all Mutex implementations
 class BaseMutex {
@@ -196,9 +196,7 @@
   void RecordContention(uint64_t blocked_tid, uint64_t owner_tid, uint64_t nano_time_blocked);
   void DumpContention(std::ostream& os) const;
 
-  const LockLevel level_;  // Support for lock hierarchy.
   const char* const name_;
-  bool should_respond_to_empty_checkpoint_request_;
 
   // A log entry that records contention but makes no guarantee that either tid will be held live.
   struct ContentionLogEntry {
@@ -221,6 +219,9 @@
   };
   ContentionLogData contention_log_data_[kContentionLogDataSize];
 
+  const LockLevel level_;  // Support for lock hierarchy.
+  bool should_respond_to_empty_checkpoint_request_;
+
  public:
   bool HasEverContended() const {
     if (kLogLockContentions) {
@@ -307,8 +308,10 @@
   pthread_mutex_t mutex_;
   Atomic<pid_t> exclusive_owner_;  // Guarded by mutex_. Asynchronous reads are OK.
 #endif
-  const bool recursive_;  // Can the lock be recursively held?
+
   unsigned int recursion_count_;
+  const bool recursive_;  // Can the lock be recursively held?
+
   friend class ConditionVariable;
   DISALLOW_COPY_AND_ASSIGN(Mutex);
 };
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 9a43790..f8b977e 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -34,8 +34,8 @@
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
 #include "gc/space/space.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/field.h"
 #include "mirror/method.h"
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index e2ad7fd..6917899 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -64,20 +64,19 @@
   void CheckOptimizedMethod(int* registers, int number_of_references, uint32_t native_pc_offset)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     ArtMethod* m = GetMethod();
-    CodeInfo code_info = GetCurrentOatQuickMethodHeader()->GetOptimizedCodeInfo();
-    CodeInfoEncoding encoding = code_info.ExtractEncoding();
-    StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+    CodeInfo code_info(GetCurrentOatQuickMethodHeader());
+    StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
     CodeItemDataAccessor accessor(m->DexInstructionData());
     uint16_t number_of_dex_registers = accessor.RegistersSize();
     DexRegisterMap dex_register_map =
-        code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
-    uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map);
-    BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map);
+        code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+    uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map);
+    BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map);
     for (int i = 0; i < number_of_references; ++i) {
       int reg = registers[i];
       CHECK_LT(reg, accessor.RegistersSize());
       DexRegisterLocation location = dex_register_map.GetDexRegisterLocation(
-          reg, number_of_dex_registers, code_info, encoding);
+          reg, number_of_dex_registers, code_info);
       switch (location.GetKind()) {
         case DexRegisterLocation::Kind::kNone:
           // Not set, should not be a reference.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 3f33f79..b88aa5e 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -79,12 +79,11 @@
 #include "imtable-inl.h"
 #include "intern_table.h"
 #include "interpreter/interpreter.h"
-#include "java_vm_ext.h"
 #include "jit/debugger_interface.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
-#include "jit/profile_compilation_info.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "linear_alloc.h"
 #include "mirror/call_site.h"
 #include "mirror/class-inl.h"
@@ -116,6 +115,7 @@
 #include "oat_file_assistant.h"
 #include "oat_file_manager.h"
 #include "object_lock.h"
+#include "profile/profile_compilation_info.h"
 #include "runtime.h"
 #include "runtime_callbacks.h"
 #include "scoped_thread_state_change-inl.h"
@@ -828,9 +828,24 @@
   return true;
 }
 
+static void CreateStringInitBindings(Thread* self, ClassLinker* class_linker)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Find String.<init> -> StringFactory bindings.
+  ObjPtr<mirror::Class> string_factory_class =
+      class_linker->FindSystemClass(self, "Ljava/lang/StringFactory;");
+  CHECK(string_factory_class != nullptr);
+  ObjPtr<mirror::Class> string_class =
+      class_linker->GetClassRoot(ClassLinker::ClassRoot::kJavaLangString);
+  WellKnownClasses::InitStringInit(string_class, string_factory_class);
+  // Update the primordial thread.
+  self->InitStringEntryPoints();
+}
+
 void ClassLinker::FinishInit(Thread* self) {
   VLOG(startup) << "ClassLinker::FinishInit entering";
 
+  CreateStringInitBindings(self, this);
+
   // Let the heap know some key offsets into java.lang.ref instances
   // Note: we hard code the field indexes here rather than using FindInstanceField
   // as the types of the field can't be resolved prior to the runtime being
@@ -4850,6 +4865,9 @@
       const uint32_t field_idx = field->GetDexFieldIndex();
       ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
       if (resolved_field == nullptr) {
+        // Populating cache of a dex file which defines `klass` should always be allowed.
+        DCHECK_EQ(hiddenapi::GetMemberAction(
+            field, class_loader.Get(), dex_cache.Get(), hiddenapi::kNone), hiddenapi::kAllow);
         dex_cache->SetResolvedField(field_idx, field, image_pointer_size_);
       } else {
         DCHECK_EQ(field, resolved_field);
@@ -7884,6 +7902,40 @@
   return resolved;
 }
 
+// Returns true if `method` is either null or hidden.
+// Does not print any warnings if it is hidden.
+static bool CheckNoSuchMethod(ArtMethod* method,
+                              ObjPtr<mirror::DexCache> dex_cache,
+                              ObjPtr<mirror::ClassLoader> class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+  return method == nullptr ||
+         hiddenapi::GetMemberAction(method,
+                                    class_loader,
+                                    dex_cache,
+                                    hiddenapi::kNone)  // do not print warnings
+             == hiddenapi::kDeny;
+}
+
+ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
+                                               ObjPtr<mirror::DexCache> dex_cache,
+                                               ObjPtr<mirror::ClassLoader> class_loader,
+                                               uint32_t method_idx) {
+  if (klass->IsInterface()) {
+    ArtMethod* method = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_);
+    return CheckNoSuchMethod(method, dex_cache, class_loader) ? nullptr : method;
+  } else {
+    // If there was an interface method with the same signature, we would have
+    // found it in the "copied" methods. Only DCHECK that the interface method
+    // really does not exist.
+    if (kIsDebugBuild) {
+      ArtMethod* method =
+          klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_);
+      DCHECK(CheckNoSuchMethod(method, dex_cache, class_loader));
+    }
+    return nullptr;
+  }
+}
+
 template <ClassLinker::ResolveMode kResolveMode>
 ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
                                       Handle<mirror::DexCache> dex_cache,
@@ -7959,13 +8011,7 @@
     // If we had a method, or if we can find one with another lookup type,
     // it's an incompatible-class-change error.
     if (resolved == nullptr) {
-      if (klass->IsInterface()) {
-        resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size);
-      } else {
-        // If there was an interface method with the same signature,
-        // we would have found it also in the "copied" methods.
-        DCHECK(klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size) == nullptr);
-      }
+      resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
     }
     if (resolved != nullptr) {
       ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
@@ -8025,26 +8071,8 @@
     return nullptr;
   }
   DCHECK(klass->IsResolved());
-  Thread* self = is_static ? Thread::Current() : nullptr;
 
-  // First try to find a field declared directly by `klass` by the field index.
-  ArtField* resolved_field = is_static
-      ? mirror::Class::FindStaticField(self, klass, dex_cache, field_idx)
-      : klass->FindInstanceField(dex_cache, field_idx);
-
-  if (resolved_field == nullptr) {
-    // If not found in `klass` by field index, search the class hierarchy using the name and type.
-    const char* name = dex_file.GetFieldName(field_id);
-    const char* type = dex_file.GetFieldTypeDescriptor(field_id);
-    resolved_field = is_static
-        ? mirror::Class::FindStaticField(self, klass, name, type)
-        : klass->FindInstanceField(name, type);
-  }
-
-  if (resolved_field != nullptr) {
-    dex_cache->SetResolvedField(field_idx, resolved_field, image_pointer_size_);
-  }
-  return resolved_field;
+  return FindResolvedField(klass, dex_cache, class_loader, field_idx, is_static);
 }
 
 ArtField* ClassLinker::ResolveField(uint32_t field_idx,
@@ -8059,39 +8087,18 @@
   }
   const DexFile& dex_file = *dex_cache->GetDexFile();
   const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
-  Thread* const self = Thread::Current();
   ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader);
   if (klass == nullptr) {
     DCHECK(Thread::Current()->IsExceptionPending());
     return nullptr;
   }
 
-  if (is_static) {
-    resolved = mirror::Class::FindStaticField(self, klass, dex_cache.Get(), field_idx);
-  } else {
-    resolved = klass->FindInstanceField(dex_cache.Get(), field_idx);
-  }
-
+  resolved = FindResolvedField(klass, dex_cache.Get(), class_loader.Get(), field_idx, is_static);
   if (resolved == nullptr) {
     const char* name = dex_file.GetFieldName(field_id);
     const char* type = dex_file.GetFieldTypeDescriptor(field_id);
-    if (is_static) {
-      resolved = mirror::Class::FindStaticField(self, klass, name, type);
-    } else {
-      resolved = klass->FindInstanceField(name, type);
-    }
-  }
-
-  if (resolved == nullptr ||
-      hiddenapi::GetMemberAction(
-          resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
-    const char* name = dex_file.GetFieldName(field_id);
-    const char* type = dex_file.GetFieldTypeDescriptor(field_id);
     ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name);
-    return nullptr;
   }
-
-  dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
   return resolved;
 }
 
@@ -8106,32 +8113,83 @@
   }
   const DexFile& dex_file = *dex_cache->GetDexFile();
   const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
-  Thread* self = Thread::Current();
   ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader);
   if (klass == nullptr) {
     DCHECK(Thread::Current()->IsExceptionPending());
     return nullptr;
   }
 
-  StringPiece name(dex_file.GetFieldName(field_id));
-  StringPiece type(dex_file.GetFieldTypeDescriptor(field_id));
-  resolved = mirror::Class::FindField(self, klass, name, type);
-  if (resolved != nullptr &&
-      hiddenapi::GetMemberAction(
-          resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
-    resolved = nullptr;
-  }
-  if (resolved != nullptr) {
-    dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
-  } else {
+  resolved = FindResolvedFieldJLS(klass, dex_cache.Get(), class_loader.Get(), field_idx);
+  if (resolved == nullptr) {
+    const char* name = dex_file.GetFieldName(field_id);
+    const char* type = dex_file.GetFieldTypeDescriptor(field_id);
     ThrowNoSuchFieldError("", klass, type, name);
   }
   return resolved;
 }
 
+ArtField* ClassLinker::FindResolvedField(ObjPtr<mirror::Class> klass,
+                                         ObjPtr<mirror::DexCache> dex_cache,
+                                         ObjPtr<mirror::ClassLoader> class_loader,
+                                         uint32_t field_idx,
+                                         bool is_static) {
+  ArtField* resolved = nullptr;
+  Thread* self = is_static ? Thread::Current() : nullptr;
+  const DexFile& dex_file = *dex_cache->GetDexFile();
+
+  resolved = is_static ? mirror::Class::FindStaticField(self, klass, dex_cache, field_idx)
+                       : klass->FindInstanceField(dex_cache, field_idx);
+
+  if (resolved == nullptr) {
+    const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
+    const char* name = dex_file.GetFieldName(field_id);
+    const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+    resolved = is_static ? mirror::Class::FindStaticField(self, klass, name, type)
+                         : klass->FindInstanceField(name, type);
+  }
+
+  if (resolved != nullptr &&
+      hiddenapi::GetMemberAction(
+          resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+    resolved = nullptr;
+  }
+
+  if (resolved != nullptr) {
+    dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
+  }
+
+  return resolved;
+}
+
+ArtField* ClassLinker::FindResolvedFieldJLS(ObjPtr<mirror::Class> klass,
+                                            ObjPtr<mirror::DexCache> dex_cache,
+                                            ObjPtr<mirror::ClassLoader> class_loader,
+                                            uint32_t field_idx) {
+  ArtField* resolved = nullptr;
+  Thread* self = Thread::Current();
+  const DexFile& dex_file = *dex_cache->GetDexFile();
+  const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
+
+  const char* name = dex_file.GetFieldName(field_id);
+  const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+  resolved = mirror::Class::FindField(self, klass, name, type);
+
+  if (resolved != nullptr &&
+      hiddenapi::GetMemberAction(
+          resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+    resolved = nullptr;
+  }
+
+  if (resolved != nullptr) {
+    dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
+  }
+
+  return resolved;
+}
+
 ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType(
     Thread* self,
-    uint32_t proto_idx,
+    dex::ProtoIndex proto_idx,
     Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader) {
   DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
@@ -8193,7 +8251,7 @@
 }
 
 ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType(Thread* self,
-                                                          uint32_t proto_idx,
+                                                          dex::ProtoIndex proto_idx,
                                                           ArtMethod* referrer) {
   StackHandleScope<2> hs(self);
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index fa70f65..52ecf82 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -329,6 +329,15 @@
                                 uint32_t method_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Find a method using the wrong lookup mechanism. If `klass` is an interface,
+  // search for a class method. If it is a class, search for an interface method.
+  // This is useful when throwing IncompatibleClassChangeError.
+  ArtMethod* FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
+                                    ObjPtr<mirror::DexCache> dex_cache,
+                                    ObjPtr<mirror::ClassLoader> class_loader,
+                                    uint32_t method_idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Resolve a method with a given ID from the DexFile associated with the given DexCache
   // and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader are
   // used as in ResolveType. What is unique is the method type argument which is used to
@@ -383,17 +392,38 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
+  // Find a field with a given ID from the DexFile associated with the given DexCache
+  // and ClassLoader, storing the result in DexCache. The declaring class is assumed
+  // to have been already resolved into `klass`. The `is_static` argument is used to
+  // determine if we are resolving a static or non-static field.
+  ArtField* FindResolvedField(ObjPtr<mirror::Class> klass,
+                              ObjPtr<mirror::DexCache> dex_cache,
+                              ObjPtr<mirror::ClassLoader> class_loader,
+                              uint32_t field_idx,
+                              bool is_static)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Find a field with a given ID from the DexFile associated with the given DexCache
+  // and ClassLoader, storing the result in DexCache. The declaring class is assumed
+  // to have been already resolved into `klass`. No is_static argument is provided
+  // so that Java field resolution semantics are followed.
+  ArtField* FindResolvedFieldJLS(ObjPtr<mirror::Class> klass,
+                                 ObjPtr<mirror::DexCache> dex_cache,
+                                 ObjPtr<mirror::ClassLoader> class_loader,
+                                 uint32_t field_idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Resolve a method type with a given ID from the DexFile associated with a given DexCache
   // and ClassLoader, storing the result in the DexCache.
   ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self,
-                                               uint32_t proto_idx,
+                                               dex::ProtoIndex proto_idx,
                                                Handle<mirror::DexCache> dex_cache,
                                                Handle<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
   ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self,
-                                               uint32_t proto_idx,
+                                               dex::ProtoIndex proto_idx,
                                                ArtMethod* referrer)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 4afc44c..2bd5411 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -25,7 +25,7 @@
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
 #include "handle_scope-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "oat_file_assistant.h"
 #include "obj_ptr-inl.h"
 #include "runtime.h"
@@ -672,9 +672,10 @@
   return !location.empty() && location[0] == '/';
 }
 
-bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec,
-                                                       bool verify_names,
-                                                       bool verify_checksums) const {
+ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch(
+    const std::string& context_spec,
+    bool verify_names,
+    bool verify_checksums) const {
   if (verify_names || verify_checksums) {
     DCHECK(dex_files_open_attempted_);
     DCHECK(dex_files_open_result_);
@@ -683,15 +684,21 @@
   ClassLoaderContext expected_context;
   if (!expected_context.Parse(context_spec, verify_checksums)) {
     LOG(WARNING) << "Invalid class loader context: " << context_spec;
-    return false;
+    return VerificationResult::kMismatch;
   }
 
   // Special shared library contexts always match. They essentially instruct the runtime
   // to ignore the class path check because the oat file is known to be loaded in different
   // contexts. OatFileManager will further verify if the oat file can be loaded based on the
   // collision check.
-  if (special_shared_library_ || expected_context.special_shared_library_) {
-    return true;
+  if (expected_context.special_shared_library_) {
+    // Special case where we are the only entry in the class path.
+    if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) {
+      return VerificationResult::kVerifies;
+    }
+    return VerificationResult::kForcedToSkipChecks;
+  } else if (special_shared_library_) {
+    return VerificationResult::kForcedToSkipChecks;
   }
 
   if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) {
@@ -699,7 +706,7 @@
         << expected_context.class_loader_chain_.size()
         << ", actual=" << class_loader_chain_.size()
         << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-    return false;
+    return VerificationResult::kMismatch;
   }
 
   for (size_t i = 0; i < class_loader_chain_.size(); i++) {
@@ -710,14 +717,14 @@
           << ". expected=" << GetClassLoaderTypeName(expected_info.type)
           << ", found=" << GetClassLoaderTypeName(info.type)
           << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-      return false;
+      return VerificationResult::kMismatch;
     }
     if (info.classpath.size() != expected_info.classpath.size()) {
       LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i
             << ". expected=" << expected_info.classpath.size()
             << ", found=" << info.classpath.size()
             << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-      return false;
+      return VerificationResult::kMismatch;
     }
 
     if (verify_checksums) {
@@ -772,7 +779,7 @@
             << ". expected=" << expected_info.classpath[k]
             << ", found=" << info.classpath[k]
             << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-        return false;
+        return VerificationResult::kMismatch;
       }
 
       // Compare the checksums.
@@ -781,11 +788,11 @@
                      << ". expected=" << expected_info.checksums[k]
                      << ", found=" << info.checksums[k]
                      << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-        return false;
+        return VerificationResult::kMismatch;
       }
     }
   }
-  return true;
+  return VerificationResult::kVerifies;
 }
 
 jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) {
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 1c83007..a4268aa 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -22,8 +22,10 @@
 
 #include "arch/instruction_set.h"
 #include "base/dchecked_vector.h"
+#include "dex/dex_file.h"
 #include "handle_scope.h"
 #include "mirror/class_loader.h"
+#include "oat_file.h"
 #include "scoped_thread_state_change.h"
 
 namespace art {
@@ -34,6 +36,18 @@
 // Utility class which holds the class loader context used during compilation/verification.
 class ClassLoaderContext {
  public:
+  enum class VerificationResult {
+    kVerifies,
+    kForcedToSkipChecks,
+    kMismatch,
+  };
+
+  enum ClassLoaderType {
+    kInvalidClassLoader = 0,
+    kPathClassLoader = 1,
+    kDelegateLastClassLoader = 2
+  };
+
   ~ClassLoaderContext();
 
   // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
@@ -109,7 +123,7 @@
   // This should be called after OpenDexFiles().
   // Names are only verified if verify_names is true.
   // Checksums are only verified if verify_checksums is true.
-  bool VerifyClassLoaderContextMatch(const std::string& context_spec,
+  VerificationResult VerifyClassLoaderContextMatch(const std::string& context_spec,
                                      bool verify_names = true,
                                      bool verify_checksums = true) const;
 
@@ -141,12 +155,6 @@
   static std::unique_ptr<ClassLoaderContext> Default();
 
  private:
-  enum ClassLoaderType {
-    kInvalidClassLoader = 0,
-    kPathClassLoader = 1,
-    kDelegateLastClassLoader = 2
-  };
-
   struct ClassLoaderInfo {
     // The type of this class loader.
     ClassLoaderType type;
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 4689ae4..5e3f48c 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -608,6 +608,17 @@
   VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA");
 }
 
+
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) {
+  std::string context_spec = "PCL[]";
+  std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+  ASSERT_TRUE(context != nullptr);
+  PretendContextOpenedDexFiles(context.get());
+  // Ensure that the special shared library marks as verified for the first thing in the class path.
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary),
+            ClassLoaderContext::VerificationResult::kVerifies);
+}
+
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) {
   std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]";
   std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
@@ -619,28 +630,36 @@
   VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
   VerifyClassLoaderDLC(context.get(), 1, "c.dex");
 
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
+            ClassLoaderContext::VerificationResult::kVerifies);
 
   std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC[";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec),
+            ClassLoaderContext::VerificationResult::kMismatch);
 }
 
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) {
@@ -652,7 +671,8 @@
   std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
 
   std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir),
+            ClassLoaderContext::VerificationResult::kVerifies);
 
   std::string dex_location = GetTestDexFileName("ForClassLoaderA");
   size_t pos = dex_location.rfind('/');
@@ -661,7 +681,8 @@
 
   std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
   ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir),
+            ClassLoaderContext::VerificationResult::kVerifies);
 }
 
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) {
@@ -669,7 +690,8 @@
 
   std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader);
 
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")),
+            ClassLoaderContext::VerificationResult::kVerifies);
 }
 
 }  // namespace art
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index 1439f11..af42878 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -20,7 +20,7 @@
 #include "art_field-inl.h"
 #include "base/mutex.h"
 #include "handle_scope.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class_loader.h"
 #include "native/dalvik_system_DexFile.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index 37e074d..9c2a40b 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -29,6 +29,7 @@
 #include "instrumentation.h"
 #include "interpreter/shadow_frame.h"
 #include "interpreter/unstarted_runtime.h"
+#include "jvalue-inl.h"
 #include "mirror/class.h"
 #include "mirror/object.h"
 #include "obj_ptr-inl.h"
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index f6a5efc..75b091d 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -48,8 +48,8 @@
 #include "gtest/gtest.h"
 #include "handle_scope-inl.h"
 #include "interpreter/unstarted_runtime.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "native/dalvik_system_DexFile.h"
@@ -67,6 +67,7 @@
 
   art::Locks::Init();
   art::InitLogging(argv, art::Runtime::Abort);
+  art::MemMap::Init();
   LOG(INFO) << "Running main() from common_runtime_test.cc...";
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
@@ -76,69 +77,6 @@
 
 using android::base::StringPrintf;
 
-ScratchFile::ScratchFile() {
-  // ANDROID_DATA needs to be set
-  CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) <<
-      "Are you subclassing RuntimeTest?";
-  filename_ = getenv("ANDROID_DATA");
-  filename_ += "/TmpFile-XXXXXX";
-  int fd = mkstemp(&filename_[0]);
-  CHECK_NE(-1, fd) << strerror(errno) << " for " << filename_;
-  file_.reset(new File(fd, GetFilename(), true));
-}
-
-ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix)
-    : ScratchFile(other.GetFilename() + suffix) {}
-
-ScratchFile::ScratchFile(const std::string& filename) : filename_(filename) {
-  int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666);
-  CHECK_NE(-1, fd);
-  file_.reset(new File(fd, GetFilename(), true));
-}
-
-ScratchFile::ScratchFile(File* file) {
-  CHECK(file != nullptr);
-  filename_ = file->GetPath();
-  file_.reset(file);
-}
-
-ScratchFile::ScratchFile(ScratchFile&& other) {
-  *this = std::move(other);
-}
-
-ScratchFile& ScratchFile::operator=(ScratchFile&& other) {
-  if (GetFile() != other.GetFile()) {
-    std::swap(filename_, other.filename_);
-    std::swap(file_, other.file_);
-  }
-  return *this;
-}
-
-ScratchFile::~ScratchFile() {
-  Unlink();
-}
-
-int ScratchFile::GetFd() const {
-  return file_->Fd();
-}
-
-void ScratchFile::Close() {
-  if (file_.get() != nullptr) {
-    if (file_->FlushCloseOrErase() != 0) {
-      PLOG(WARNING) << "Error closing scratch file.";
-    }
-  }
-}
-
-void ScratchFile::Unlink() {
-  if (!OS::FileExists(filename_.c_str())) {
-    return;
-  }
-  Close();
-  int unlink_result = unlink(filename_.c_str());
-  CHECK_EQ(0, unlink_result);
-}
-
 static bool unstarted_initialized_ = false;
 
 CommonRuntimeTestImpl::CommonRuntimeTestImpl()
@@ -151,124 +89,6 @@
   runtime_.reset();
 }
 
-void CommonRuntimeTestImpl::SetUpAndroidRoot() {
-  if (IsHost()) {
-    // $ANDROID_ROOT is set on the device, but not necessarily on the host.
-    // But it needs to be set so that icu4c can find its locale data.
-    const char* android_root_from_env = getenv("ANDROID_ROOT");
-    if (android_root_from_env == nullptr) {
-      // Use ANDROID_HOST_OUT for ANDROID_ROOT if it is set.
-      const char* android_host_out = getenv("ANDROID_HOST_OUT");
-      if (android_host_out != nullptr) {
-        setenv("ANDROID_ROOT", android_host_out, 1);
-      } else {
-        // Build it from ANDROID_BUILD_TOP or cwd
-        std::string root;
-        const char* android_build_top = getenv("ANDROID_BUILD_TOP");
-        if (android_build_top != nullptr) {
-          root += android_build_top;
-        } else {
-          // Not set by build server, so default to current directory
-          char* cwd = getcwd(nullptr, 0);
-          setenv("ANDROID_BUILD_TOP", cwd, 1);
-          root += cwd;
-          free(cwd);
-        }
-#if defined(__linux__)
-        root += "/out/host/linux-x86";
-#elif defined(__APPLE__)
-        root += "/out/host/darwin-x86";
-#else
-#error unsupported OS
-#endif
-        setenv("ANDROID_ROOT", root.c_str(), 1);
-      }
-    }
-    setenv("LD_LIBRARY_PATH", ":", 0);  // Required by java.lang.System.<clinit>.
-
-    // Not set by build server, so default
-    if (getenv("ANDROID_HOST_OUT") == nullptr) {
-      setenv("ANDROID_HOST_OUT", getenv("ANDROID_ROOT"), 1);
-    }
-  }
-}
-
-void CommonRuntimeTestImpl::SetUpAndroidData(std::string& android_data) {
-  // On target, Cannot use /mnt/sdcard because it is mounted noexec, so use subdir of dalvik-cache
-  if (IsHost()) {
-    const char* tmpdir = getenv("TMPDIR");
-    if (tmpdir != nullptr && tmpdir[0] != 0) {
-      android_data = tmpdir;
-    } else {
-      android_data = "/tmp";
-    }
-  } else {
-    android_data = "/data/dalvik-cache";
-  }
-  android_data += "/art-data-XXXXXX";
-  if (mkdtemp(&android_data[0]) == nullptr) {
-    PLOG(FATAL) << "mkdtemp(\"" << &android_data[0] << "\") failed";
-  }
-  setenv("ANDROID_DATA", android_data.c_str(), 1);
-}
-
-void CommonRuntimeTestImpl::TearDownAndroidData(const std::string& android_data,
-                                                bool fail_on_error) {
-  if (fail_on_error) {
-    ASSERT_EQ(rmdir(android_data.c_str()), 0);
-  } else {
-    rmdir(android_data.c_str());
-  }
-}
-
-// Helper - find directory with the following format:
-// ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/
-static std::string GetAndroidToolsDir(const std::string& subdir1,
-                                      const std::string& subdir2,
-                                      const std::string& subdir3) {
-  std::string root;
-  const char* android_build_top = getenv("ANDROID_BUILD_TOP");
-  if (android_build_top != nullptr) {
-    root = android_build_top;
-  } else {
-    // Not set by build server, so default to current directory
-    char* cwd = getcwd(nullptr, 0);
-    setenv("ANDROID_BUILD_TOP", cwd, 1);
-    root = cwd;
-    free(cwd);
-  }
-
-  std::string toolsdir = root + "/" + subdir1;
-  std::string founddir;
-  DIR* dir;
-  if ((dir = opendir(toolsdir.c_str())) != nullptr) {
-    float maxversion = 0;
-    struct dirent* entry;
-    while ((entry = readdir(dir)) != nullptr) {
-      std::string format = subdir2 + "-%f";
-      float version;
-      if (std::sscanf(entry->d_name, format.c_str(), &version) == 1) {
-        if (version > maxversion) {
-          maxversion = version;
-          founddir = toolsdir + "/" + entry->d_name + "/" + subdir3 + "/bin/";
-        }
-      }
-    }
-    closedir(dir);
-  }
-
-  if (founddir.empty()) {
-    ADD_FAILURE() << "Cannot find Android tools directory.";
-  }
-  return founddir;
-}
-
-std::string CommonRuntimeTestImpl::GetAndroidHostToolsDir() {
-  return GetAndroidToolsDir("prebuilts/gcc/linux-x86/host",
-                            "x86_64-linux-glibc2.15",
-                            "x86_64-linux");
-}
-
 std::string CommonRuntimeTestImpl::GetAndroidTargetToolsDir(InstructionSet isa) {
   switch (isa) {
     case InstructionSet::kArm:
@@ -297,38 +117,8 @@
   return "";
 }
 
-std::string CommonRuntimeTestImpl::GetCoreArtLocation() {
-  return GetCoreFileLocation("art");
-}
-
-std::string CommonRuntimeTestImpl::GetCoreOatLocation() {
-  return GetCoreFileLocation("oat");
-}
-
-std::unique_ptr<const DexFile> CommonRuntimeTestImpl::LoadExpectSingleDexFile(
-    const char* location) {
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  std::string error_msg;
-  MemMap::Init();
-  static constexpr bool kVerifyChecksum = true;
-  const ArtDexFileLoader dex_file_loader;
-  if (!dex_file_loader.Open(
-        location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)) {
-    LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n";
-    UNREACHABLE();
-  } else {
-    CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location;
-    return std::move(dex_files[0]);
-  }
-}
-
 void CommonRuntimeTestImpl::SetUp() {
-  SetUpAndroidRoot();
-  SetUpAndroidData(android_data_);
-  dalvik_cache_.append(android_data_.c_str());
-  dalvik_cache_.append("/dalvik-cache");
-  int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700);
-  ASSERT_EQ(mkdir_result, 0);
+  CommonArtTestImpl::SetUp();
 
   std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB));
   std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB));
@@ -406,80 +196,13 @@
   runtime_->GetHeap()->SetMinIntervalHomogeneousSpaceCompactionByOom(0U);
 }
 
-void CommonRuntimeTestImpl::ClearDirectory(const char* dirpath, bool recursive) {
-  ASSERT_TRUE(dirpath != nullptr);
-  DIR* dir = opendir(dirpath);
-  ASSERT_TRUE(dir != nullptr);
-  dirent* e;
-  struct stat s;
-  while ((e = readdir(dir)) != nullptr) {
-    if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) {
-      continue;
-    }
-    std::string filename(dirpath);
-    filename.push_back('/');
-    filename.append(e->d_name);
-    int stat_result = lstat(filename.c_str(), &s);
-    ASSERT_EQ(0, stat_result) << "unable to stat " << filename;
-    if (S_ISDIR(s.st_mode)) {
-      if (recursive) {
-        ClearDirectory(filename.c_str());
-        int rmdir_result = rmdir(filename.c_str());
-        ASSERT_EQ(0, rmdir_result) << filename;
-      }
-    } else {
-      int unlink_result = unlink(filename.c_str());
-      ASSERT_EQ(0, unlink_result) << filename;
-    }
-  }
-  closedir(dir);
-}
-
 void CommonRuntimeTestImpl::TearDown() {
-  const char* android_data = getenv("ANDROID_DATA");
-  ASSERT_TRUE(android_data != nullptr);
-  ClearDirectory(dalvik_cache_.c_str());
-  int rmdir_cache_result = rmdir(dalvik_cache_.c_str());
-  ASSERT_EQ(0, rmdir_cache_result);
-  TearDownAndroidData(android_data_, true);
-  dalvik_cache_.clear();
-
+  CommonArtTestImpl::TearDown();
   if (runtime_ != nullptr) {
     runtime_->GetHeap()->VerifyHeap();  // Check for heap corruption after the test
   }
 }
 
-static std::string GetDexFileName(const std::string& jar_prefix, bool host) {
-  std::string path;
-  if (host) {
-    const char* host_dir = getenv("ANDROID_HOST_OUT");
-    CHECK(host_dir != nullptr);
-    path = host_dir;
-  } else {
-    path = GetAndroidRoot();
-  }
-
-  std::string suffix = host
-      ? "-hostdex"                 // The host version.
-      : "-testdex";                // The unstripped target version.
-
-  return StringPrintf("%s/framework/%s%s.jar", path.c_str(), jar_prefix.c_str(), suffix.c_str());
-}
-
-std::vector<std::string> CommonRuntimeTestImpl::GetLibCoreDexFileNames() {
-  return std::vector<std::string>({GetDexFileName("core-oj", IsHost()),
-                                   GetDexFileName("core-libart", IsHost())});
-}
-
-std::string CommonRuntimeTestImpl::GetTestAndroidRoot() {
-  if (IsHost()) {
-    const char* host_dir = getenv("ANDROID_HOST_OUT");
-    CHECK(host_dir != nullptr);
-    return host_dir;
-  }
-  return GetAndroidRoot();
-}
-
 // Check that for target builds we have ART_TARGET_NATIVETEST_DIR set.
 #ifdef ART_TARGET
 #ifndef ART_TARGET_NATIVETEST_DIR
@@ -491,47 +214,6 @@
 #define ART_TARGET_NATIVETEST_DIR_STRING ""
 #endif
 
-std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) const {
-  CHECK(name != nullptr);
-  std::string filename;
-  if (IsHost()) {
-    filename += getenv("ANDROID_HOST_OUT");
-    filename += "/framework/";
-  } else {
-    filename += ART_TARGET_NATIVETEST_DIR_STRING;
-  }
-  filename += "art-gtest-";
-  filename += name;
-  filename += ".jar";
-  return filename;
-}
-
-std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTestImpl::OpenTestDexFiles(
-    const char* name) {
-  std::string filename = GetTestDexFileName(name);
-  static constexpr bool kVerifyChecksum = true;
-  std::string error_msg;
-  const ArtDexFileLoader dex_file_loader;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  bool success = dex_file_loader.Open(filename.c_str(),
-                                      filename.c_str(),
-                                      /* verify */ true,
-                                      kVerifyChecksum,
-                                      &error_msg, &dex_files);
-  CHECK(success) << "Failed to open '" << filename << "': " << error_msg;
-  for (auto& dex_file : dex_files) {
-    CHECK_EQ(PROT_READ, dex_file->GetPermissions());
-    CHECK(dex_file->IsReadOnly());
-  }
-  return dex_files;
-}
-
-std::unique_ptr<const DexFile> CommonRuntimeTestImpl::OpenTestDexFile(const char* name) {
-  std::vector<std::unique_ptr<const DexFile>> vector = OpenTestDexFiles(name);
-  EXPECT_EQ(1U, vector.size());
-  return std::move(vector[0]);
-}
-
 std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(jobject jclass_loader) {
   ScopedObjectAccess soa(Thread::Current());
 
@@ -658,43 +340,6 @@
                                        parent_loader);
 }
 
-std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) {
-  CHECK(suffix != nullptr);
-
-  std::string location;
-  if (IsHost()) {
-    const char* host_dir = getenv("ANDROID_HOST_OUT");
-    CHECK(host_dir != nullptr);
-    location = StringPrintf("%s/framework/core.%s", host_dir, suffix);
-  } else {
-    location = StringPrintf("/data/art-test/core.%s", suffix);
-  }
-
-  return location;
-}
-
-std::string CommonRuntimeTestImpl::CreateClassPath(
-    const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
-  CHECK(!dex_files.empty());
-  std::string classpath = dex_files[0]->GetLocation();
-  for (size_t i = 1; i < dex_files.size(); i++) {
-    classpath += ":" + dex_files[i]->GetLocation();
-  }
-  return classpath;
-}
-
-std::string CommonRuntimeTestImpl::CreateClassPathWithChecksums(
-    const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
-  CHECK(!dex_files.empty());
-  std::string classpath = dex_files[0]->GetLocation() + "*" +
-      std::to_string(dex_files[0]->GetLocationChecksum());
-  for (size_t i = 1; i < dex_files.size(); i++) {
-    classpath += ":" + dex_files[i]->GetLocation() + "*" +
-        std::to_string(dex_files[i]->GetLocationChecksum());
-  }
-  return classpath;
-}
-
 void CommonRuntimeTestImpl::FillHeap(Thread* self,
                                      ClassLinker* class_linker,
                                      VariableSizedHandleScope* handle_scope) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 83a1f9a..892abb4 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -25,12 +25,13 @@
 #include <android-base/logging.h>
 
 #include "arch/instruction_set.h"
+#include "base/common_art_test.h"
+#include "base/globals.h"
 #include "base/mutex.h"
 #include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/compact_dex_level.h"
-#include "globals.h"
 // TODO: Add inl file and avoid including inl.
 #include "obj_ptr-inl.h"
 #include "scoped_thread_state_change-inl.h"
@@ -55,64 +56,13 @@
 class Thread;
 class VariableSizedHandleScope;
 
-class ScratchFile {
- public:
-  ScratchFile();
-
-  explicit ScratchFile(const std::string& filename);
-
-  ScratchFile(const ScratchFile& other, const char* suffix);
-
-  ScratchFile(ScratchFile&& other);
-
-  ScratchFile& operator=(ScratchFile&& other);
-
-  explicit ScratchFile(File* file);
-
-  ~ScratchFile();
-
-  const std::string& GetFilename() const {
-    return filename_;
-  }
-
-  File* GetFile() const {
-    return file_.get();
-  }
-
-  int GetFd() const;
-
-  void Close();
-  void Unlink();
-
- private:
-  std::string filename_;
-  std::unique_ptr<File> file_;
-};
-
-class CommonRuntimeTestImpl {
+class CommonRuntimeTestImpl : public CommonArtTestImpl {
  public:
   CommonRuntimeTestImpl();
   virtual ~CommonRuntimeTestImpl();
-  static void SetUpAndroidRoot();
 
-  // Note: setting up ANDROID_DATA may create a temporary directory. If this is used in a
-  // non-derived class, be sure to also call the corresponding tear-down below.
-  static void SetUpAndroidData(std::string& android_data);
-
-  static void TearDownAndroidData(const std::string& android_data, bool fail_on_error);
-
-  // Gets the paths of the libcore dex files.
-  static std::vector<std::string> GetLibCoreDexFileNames();
-
-  // Returns bin directory which contains host's prebuild tools.
-  static std::string GetAndroidHostToolsDir();
-
-  // Returns bin directory which contains target's prebuild tools.
   static std::string GetAndroidTargetToolsDir(InstructionSet isa);
 
-  // Retuerns the filename for a test dex (i.e. XandY or ManyMethods).
-  std::string GetTestDexFileName(const char* name) const;
-
   // A helper function to fill the heap.
   static void FillHeap(Thread* self,
                        ClassLinker* class_linker,
@@ -157,26 +107,6 @@
   // Called after the runtime is created.
   virtual void PostRuntimeCreate() {}
 
-  static bool IsHost() {
-    return !kIsTargetBuild;
-  }
-
-  // File location to core.art, e.g. $ANDROID_HOST_OUT/system/framework/core.art
-  static std::string GetCoreArtLocation();
-
-  // File location to core.oat, e.g. $ANDROID_HOST_OUT/system/framework/core.oat
-  static std::string GetCoreOatLocation();
-
-  std::unique_ptr<const DexFile> LoadExpectSingleDexFile(const char* location);
-
-  void ClearDirectory(const char* dirpath, bool recursive = true);
-
-  std::string GetTestAndroidRoot();
-
-  std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name);
-
-  std::unique_ptr<const DexFile> OpenTestDexFile(const char* name);
-
   // Loads the test dex file identified by the given dex_name into a PathClassLoader.
   // Returns the created class loader.
   jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -191,9 +121,6 @@
                                         jclass loader_class,
                                         jobject parent_loader);
 
-  std::string android_data_;
-  std::string dalvik_cache_;
-
   std::unique_ptr<Runtime> runtime_;
 
   // The class_linker_, java_lang_dex_file_, and boot_class_path_ are all
@@ -221,20 +148,6 @@
   // Called to finish up runtime creation and filling test fields. By default runs root
   // initializers, initialize well-known classes, and creates the heap thread pool.
   virtual void FinalizeSetup();
-
-  // Creates the class path string for the given dex files (the list of dex file locations
-  // separated by ':').
-  std::string CreateClassPath(
-      const std::vector<std::unique_ptr<const DexFile>>& dex_files);
-  // Same as CreateClassPath but add the dex file checksum after each location. The separator
-  // is '*'.
-  std::string CreateClassPathWithChecksums(
-      const std::vector<std::unique_ptr<const DexFile>>& dex_files);
-
- private:
-  static std::string GetCoreFileLocation(const char* suffix);
-
-  std::vector<std::unique_ptr<const DexFile>> loaded_dex_files_;
 };
 
 template <typename TestType>
@@ -278,12 +191,6 @@
   DISALLOW_COPY_AND_ASSIGN(CheckJniAbortCatcher);
 };
 
-#define TEST_DISABLED_FOR_TARGET() \
-  if (kIsTargetBuild) { \
-    printf("WARNING: TEST DISABLED FOR TARGET\n"); \
-    return; \
-  }
-
 #define TEST_DISABLED_FOR_MIPS() \
   if (kRuntimeISA == InstructionSet::kMips) { \
     printf("WARNING: TEST DISABLED FOR MIPS\n"); \
@@ -308,30 +215,6 @@
     return; \
   }
 
-#define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \
-  if (!kHostStaticBuildEnabled) { \
-    printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \
-    return; \
-  }
-
-#define TEST_DISABLED_FOR_MEMORY_TOOL() \
-  if (RUNNING_ON_MEMORY_TOOL > 0) { \
-    printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \
-    return; \
-  }
-
-#define TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND() \
-  if (RUNNING_ON_MEMORY_TOOL > 0 && kMemoryToolIsValgrind) { \
-    printf("WARNING: TEST DISABLED FOR MEMORY TOOL VALGRIND\n"); \
-    return; \
-  }
-
-#define TEST_DISABLED_FOR_MEMORY_TOOL_ASAN() \
-  if (RUNNING_ON_MEMORY_TOOL > 0 && !kMemoryToolIsValgrind) { \
-    printf("WARNING: TEST DISABLED FOR MEMORY TOOL ASAN\n"); \
-    return; \
-  }
-
 #define TEST_DISABLED_FOR_HEAP_POISONING() \
   if (kPoisonHeapReferences) { \
     printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 8fd95ed..657a78b 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -880,11 +880,14 @@
 
 void ThrowWrongMethodTypeException(ObjPtr<mirror::MethodType> expected_type,
                                    ObjPtr<mirror::MethodType> actual_type) {
-  ThrowException("Ljava/lang/invoke/WrongMethodTypeException;",
-                 nullptr,
-                 StringPrintf("Expected %s but was %s",
-                              expected_type->PrettyDescriptor().c_str(),
-                              actual_type->PrettyDescriptor().c_str()).c_str());
+  ThrowWrongMethodTypeException(expected_type->PrettyDescriptor(), actual_type->PrettyDescriptor());
+}
+
+void ThrowWrongMethodTypeException(const std::string& expected_descriptor,
+                                   const std::string& actual_descriptor) {
+  std::ostringstream msg;
+  msg << "Expected " << expected_descriptor << " but was " << actual_descriptor;
+  ThrowException("Ljava/lang/invoke/WrongMethodTypeException;",  nullptr, msg.str().c_str());
 }
 
 }  // namespace art
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 29a056e..6acff6f 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -274,6 +274,10 @@
                                    ObjPtr<mirror::MethodType> callsite_type)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
+void ThrowWrongMethodTypeException(const std::string& expected_descriptor,
+                                   const std::string& actual_descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_COMMON_THROWS_H_
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 28659cb..88628bb 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -51,7 +51,7 @@
 #include "handle_scope-inl.h"
 #include "jdwp/jdwp_priv.h"
 #include "jdwp/object_registry.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "jvalue-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/class.h"
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index 6f3354b..95b42d2 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -24,11 +24,12 @@
 #include "art_method-inl.h"
 #include "class_linker-inl.h"
 #include "dex/dex_file-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "jvalue-inl.h"
 #include "mirror/field.h"
 #include "mirror/method.h"
 #include "oat_file.h"
+#include "obj_ptr-inl.h"
 #include "reflection.h"
 #include "thread.h"
 #include "well_known_classes.h"
@@ -116,9 +117,9 @@
   DISALLOW_COPY_AND_ASSIGN(ClassData);
 };
 
-mirror::Object* CreateAnnotationMember(const ClassData& klass,
-                                       Handle<mirror::Class> annotation_class,
-                                       const uint8_t** annotation)
+ObjPtr<mirror::Object> CreateAnnotationMember(const ClassData& klass,
+                                              Handle<mirror::Class> annotation_class,
+                                              const uint8_t** annotation)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 bool IsVisibilityCompatible(uint32_t actual, uint32_t expected) {
@@ -333,7 +334,7 @@
   return dex_file.GetClassAnnotationSet(annotations_dir);
 }
 
-mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** annotation)
+ObjPtr<mirror::Object> ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** annotation)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   uint32_t type_index = DecodeUnsignedLeb128(annotation);
   uint32_t size = DecodeUnsignedLeb128(annotation);
@@ -355,13 +356,13 @@
   }
 
   ObjPtr<mirror::Class> annotation_member_class =
-      soa.Decode<mirror::Class>(WellKnownClasses::libcore_reflect_AnnotationMember).Ptr();
-  mirror::Class* annotation_member_array_class =
+      soa.Decode<mirror::Class>(WellKnownClasses::libcore_reflect_AnnotationMember);
+  ObjPtr<mirror::Class> annotation_member_array_class =
       class_linker->FindArrayClass(self, &annotation_member_class);
   if (annotation_member_array_class == nullptr) {
     return nullptr;
   }
-  mirror::ObjectArray<mirror::Object>* element_array = nullptr;
+  ObjPtr<mirror::ObjectArray<mirror::Object>> element_array = nullptr;
   if (size > 0) {
     element_array =
         mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_member_array_class, size);
@@ -373,7 +374,7 @@
 
   Handle<mirror::ObjectArray<mirror::Object>> h_element_array(hs.NewHandle(element_array));
   for (uint32_t i = 0; i < size; ++i) {
-    mirror::Object* new_member = CreateAnnotationMember(klass, annotation_class, annotation);
+    ObjPtr<mirror::Object> new_member = CreateAnnotationMember(klass, annotation_class, annotation);
     if (new_member == nullptr) {
       return nullptr;
     }
@@ -605,7 +606,7 @@
             return false;
           }
           if (!component_type->IsPrimitive()) {
-            mirror::Object* obj = new_annotation_value.value_.GetL();
+            ObjPtr<mirror::Object> obj = new_annotation_value.value_.GetL();
             new_array->AsObjectArray<mirror::Object>()->
                 SetWithoutChecks<kTransactionActive>(i, obj);
           } else {
@@ -682,20 +683,20 @@
   *annotation_ptr = annotation;
 
   if (result_style == DexFile::kAllObjects && primitive_type != Primitive::kPrimVoid) {
-    element_object = BoxPrimitive(primitive_type, annotation_value->value_).Ptr();
+    element_object = BoxPrimitive(primitive_type, annotation_value->value_);
     set_object = true;
   }
 
   if (set_object) {
-    annotation_value->value_.SetL(element_object.Ptr());
+    annotation_value->value_.SetL(element_object);
   }
 
   return true;
 }
 
-mirror::Object* CreateAnnotationMember(const ClassData& klass,
-                                       Handle<mirror::Class> annotation_class,
-                                       const uint8_t** annotation) {
+ObjPtr<mirror::Object> CreateAnnotationMember(const ClassData& klass,
+                                              Handle<mirror::Class> annotation_class,
+                                              const uint8_t** annotation) {
   const DexFile& dex_file = klass.GetDexFile();
   Thread* self = Thread::Current();
   ScopedObjectAccessUnchecked soa(self);
@@ -799,7 +800,7 @@
   return nullptr;
 }
 
-mirror::Object* GetAnnotationObjectFromAnnotationSet(
+ObjPtr<mirror::Object> GetAnnotationObjectFromAnnotationSet(
     const ClassData& klass,
     const DexFile::AnnotationSetItem* annotation_set,
     uint32_t visibility,
@@ -814,11 +815,11 @@
   return ProcessEncodedAnnotation(klass, &annotation);
 }
 
-mirror::Object* GetAnnotationValue(const ClassData& klass,
-                                   const DexFile::AnnotationItem* annotation_item,
-                                   const char* annotation_name,
-                                   Handle<mirror::Class> array_class,
-                                   uint32_t expected_type)
+ObjPtr<mirror::Object> GetAnnotationValue(const ClassData& klass,
+                                          const DexFile::AnnotationItem* annotation_item,
+                                          const char* annotation_name,
+                                          Handle<mirror::Class> array_class,
+                                          uint32_t expected_type)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   const DexFile& dex_file = klass.GetDexFile();
   const uint8_t* annotation =
@@ -864,7 +865,7 @@
   if (string_array_class == nullptr) {
     return nullptr;
   }
-  mirror::Object* obj =
+  ObjPtr<mirror::Object> obj =
       GetAnnotationValue(klass, annotation_item, "value", string_array_class,
                          DexFile::kDexAnnotationArray);
   if (obj == nullptr) {
@@ -873,8 +874,9 @@
   return obj->AsObjectArray<mirror::String>();
 }
 
-mirror::ObjectArray<mirror::Class>* GetThrowsValue(const ClassData& klass,
-                                                   const DexFile::AnnotationSetItem* annotation_set)
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetThrowsValue(
+    const ClassData& klass,
+    const DexFile::AnnotationSetItem* annotation_set)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   const DexFile& dex_file = klass.GetDexFile();
   StackHandleScope<1> hs(Thread::Current());
@@ -890,7 +892,7 @@
   if (class_array_class == nullptr) {
     return nullptr;
   }
-  mirror::Object* obj =
+  ObjPtr<mirror::Object> obj =
       GetAnnotationValue(klass, annotation_item, "value", class_array_class,
                          DexFile::kDexAnnotationArray);
   if (obj == nullptr) {
@@ -899,7 +901,7 @@
   return obj->AsObjectArray<mirror::Class>();
 }
 
-mirror::ObjectArray<mirror::Object>* ProcessAnnotationSet(
+ObjPtr<mirror::ObjectArray<mirror::Object>> ProcessAnnotationSet(
     const ClassData& klass,
     const DexFile::AnnotationSetItem* annotation_set,
     uint32_t visibility)
@@ -930,7 +932,7 @@
       continue;
     }
     const uint8_t* annotation = annotation_item->annotation_;
-    mirror::Object* annotation_obj = ProcessEncodedAnnotation(klass, &annotation);
+    ObjPtr<mirror::Object> annotation_obj = ProcessEncodedAnnotation(klass, &annotation);
     if (annotation_obj != nullptr) {
       result->SetWithoutChecks<false>(dest_index, annotation_obj);
       ++dest_index;
@@ -943,21 +945,21 @@
     return result.Get();
   }
 
-  mirror::ObjectArray<mirror::Object>* trimmed_result =
+  ObjPtr<mirror::ObjectArray<mirror::Object>> trimmed_result =
       mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), dest_index);
   if (trimmed_result == nullptr) {
     return nullptr;
   }
 
   for (uint32_t i = 0; i < dest_index; ++i) {
-    mirror::Object* obj = result->GetWithoutChecks(i);
+    ObjPtr<mirror::Object> obj = result->GetWithoutChecks(i);
     trimmed_result->SetWithoutChecks<false>(i, obj);
   }
 
   return trimmed_result;
 }
 
-mirror::ObjectArray<mirror::Object>* ProcessAnnotationSetRefList(
+ObjPtr<mirror::ObjectArray<mirror::Object>> ProcessAnnotationSetRefList(
     const ClassData& klass,
     const DexFile::AnnotationSetRefList* set_ref_list,
     uint32_t size)
@@ -968,7 +970,7 @@
   StackHandleScope<1> hs(self);
   ObjPtr<mirror::Class> annotation_array_class =
       soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
-  mirror::Class* annotation_array_array_class =
+  ObjPtr<mirror::Class> annotation_array_array_class =
       Runtime::Current()->GetClassLinker()->FindArrayClass(self, &annotation_array_class);
   if (annotation_array_array_class == nullptr) {
     return nullptr;
@@ -982,8 +984,9 @@
   for (uint32_t index = 0; index < size; ++index) {
     const DexFile::AnnotationSetRefItem* set_ref_item = &set_ref_list->list_[index];
     const DexFile::AnnotationSetItem* set_item = dex_file.GetSetRefItemItem(set_ref_item);
-    mirror::Object* annotation_set = ProcessAnnotationSet(klass, set_item,
-                                                          DexFile::kDexVisibilityRuntime);
+    ObjPtr<mirror::Object> annotation_set = ProcessAnnotationSet(klass,
+                                                                 set_item,
+                                                                 DexFile::kDexVisibilityRuntime);
     if (annotation_set == nullptr) {
       return nullptr;
     }
@@ -995,7 +998,8 @@
 
 namespace annotations {
 
-mirror::Object* GetAnnotationForField(ArtField* field, Handle<mirror::Class> annotation_class) {
+ObjPtr<mirror::Object> GetAnnotationForField(ArtField* field,
+                                             Handle<mirror::Class> annotation_class) {
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field);
   if (annotation_set == nullptr) {
     return nullptr;
@@ -1008,7 +1012,7 @@
                                               annotation_class);
 }
 
-mirror::ObjectArray<mirror::Object>* GetAnnotationsForField(ArtField* field) {
+ObjPtr<mirror::ObjectArray<mirror::Object>> GetAnnotationsForField(ArtField* field) {
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field);
   StackHandleScope<1> hs(Thread::Current());
   const ClassData field_class(hs, field);
@@ -1037,7 +1041,7 @@
   return annotation_item != nullptr;
 }
 
-mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) {
+ObjPtr<mirror::Object> GetAnnotationDefaultValue(ArtMethod* method) {
   const ClassData klass(method);
   const DexFile* dex_file = &klass.GetDexFile();
   const DexFile::AnnotationsDirectoryItem* annotations_dir =
@@ -1081,7 +1085,8 @@
   return annotation_value.value_.GetL();
 }
 
-mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle<mirror::Class> annotation_class) {
+ObjPtr<mirror::Object> GetAnnotationForMethod(ArtMethod* method,
+                                              Handle<mirror::Class> annotation_class) {
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method);
   if (annotation_set == nullptr) {
     return nullptr;
@@ -1090,14 +1095,14 @@
                                               DexFile::kDexVisibilityRuntime, annotation_class);
 }
 
-mirror::ObjectArray<mirror::Object>* GetAnnotationsForMethod(ArtMethod* method) {
+ObjPtr<mirror::ObjectArray<mirror::Object>> GetAnnotationsForMethod(ArtMethod* method) {
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method);
   return ProcessAnnotationSet(ClassData(method),
                               annotation_set,
                               DexFile::kDexVisibilityRuntime);
 }
 
-mirror::ObjectArray<mirror::Class>* GetExceptionTypesForMethod(ArtMethod* method) {
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetExceptionTypesForMethod(ArtMethod* method) {
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method);
   if (annotation_set == nullptr) {
     return nullptr;
@@ -1105,7 +1110,7 @@
   return GetThrowsValue(ClassData(method), annotation_set);
 }
 
-mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method) {
+ObjPtr<mirror::ObjectArray<mirror::Object>> GetParameterAnnotations(ArtMethod* method) {
   const DexFile* dex_file = method->GetDexFile();
   const DexFile::ParameterAnnotationsItem* parameter_annotations =
       FindAnnotationsItemForMethod(method);
@@ -1136,9 +1141,9 @@
   return set_ref_list->size_;
 }
 
-mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method,
-                                                uint32_t parameter_idx,
-                                                Handle<mirror::Class> annotation_class) {
+ObjPtr<mirror::Object> GetAnnotationForMethodParameter(ArtMethod* method,
+                                                       uint32_t parameter_idx,
+                                                       Handle<mirror::Class> annotation_class) {
   const DexFile* dex_file = method->GetDexFile();
   const DexFile::ParameterAnnotationsItem* parameter_annotations =
       FindAnnotationsItemForMethod(method);
@@ -1307,8 +1312,8 @@
   return access_flags;
 }
 
-mirror::Object* GetAnnotationForClass(Handle<mirror::Class> klass,
-                                      Handle<mirror::Class> annotation_class) {
+ObjPtr<mirror::Object> GetAnnotationForClass(Handle<mirror::Class> klass,
+                                             Handle<mirror::Class> annotation_class) {
   ClassData data(klass);
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
   if (annotation_set == nullptr) {
@@ -1320,13 +1325,13 @@
                                               annotation_class);
 }
 
-mirror::ObjectArray<mirror::Object>* GetAnnotationsForClass(Handle<mirror::Class> klass) {
+ObjPtr<mirror::ObjectArray<mirror::Object>> GetAnnotationsForClass(Handle<mirror::Class> klass) {
   ClassData data(klass);
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
   return ProcessAnnotationSet(data, annotation_set, DexFile::kDexVisibilityRuntime);
 }
 
-mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> klass) {
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetDeclaredClasses(Handle<mirror::Class> klass) {
   ClassData data(klass);
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
   if (annotation_set == nullptr) {
@@ -1345,7 +1350,7 @@
   if (class_array_class == nullptr) {
     return nullptr;
   }
-  mirror::Object* obj =
+  ObjPtr<mirror::Object> obj =
       GetAnnotationValue(data, annotation_item, "value", class_array_class,
                          DexFile::kDexAnnotationArray);
   if (obj == nullptr) {
@@ -1354,7 +1359,7 @@
   return obj->AsObjectArray<mirror::Class>();
 }
 
-mirror::Class* GetDeclaringClass(Handle<mirror::Class> klass) {
+ObjPtr<mirror::Class> GetDeclaringClass(Handle<mirror::Class> klass) {
   ClassData data(klass);
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
   if (annotation_set == nullptr) {
@@ -1366,17 +1371,19 @@
   if (annotation_item == nullptr) {
     return nullptr;
   }
-  mirror::Object* obj = GetAnnotationValue(data, annotation_item, "value",
-                                           ScopedNullHandle<mirror::Class>(),
-                                           DexFile::kDexAnnotationType);
+  ObjPtr<mirror::Object> obj = GetAnnotationValue(data,
+                                                  annotation_item,
+                                                  "value",
+                                                  ScopedNullHandle<mirror::Class>(),
+                                                  DexFile::kDexAnnotationType);
   if (obj == nullptr) {
     return nullptr;
   }
   return obj->AsClass();
 }
 
-mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) {
-  mirror::Class* declaring_class = GetDeclaringClass(klass);
+ObjPtr<mirror::Class> GetEnclosingClass(Handle<mirror::Class> klass) {
+  ObjPtr<mirror::Class> declaring_class = GetDeclaringClass(klass);
   if (declaring_class != nullptr) {
     return declaring_class;
   }
@@ -1420,7 +1427,7 @@
   return method->GetDeclaringClass();
 }
 
-mirror::Object* GetEnclosingMethod(Handle<mirror::Class> klass) {
+ObjPtr<mirror::Object> GetEnclosingMethod(Handle<mirror::Class> klass) {
   ClassData data(klass);
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
   if (annotation_set == nullptr) {
@@ -1438,7 +1445,7 @@
       DexFile::kDexAnnotationMethod);
 }
 
-bool GetInnerClass(Handle<mirror::Class> klass, mirror::String** name) {
+bool GetInnerClass(Handle<mirror::Class> klass, ObjPtr<mirror::String>* name) {
   ClassData data(klass);
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
   if (annotation_set == nullptr) {
diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h
index 4bb0d75..9645a7f 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -22,6 +22,7 @@
 #include "handle.h"
 #include "mirror/dex_cache.h"
 #include "mirror/object_array.h"
+#include "obj_ptr.h"
 
 namespace art {
 
@@ -35,9 +36,10 @@
 namespace annotations {
 
 // Field annotations.
-mirror::Object* GetAnnotationForField(ArtField* field, Handle<mirror::Class> annotation_class)
+ObjPtr<mirror::Object> GetAnnotationForField(ArtField* field,
+                                             Handle<mirror::Class> annotation_class)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::ObjectArray<mirror::Object>* GetAnnotationsForField(ArtField* field)
+ObjPtr<mirror::ObjectArray<mirror::Object>> GetAnnotationsForField(ArtField* field)
     REQUIRES_SHARED(Locks::mutator_lock_);
 mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForField(ArtField* field)
     REQUIRES_SHARED(Locks::mutator_lock_);
@@ -45,21 +47,22 @@
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Method annotations.
-mirror::Object* GetAnnotationDefaultValue(ArtMethod* method)
+ObjPtr<mirror::Object> GetAnnotationDefaultValue(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle<mirror::Class> annotation_class)
+ObjPtr<mirror::Object> GetAnnotationForMethod(ArtMethod* method,
+                                              Handle<mirror::Class> annotation_class)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::ObjectArray<mirror::Object>* GetAnnotationsForMethod(ArtMethod* method)
+ObjPtr<mirror::ObjectArray<mirror::Object>> GetAnnotationsForMethod(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::ObjectArray<mirror::Class>* GetExceptionTypesForMethod(ArtMethod* method)
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetExceptionTypesForMethod(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method)
+ObjPtr<mirror::ObjectArray<mirror::Object>> GetParameterAnnotations(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_);
 uint32_t GetNumberOfAnnotatedMethodParameters(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method,
-                                                uint32_t parameter_idx,
-                                                Handle<mirror::Class> annotation_class)
+ObjPtr<mirror::Object> GetAnnotationForMethodParameter(ArtMethod* method,
+                                                       uint32_t parameter_idx,
+                                                       Handle<mirror::Class> annotation_class)
     REQUIRES_SHARED(Locks::mutator_lock_);
 bool GetParametersMetadataForMethod(ArtMethod* method,
                                     MutableHandle<mirror::ObjectArray<mirror::String>>* names,
@@ -85,20 +88,20 @@
                                               uint32_t method_index);
 
 // Class annotations.
-mirror::Object* GetAnnotationForClass(Handle<mirror::Class> klass,
+ObjPtr<mirror::Object> GetAnnotationForClass(Handle<mirror::Class> klass,
                                       Handle<mirror::Class> annotation_class)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::ObjectArray<mirror::Object>* GetAnnotationsForClass(Handle<mirror::Class> klass)
+ObjPtr<mirror::ObjectArray<mirror::Object>> GetAnnotationsForClass(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> klass)
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetDeclaredClasses(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::Class* GetDeclaringClass(Handle<mirror::Class> klass)
+ObjPtr<mirror::Class> GetDeclaringClass(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass)
+ObjPtr<mirror::Class> GetEnclosingClass(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::Object* GetEnclosingMethod(Handle<mirror::Class> klass)
+ObjPtr<mirror::Object> GetEnclosingMethod(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
-bool GetInnerClass(Handle<mirror::Class> klass, mirror::String** name)
+bool GetInnerClass(Handle<mirror::Class> klass, ObjPtr<mirror::String>* name)
     REQUIRES_SHARED(Locks::mutator_lock_);
 bool GetInnerClassFlags(Handle<mirror::Class> klass, uint32_t* flags)
     REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 8f0f9c6..f8388f3 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -105,7 +105,8 @@
   }
 
   // Verify the odex file was generated as expected.
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   oat_location.c_str(),
                                                    oat_location.c_str(),
                                                    nullptr,
                                                    nullptr,
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 719b4af..026b5da 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1073,6 +1073,29 @@
   return true;
 }
 
+static InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) {
+  switch (e_machine) {
+    case EM_ARM:
+      return InstructionSet::kArm;
+    case EM_AARCH64:
+      return InstructionSet::kArm64;
+    case EM_386:
+      return InstructionSet::kX86;
+    case EM_X86_64:
+      return InstructionSet::kX86_64;
+    case EM_MIPS: {
+      if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 ||
+          (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) {
+        return InstructionSet::kMips;
+      } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) {
+        return InstructionSet::kMips64;
+      }
+      break;
+    }
+  }
+  return InstructionSet::kNone;
+}
+
 template <typename ElfTypes>
 bool ElfFileImpl<ElfTypes>::Load(File* file,
                                  bool executable,
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 270bce2..f6b1c73 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -31,7 +31,7 @@
 #include "imt_conflict_table.h"
 #include "imtable-inl.h"
 #include "indirect_reference_table.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/array.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
@@ -47,7 +47,6 @@
 inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
                                     const MethodInfo& method_info,
                                     const InlineInfo& inline_info,
-                                    const InlineInfoEncoding& encoding,
                                     uint8_t inlining_depth)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(!outer_method->IsObsolete());
@@ -57,12 +56,12 @@
   // suspended while executing it.
   ScopedAssertNoThreadSuspension sants(__FUNCTION__);
 
-  if (inline_info.EncodesArtMethodAtDepth(encoding, inlining_depth)) {
-    return inline_info.GetArtMethodAtDepth(encoding, inlining_depth);
+  if (inline_info.EncodesArtMethodAtDepth(inlining_depth)) {
+    return inline_info.GetArtMethodAtDepth(inlining_depth);
   }
 
-  uint32_t method_index = inline_info.GetMethodIndexAtDepth(encoding, method_info, inlining_depth);
-  if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast<uint32_t>(-1)) {
+  uint32_t method_index = inline_info.GetMethodIndexAtDepth(method_info, inlining_depth);
+  if (inline_info.GetDexPcAtDepth(inlining_depth) == static_cast<uint32_t>(-1)) {
     // "charAt" special case. It is the only non-leaf method we inline across dex files.
     ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
     DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index);
@@ -70,45 +69,41 @@
   }
 
   // Find which method did the call in the inlining hierarchy.
-  ArtMethod* caller = outer_method;
-  if (inlining_depth != 0) {
-    caller = GetResolvedMethod(outer_method,
-                               method_info,
-                               inline_info,
-                               encoding,
-                               inlining_depth - 1);
-  }
-
-  // Lookup the declaring class of the inlined method.
-  ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache();
-  ArtMethod* inlined_method = dex_cache->GetResolvedMethod(method_index, kRuntimePointerSize);
-  if (inlined_method != nullptr) {
-    DCHECK(!inlined_method->IsRuntimeMethod());
-    return inlined_method;
-  }
-  // TODO: Use ClassLoader::LookupResolvedMethod() instead.
-  const DexFile* dex_file = dex_cache->GetDexFile();
-  const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index);
-  const char* descriptor = dex_file->StringByTypeIdx(method_id.class_idx_);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Thread* self = Thread::Current();
-  mirror::ClassLoader* class_loader = caller->GetDeclaringClass()->GetClassLoader();
-  mirror::Class* klass = class_linker->LookupClass(self, descriptor, class_loader);
-  if (klass == nullptr) {
-    LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor
-               << " was not found in the class loader of " << caller->PrettyMethod() << ". "
-               << "This must be due to playing wrongly with class loaders";
+  ArtMethod* method = outer_method;
+  for (uint32_t depth = 0, end = inlining_depth + 1u; depth != end; ++depth) {
+    DCHECK(!inline_info.EncodesArtMethodAtDepth(depth));
+    DCHECK_NE(inline_info.GetDexPcAtDepth(depth), static_cast<uint32_t>(-1));
+    method_index = inline_info.GetMethodIndexAtDepth(method_info, depth);
+    ArtMethod* inlined_method = class_linker->LookupResolvedMethod(method_index,
+                                                                   method->GetDexCache(),
+                                                                   method->GetClassLoader());
+    if (UNLIKELY(inlined_method == nullptr)) {
+      LOG(FATAL) << "Could not find an inlined method from an .oat file: "
+                 << method->GetDexFile()->PrettyMethod(method_index) << " . "
+                 << "This must be due to duplicate classes or playing wrongly with class loaders";
+      UNREACHABLE();
+    }
+    DCHECK(!inlined_method->IsRuntimeMethod());
+    if (UNLIKELY(inlined_method->GetDexFile() != method->GetDexFile())) {
+      // TODO: We could permit inlining within a multi-dex oat file and the boot image,
+      // even going back from boot image methods to the same oat file. However, this is
+      // not currently implemented in the compiler. Therefore crossing dex file boundary
+      // indicates that the inlined definition is not the same as the one used at runtime.
+      LOG(FATAL) << "Inlined method resolution crossed dex file boundary: from "
+                 << method->PrettyMethod()
+                 << " in " << method->GetDexFile()->GetLocation() << "/"
+                 << static_cast<const void*>(method->GetDexFile())
+                 << " to " << inlined_method->PrettyMethod()
+                 << " in " << inlined_method->GetDexFile()->GetLocation() << "/"
+                 << static_cast<const void*>(inlined_method->GetDexFile()) << ". "
+                 << "This must be due to duplicate classes or playing wrongly with class loaders";
+      UNREACHABLE();
+    }
+    method = inlined_method;
   }
 
-  inlined_method = class_linker->FindResolvedMethod(klass, dex_cache, class_loader, method_index);
-  if (inlined_method == nullptr) {
-    LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor
-               << " does not have " << dex_file->GetMethodName(method_id)
-               << dex_file->GetMethodSignature(method_id) << " declared. "
-               << "This must be due to duplicate classes or playing wrongly with class loaders";
-  }
-
-  return inlined_method;
+  return method;
 }
 
 ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(mirror::Class* klass,
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index ffa138d..7f9b385 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -26,7 +26,7 @@
 #include "entrypoints/quick/callee_save_frame.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc/accounting/card_table-inl.h"
-#include "java_vm_ext.h"
+#include "jni/java_vm_ext.h"
 #include "mirror/class-inl.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
@@ -180,10 +180,10 @@
     ArtMethod** sp, CalleeSaveType type) REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type));
 
-  const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type);
+  const size_t callee_frame_size = RuntimeCalleeSaveFrame::GetFrameSize(type);
   auto** caller_sp = reinterpret_cast<ArtMethod**>(
       reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
-  const size_t callee_return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, type);
+  const size_t callee_return_pc_offset = RuntimeCalleeSaveFrame::GetReturnPcOffset(type);
   uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(
       (reinterpret_cast<uint8_t*>(sp) + callee_return_pc_offset));
   ArtMethod* outer_method = *caller_sp;
@@ -201,18 +201,16 @@
       DCHECK(current_code != nullptr);
       DCHECK(current_code->IsOptimized());
       uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc);
-      CodeInfo code_info = current_code->GetOptimizedCodeInfo();
+      CodeInfo code_info(current_code);
       MethodInfo method_info = current_code->GetOptimizedMethodInfo();
-      CodeInfoEncoding encoding = code_info.ExtractEncoding();
-      StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+      StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
       DCHECK(stack_map.IsValid());
-      if (stack_map.HasInlineInfo(encoding.stack_map.encoding)) {
-        InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+      if (stack_map.HasInlineInfo()) {
+        InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
         caller = GetResolvedMethod(outer_method,
                                    method_info,
                                    inline_info,
-                                   encoding.inline_info.encoding,
-                                   inline_info.GetDepth(encoding.inline_info.encoding) - 1);
+                                   inline_info.GetDepth() - 1);
       }
     }
     if (kIsDebugBuild && do_caller_check) {
@@ -260,4 +258,26 @@
   return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first;
 }
 
+ObjPtr<mirror::MethodHandle> ResolveMethodHandleFromCode(ArtMethod* referrer,
+                                                         uint32_t method_handle_idx) {
+  Thread::PoisonObjectPointersIfDebug();
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  return class_linker->ResolveMethodHandle(Thread::Current(), method_handle_idx, referrer);
+}
+
+ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer,
+                                                     dex::ProtoIndex proto_idx) {
+  Thread::PoisonObjectPointersIfDebug();
+  ObjPtr<mirror::MethodType> method_type =
+      referrer->GetDexCache()->GetResolvedMethodType(proto_idx);
+  if (UNLIKELY(method_type == nullptr)) {
+    StackHandleScope<2> hs(Thread::Current());
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+    Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    method_type = class_linker->ResolveMethodType(hs.Self(), proto_idx, dex_cache, class_loader);
+  }
+  return method_type;
+}
+
 }  // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index eb32153..e33de9c 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -34,6 +34,8 @@
 namespace mirror {
 class Array;
 class Class;
+class MethodHandle;
+class MethodType;
 class Object;
 class String;
 }  // namespace mirror
@@ -151,6 +153,15 @@
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
+ObjPtr<mirror::MethodHandle> ResolveMethodHandleFromCode(ArtMethod* referrer,
+                                                         uint32_t method_handle_idx)
+    REQUIRES_SHARED(Locks::mutator_lock_)
+    REQUIRES(!Roles::uninterruptible_);
+
+ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, dex::ProtoIndex proto_idx)
+    REQUIRES_SHARED(Locks::mutator_lock_)
+    REQUIRES(!Roles::uninterruptible_);
+
 inline ObjPtr<mirror::String> ResolveStringFromCode(ArtMethod* referrer,
                                                     dex::StringIndex string_idx)
     REQUIRES_SHARED(Locks::mutator_lock_)
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index 780e221..a4083a4 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -18,7 +18,7 @@
 
 #include "art_method-inl.h"
 #include "entrypoints/entrypoint_utils.h"
-#include "java_vm_ext.h"
+#include "jni/java_vm_ext.h"
 #include "mirror/object-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h
index ef27ca3..6f1bbaa 100644
--- a/runtime/entrypoints/quick/callee_save_frame.h
+++ b/runtime/entrypoints/quick/callee_save_frame.h
@@ -21,16 +21,17 @@
 #include "base/callee_save_type.h"
 #include "base/enums.h"
 #include "base/mutex.h"
+#include "quick/quick_method_frame_info.h"
 #include "thread-inl.h"
 
 // Specific frame size code is in architecture-specific files. We include this to compile-time
 // specialize the code.
-#include "arch/arm/quick_method_frame_info_arm.h"
-#include "arch/arm64/quick_method_frame_info_arm64.h"
-#include "arch/mips/quick_method_frame_info_mips.h"
-#include "arch/mips64/quick_method_frame_info_mips64.h"
-#include "arch/x86/quick_method_frame_info_x86.h"
-#include "arch/x86_64/quick_method_frame_info_x86_64.h"
+#include "arch/arm/callee_save_frame_arm.h"
+#include "arch/arm64/callee_save_frame_arm64.h"
+#include "arch/mips/callee_save_frame_mips.h"
+#include "arch/mips64/callee_save_frame_mips64.h"
+#include "arch/x86/callee_save_frame_x86.h"
+#include "arch/x86_64/callee_save_frame_x86_64.h"
 
 namespace art {
 class ArtMethod;
@@ -67,57 +68,28 @@
   bool exit_check_;
 };
 
-static constexpr size_t GetCalleeSaveFrameSize(InstructionSet isa, CalleeSaveType type) {
-  switch (isa) {
-    case InstructionSet::kArm:
-    case InstructionSet::kThumb2:
-      return arm::ArmCalleeSaveFrameSize(type);
-    case InstructionSet::kArm64:
-      return arm64::Arm64CalleeSaveFrameSize(type);
-    case InstructionSet::kMips:
-      return mips::MipsCalleeSaveFrameSize(type);
-    case InstructionSet::kMips64:
-      return mips64::Mips64CalleeSaveFrameSize(type);
-    case InstructionSet::kX86:
-      return x86::X86CalleeSaveFrameSize(type);
-    case InstructionSet::kX86_64:
-      return x86_64::X86_64CalleeSaveFrameSize(type);
-    case InstructionSet::kNone:
-      LOG(FATAL) << "kNone has no frame size";
-      UNREACHABLE();
-  }
-  LOG(FATAL) << "Unknown ISA " << isa;
-  UNREACHABLE();
-}
+namespace detail_ {
 
-// Note: this specialized statement is sanity-checked in the quick-trampoline gtest.
-static constexpr PointerSize GetConstExprPointerSize(InstructionSet isa) {
-  switch (isa) {
-    case InstructionSet::kArm:
-    case InstructionSet::kThumb2:
-      return kArmPointerSize;
-    case InstructionSet::kArm64:
-      return kArm64PointerSize;
-    case InstructionSet::kMips:
-      return kMipsPointerSize;
-    case InstructionSet::kMips64:
-      return kMips64PointerSize;
-    case InstructionSet::kX86:
-      return kX86PointerSize;
-    case InstructionSet::kX86_64:
-      return kX86_64PointerSize;
-    case InstructionSet::kNone:
-      LOG(FATAL) << "kNone has no pointer size";
-      UNREACHABLE();
-  }
-  LOG(FATAL) << "Unknown ISA " << isa;
-  UNREACHABLE();
-}
+template <InstructionSet>
+struct CSFSelector;  // No definition for unspecialized callee save frame selector.
 
-// Note: this specialized statement is sanity-checked in the quick-trampoline gtest.
-static constexpr size_t GetCalleeSaveReturnPcOffset(InstructionSet isa, CalleeSaveType type) {
-  return GetCalleeSaveFrameSize(isa, type) - static_cast<size_t>(GetConstExprPointerSize(isa));
-}
+// Note: kThumb2 is never the kRuntimeISA.
+template <>
+struct CSFSelector<InstructionSet::kArm> { using type = arm::ArmCalleeSaveFrame; };
+template <>
+struct CSFSelector<InstructionSet::kArm64> { using type = arm64::Arm64CalleeSaveFrame; };
+template <>
+struct CSFSelector<InstructionSet::kMips> { using type = mips::MipsCalleeSaveFrame; };
+template <>
+struct CSFSelector<InstructionSet::kMips64> { using type = mips64::Mips64CalleeSaveFrame; };
+template <>
+struct CSFSelector<InstructionSet::kX86> { using type = x86::X86CalleeSaveFrame; };
+template <>
+struct CSFSelector<InstructionSet::kX86_64> { using type = x86_64::X86_64CalleeSaveFrame; };
+
+}  // namespace detail_
+
+using RuntimeCalleeSaveFrame = detail_::CSFSelector<kRuntimeISA>::type;
 
 }  // namespace art
 
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 2d0932a..1804d9e 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -37,6 +37,8 @@
 extern "C" void* art_quick_initialize_static_storage(uint32_t);
 extern "C" void* art_quick_initialize_type(uint32_t);
 extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t);
+extern "C" void* art_quick_resolve_method_handle(uint32_t);
+extern "C" void* art_quick_resolve_method_type(uint32_t);
 extern "C" void* art_quick_resolve_string(uint32_t);
 
 // Field entrypoints.
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 8c90800..3f66045 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -37,6 +37,8 @@
   qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
   qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
   qpoints->pInitializeType = art_quick_initialize_type;
+  qpoints->pResolveMethodHandle = art_quick_resolve_method_handle;
+  qpoints->pResolveMethodType = art_quick_resolve_method_type;
   qpoints->pResolveString = art_quick_resolve_string;
 
   // Field
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index cfb427f..fa536c7 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -183,6 +183,27 @@
   return result.Ptr();
 }
 
+extern "C" mirror::MethodHandle* artResolveMethodHandleFromCode(uint32_t method_handle_idx,
+                                                                Thread* self)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  auto caller_and_outer =
+      GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything);
+  ArtMethod* caller = caller_and_outer.caller;
+  ObjPtr<mirror::MethodHandle> result = ResolveMethodHandleFromCode(caller, method_handle_idx);
+  return result.Ptr();
+}
+
+extern "C" mirror::MethodType* artResolveMethodTypeFromCode(uint32_t proto_idx, Thread* self)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self,
+                                                                  CalleeSaveType::kSaveEverything);
+  ArtMethod* caller = caller_and_outer.caller;
+  ObjPtr<mirror::MethodType> result = ResolveMethodTypeFromCode(caller, dex::ProtoIndex(proto_idx));
+  return result.Ptr();
+}
+
 extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 48a56f2..3a8faca 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -38,6 +38,8 @@
   V(InitializeStaticStorage, void*, uint32_t) \
   V(InitializeTypeAndVerifyAccess, void*, uint32_t) \
   V(InitializeType, void*, uint32_t) \
+  V(ResolveMethodHandle, void*, uint32_t) \
+  V(ResolveMethodType, void*, uint32_t) \
   V(ResolveString, void*, uint32_t) \
 \
   V(Set8Instance, int, uint32_t, void*, int8_t) \
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 39429c5..ff85f47 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -26,6 +26,7 @@
 #include "dex/dex_instruction-inl.h"
 #include "dex/method_reference.h"
 #include "entrypoints/entrypoint_utils-inl.h"
+#include "entrypoints/quick/callee_save_frame.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc/accounting/card_table-inl.h"
 #include "imt_conflict_table.h"
@@ -42,6 +43,7 @@
 #include "mirror/method_handle_impl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
+#include "mirror/var_handle.h"
 #include "oat_file.h"
 #include "oat_quick_method_header.h"
 #include "quick_exception_handler.h"
@@ -49,6 +51,7 @@
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread-inl.h"
+#include "var_handles.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -59,7 +62,16 @@
   static constexpr size_t kBytesStackArgLocation = 4;
   // Frame size in bytes of a callee-save frame for RefsAndArgs.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize =
-      GetCalleeSaveFrameSize(kRuntimeISA, CalleeSaveType::kSaveRefsAndArgs);
+      RuntimeCalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveRefsAndArgs);
+  // Offset of first GPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset =
+      RuntimeCalleeSaveFrame::GetGpr1Offset(CalleeSaveType::kSaveRefsAndArgs);
+  // Offset of first FPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =
+      RuntimeCalleeSaveFrame::GetFpr1Offset(CalleeSaveType::kSaveRefsAndArgs);
+  // Offset of return address.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_ReturnPcOffset =
+      RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveRefsAndArgs);
 #if defined(__arm__)
   // The callee save frame is pointed to by SP.
   // | argN       |  |
@@ -87,12 +99,6 @@
   static constexpr size_t kNumQuickGprArgs = 3;
   static constexpr size_t kNumQuickFprArgs = 16;
   static constexpr bool kGprFprLockstep = false;
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =
-      arm::ArmCalleeSaveFpr1Offset(CalleeSaveType::kSaveRefsAndArgs);  // Offset of first FPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset =
-      arm::ArmCalleeSaveGpr1Offset(CalleeSaveType::kSaveRefsAndArgs);  // Offset of first GPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset =
-      arm::ArmCalleeSaveLrOffset(CalleeSaveType::kSaveRefsAndArgs);  // Offset of return address.
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
   }
@@ -125,15 +131,6 @@
   static constexpr size_t kNumQuickGprArgs = 7;  // 7 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 8;  // 8 arguments passed in FPRs.
   static constexpr bool kGprFprLockstep = false;
-  // Offset of first FPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =
-      arm64::Arm64CalleeSaveFpr1Offset(CalleeSaveType::kSaveRefsAndArgs);
-  // Offset of first GPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset =
-      arm64::Arm64CalleeSaveGpr1Offset(CalleeSaveType::kSaveRefsAndArgs);
-  // Offset of return address.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset =
-      arm64::Arm64CalleeSaveLrOffset(CalleeSaveType::kSaveRefsAndArgs);
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
   }
@@ -177,9 +174,6 @@
                                                   // passed only in even numbered registers and each
                                                   // double occupies two registers.
   static constexpr bool kGprFprLockstep = false;
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 8;  // Offset of first FPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 56;  // Offset of first GPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 108;  // Offset of return address.
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
   }
@@ -221,9 +215,6 @@
   static constexpr size_t kNumQuickFprArgs = 7;  // 7 arguments passed in FPRs.
   static constexpr bool kGprFprLockstep = true;
 
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 24;  // Offset of first FPR arg (F13).
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80;  // Offset of first GPR arg (A1).
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 200;  // Offset of return address.
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
   }
@@ -254,9 +245,6 @@
   static constexpr size_t kNumQuickGprArgs = 3;  // 3 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 4;  // 4 arguments passed in FPRs.
   static constexpr bool kGprFprLockstep = false;
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 4;  // Offset of first FPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4 + 4*8;  // Offset of first GPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28 + 4*8;  // Offset of return address.
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
   }
@@ -296,9 +284,6 @@
   static constexpr size_t kNumQuickGprArgs = 5;  // 5 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 8;  // 8 arguments passed in FPRs.
   static constexpr bool kGprFprLockstep = false;
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16;  // Offset of first FPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80 + 4*8;  // Offset of first GPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168 + 4*8;  // Offset of return address.
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     switch (gpr_index) {
       case 0: return (4 * GetBytesPerGprSpillLocation(kRuntimeISA));
@@ -345,8 +330,8 @@
 
   static uint32_t GetCallingDexPc(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK((*sp)->IsCalleeSaveMethod());
-    const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA,
-                                                            CalleeSaveType::kSaveRefsAndArgs);
+    constexpr size_t callee_frame_size =
+        RuntimeCalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveRefsAndArgs);
     ArtMethod** caller_sp = reinterpret_cast<ArtMethod**>(
         reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
     uintptr_t outer_pc = QuickArgumentVisitor::GetCallingPc(sp);
@@ -354,16 +339,14 @@
     uintptr_t outer_pc_offset = current_code->NativeQuickPcOffset(outer_pc);
 
     if (current_code->IsOptimized()) {
-      CodeInfo code_info = current_code->GetOptimizedCodeInfo();
-      CodeInfoEncoding encoding = code_info.ExtractEncoding();
-      StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset, encoding);
+      CodeInfo code_info(current_code);
+      StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset);
       DCHECK(stack_map.IsValid());
-      if (stack_map.HasInlineInfo(encoding.stack_map.encoding)) {
-        InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
-        return inline_info.GetDexPcAtDepth(encoding.inline_info.encoding,
-                                           inline_info.GetDepth(encoding.inline_info.encoding)-1);
+      if (stack_map.HasInlineInfo()) {
+        InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
+        return inline_info.GetDexPcAtDepth(inline_info.GetDepth()-1);
       } else {
-        return stack_map.GetDexPc(encoding.stack_map.encoding);
+        return stack_map.GetDexPc();
       }
     } else {
       return current_code->ToDexPc(*caller_sp, outer_pc);
@@ -373,8 +356,8 @@
   static bool GetInvokeType(ArtMethod** sp, InvokeType* invoke_type, uint32_t* dex_method_index)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK((*sp)->IsCalleeSaveMethod());
-    const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA,
-                                                            CalleeSaveType::kSaveRefsAndArgs);
+    constexpr size_t callee_frame_size =
+        RuntimeCalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveRefsAndArgs);
     ArtMethod** caller_sp = reinterpret_cast<ArtMethod**>(
         reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
     uintptr_t outer_pc = QuickArgumentVisitor::GetCallingPc(sp);
@@ -383,13 +366,12 @@
       return false;
     }
     uintptr_t outer_pc_offset = current_code->NativeQuickPcOffset(outer_pc);
-    CodeInfo code_info = current_code->GetOptimizedCodeInfo();
-    CodeInfoEncoding encoding = code_info.ExtractEncoding();
+    CodeInfo code_info(current_code);
     MethodInfo method_info = current_code->GetOptimizedMethodInfo();
-    InvokeInfo invoke(code_info.GetInvokeInfoForNativePcOffset(outer_pc_offset, encoding));
+    InvokeInfo invoke(code_info.GetInvokeInfoForNativePcOffset(outer_pc_offset));
     if (invoke.IsValid()) {
-      *invoke_type = static_cast<InvokeType>(invoke.GetInvokeType(encoding.invoke_info.encoding));
-      *dex_method_index = invoke.GetMethodIndex(encoding.invoke_info.encoding, method_info);
+      *invoke_type = static_cast<InvokeType>(invoke.GetInvokeType());
+      *dex_method_index = invoke.GetMethodIndex(method_info);
       return true;
     }
     return false;
@@ -398,8 +380,9 @@
   // For the given quick ref and args quick frame, return the caller's PC.
   static uintptr_t GetCallingPc(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK((*sp)->IsCalleeSaveMethod());
-    uint8_t* lr = reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_LrOffset;
-    return *reinterpret_cast<uintptr_t*>(lr);
+    uint8_t* return_adress_spill =
+        reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_ReturnPcOffset;
+    return *reinterpret_cast<uintptr_t*>(return_adress_spill);
   }
 
   QuickArgumentVisitor(ArtMethod** sp, bool is_static, const char* shorty,
@@ -1157,8 +1140,8 @@
   CHECK(!self->IsExceptionPending()) << "Enter instrumentation exit stub with pending exception "
                                      << self->GetException()->Dump();
   // Compute address of return PC and sanity check that it currently holds 0.
-  size_t return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA,
-                                                        CalleeSaveType::kSaveEverything);
+  constexpr size_t return_pc_offset =
+      RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverything);
   uintptr_t* return_pc = reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) +
                                                       return_pc_offset);
   CHECK_EQ(*return_pc, 0U);
@@ -1210,10 +1193,10 @@
   constexpr CalleeSaveType type = CalleeSaveType::kSaveRefsAndArgs;
   CHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type));
 
-  const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type);
+  constexpr size_t callee_frame_size = RuntimeCalleeSaveFrame::GetFrameSize(type);
   auto** caller_sp = reinterpret_cast<ArtMethod**>(
       reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
-  const size_t callee_return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, type);
+  constexpr size_t callee_return_pc_offset = RuntimeCalleeSaveFrame::GetReturnPcOffset(type);
   uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(
       (reinterpret_cast<uint8_t*>(sp) + callee_return_pc_offset));
   ArtMethod* outer_method = *caller_sp;
@@ -1228,12 +1211,11 @@
   CHECK(current_code != nullptr);
   CHECK(current_code->IsOptimized());
   uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc);
-  CodeInfo code_info = current_code->GetOptimizedCodeInfo();
+  CodeInfo code_info(current_code);
   MethodInfo method_info = current_code->GetOptimizedMethodInfo();
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+  StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
   CHECK(stack_map.IsValid());
-  uint32_t dex_pc = stack_map.GetDexPc(encoding.stack_map.encoding);
+  uint32_t dex_pc = stack_map.GetDexPc();
 
   // Log the outer method and its associated dex file and class table pointer which can be used
   // to find out if the inlined methods were defined by other dex file(s) or class loader(s).
@@ -1247,20 +1229,17 @@
   LOG(FATAL_WITHOUT_ABORT) << "  instruction: " << DumpInstruction(outer_method, dex_pc);
 
   ArtMethod* caller = outer_method;
-  if (stack_map.HasInlineInfo(encoding.stack_map.encoding)) {
-    InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
-    const InlineInfoEncoding& inline_info_encoding = encoding.inline_info.encoding;
-    size_t depth = inline_info.GetDepth(inline_info_encoding);
+  if (stack_map.HasInlineInfo()) {
+    InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
+    size_t depth = inline_info.GetDepth();
     for (size_t d = 0; d < depth; ++d) {
       const char* tag = "";
-      dex_pc = inline_info.GetDexPcAtDepth(inline_info_encoding, d);
-      if (inline_info.EncodesArtMethodAtDepth(inline_info_encoding, d)) {
+      dex_pc = inline_info.GetDexPcAtDepth(d);
+      if (inline_info.EncodesArtMethodAtDepth(d)) {
         tag = "encoded ";
-        caller = inline_info.GetArtMethodAtDepth(inline_info_encoding, d);
+        caller = inline_info.GetArtMethodAtDepth(d);
       } else {
-        uint32_t method_index = inline_info.GetMethodIndexAtDepth(inline_info_encoding,
-                                                                  method_info,
-                                                                  d);
+        uint32_t method_index = inline_info.GetMethodIndexAtDepth(method_info, d);
         if (dex_pc == static_cast<uint32_t>(-1)) {
           tag = "special ";
           CHECK_EQ(d + 1u, depth);
@@ -2766,7 +2745,7 @@
   const Instruction& inst = caller_method->DexInstructions().InstructionAt(dex_pc);
   DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC ||
          inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
-  const uint32_t proto_idx = inst.VRegH();
+  const dex::ProtoIndex proto_idx(inst.VRegH());
   const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx);
   const size_t shorty_length = strlen(shorty);
   static const bool kMethodIsStatic = false;  // invoke() and invokeExact() are not static.
@@ -2789,13 +2768,6 @@
     return static_cast<uintptr_t>('V');
   }
 
-  // TODO(oth): Ensure this path isn't taken for VarHandle accessors (b/65872996).
-  DCHECK_EQ(resolved_method->GetDeclaringClass(),
-            WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_MethodHandle));
-
-  Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
-      ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
-
   Handle<mirror::MethodType> method_type(
       hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method)));
 
@@ -2835,24 +2807,43 @@
   // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in
   // consecutive order.
   RangeInstructionOperands operands(first_arg + 1, num_vregs - 1);
-  bool isExact = (jni::EncodeArtMethod(resolved_method) ==
-                  WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+  Intrinsics intrinsic = static_cast<Intrinsics>(resolved_method->GetIntrinsic());
   bool success = false;
-  if (isExact) {
-    success = MethodHandleInvokeExact(self,
+  if (resolved_method->GetDeclaringClass() == mirror::MethodHandle::StaticClass()) {
+    Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
+        ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
+    if (intrinsic == Intrinsics::kMethodHandleInvokeExact) {
+      success = MethodHandleInvokeExact(self,
+                                        *shadow_frame,
+                                        method_handle,
+                                        method_type,
+                                        &operands,
+                                        result);
+    } else {
+      DCHECK_EQ(static_cast<uint32_t>(intrinsic),
+                static_cast<uint32_t>(Intrinsics::kMethodHandleInvoke));
+      success = MethodHandleInvoke(self,
+                                   *shadow_frame,
+                                   method_handle,
+                                   method_type,
+                                   &operands,
+                                   result);
+    }
+  } else {
+    DCHECK_EQ(mirror::VarHandle::StaticClass(), resolved_method->GetDeclaringClass());
+    Handle<mirror::VarHandle> var_handle(hs.NewHandle(
+        ObjPtr<mirror::VarHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
+    mirror::VarHandle::AccessMode access_mode =
+        mirror::VarHandle::GetAccessModeByIntrinsic(intrinsic);
+    success = VarHandleInvokeAccessor(self,
                                       *shadow_frame,
-                                      method_handle,
+                                      var_handle,
                                       method_type,
+                                      access_mode,
                                       &operands,
                                       result);
-  } else {
-    success = MethodHandleInvoke(self,
-                                 *shadow_frame,
-                                 method_handle,
-                                 method_type,
-                                 &operands,
-                                 result);
   }
+
   DCHECK(success || self->IsExceptionPending());
 
   // Pop transition record.
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc
index 77b3132..89694e3 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc
@@ -54,15 +54,6 @@
     return save_method;
   }
 
-  static void CheckFrameSize(InstructionSet isa, CalleeSaveType type, uint32_t save_size)
-      NO_THREAD_SAFETY_ANALYSIS {
-    ArtMethod* save_method = CreateCalleeSaveMethod(isa, type);
-    QuickMethodFrameInfo frame_info = Runtime::Current()->GetRuntimeMethodFrameInfo(save_method);
-    EXPECT_EQ(frame_info.FrameSizeInBytes(), save_size) << "Expected and real size differs for "
-        << type << " core spills=" << std::hex << frame_info.CoreSpillMask() << " fp spills="
-        << frame_info.FpSpillMask() << std::dec << " ISA " << isa;
-  }
-
   static void CheckPCOffset(InstructionSet isa, CalleeSaveType type, size_t pc_offset)
       NO_THREAD_SAFETY_ANALYSIS {
     ArtMethod* save_method = CreateCalleeSaveMethod(isa, type);
@@ -74,79 +65,36 @@
   }
 };
 
-// Note: these tests are all runtime tests. They let the Runtime create the corresponding ArtMethod
-// and check against it. Technically we know and expect certain values, but the Runtime code is
-// not constexpr, so we cannot make this compile-time checks (and I want the Runtime code tested).
-
-// This test ensures that kQuickCalleeSaveFrame_RefAndArgs_FrameSize is correct.
-TEST_F(QuickTrampolineEntrypointsTest, FrameSize) {
-  // We have to use a define here as the callee_save_frame.h functions are constexpr.
-#define CHECK_FRAME_SIZE(isa)                                                        \
-  CheckFrameSize(isa,                                                                \
-                 CalleeSaveType::kSaveRefsAndArgs,                                   \
-                 GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveRefsAndArgs));     \
-  CheckFrameSize(isa,                                                                \
-                 CalleeSaveType::kSaveRefsOnly,                                      \
-                 GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveRefsOnly));        \
-  CheckFrameSize(isa,                                                                \
-                 CalleeSaveType::kSaveAllCalleeSaves,                                \
-                 GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveAllCalleeSaves));  \
-  CheckFrameSize(isa,                                                                \
-                 CalleeSaveType::kSaveEverything,                                    \
-                 GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveEverything));      \
-  CheckFrameSize(isa,                                                                \
-                 CalleeSaveType::kSaveEverythingForClinit,                           \
-                 GetCalleeSaveFrameSize(isa,                                         \
-                                        CalleeSaveType::kSaveEverythingForClinit));  \
-  CheckFrameSize(isa,                                                                \
-                 CalleeSaveType::kSaveEverythingForSuspendCheck,                     \
-                 GetCalleeSaveFrameSize(                                             \
-                     isa, CalleeSaveType::kSaveEverythingForSuspendCheck))
-
-  CHECK_FRAME_SIZE(InstructionSet::kArm);
-  CHECK_FRAME_SIZE(InstructionSet::kArm64);
-  CHECK_FRAME_SIZE(InstructionSet::kMips);
-  CHECK_FRAME_SIZE(InstructionSet::kMips64);
-  CHECK_FRAME_SIZE(InstructionSet::kX86);
-  CHECK_FRAME_SIZE(InstructionSet::kX86_64);
-}
-
-// This test ensures that GetConstExprPointerSize is correct with respect to
-// GetInstructionSetPointerSize.
-TEST_F(QuickTrampolineEntrypointsTest, PointerSize) {
-  EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kArm),
-            GetConstExprPointerSize(InstructionSet::kArm));
-  EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kArm64),
-            GetConstExprPointerSize(InstructionSet::kArm64));
-  EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kMips),
-            GetConstExprPointerSize(InstructionSet::kMips));
-  EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kMips64),
-            GetConstExprPointerSize(InstructionSet::kMips64));
-  EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kX86),
-            GetConstExprPointerSize(InstructionSet::kX86));
-  EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kX86_64),
-            GetConstExprPointerSize(InstructionSet::kX86_64));
-}
-
 // This test ensures that the constexpr specialization of the return PC offset computation in
 // GetCalleeSavePCOffset is correct.
 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
   // sizeof(void*), which is wrong when the target bitwidth is not the same as the host's.
-  CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveRefsAndArgs,
-                GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsAndArgs));
-  CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly,
-                GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly));
-  CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves,
-                GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves));
-  CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverything,
-                GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverything));
-  CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit,
-                GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit));
-  CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForSuspendCheck,
-                GetCalleeSaveReturnPcOffset(kRuntimeISA,
-                                            CalleeSaveType::kSaveEverythingForSuspendCheck));
+  CheckPCOffset(
+      kRuntimeISA,
+      CalleeSaveType::kSaveRefsAndArgs,
+      RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveRefsAndArgs));
+  CheckPCOffset(
+      kRuntimeISA,
+      CalleeSaveType::kSaveRefsOnly,
+      RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveRefsOnly));
+  CheckPCOffset(
+      kRuntimeISA,
+      CalleeSaveType::kSaveAllCalleeSaves,
+      RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveAllCalleeSaves));
+  CheckPCOffset(
+      kRuntimeISA,
+      CalleeSaveType::kSaveEverything,
+      RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverything));
+  CheckPCOffset(
+      kRuntimeISA,
+      CalleeSaveType::kSaveEverythingForClinit,
+      RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverythingForClinit));
+  CheckPCOffset(
+      kRuntimeISA,
+      CalleeSaveType::kSaveEverythingForSuspendCheck,
+      RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverythingForSuspendCheck));
 }
 
 }  // namespace art
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 1fdf439..1337cd5 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -183,7 +183,9 @@
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType,
                          sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveMethodHandle, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodHandle, pResolveMethodType, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodType, 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*));
diff --git a/runtime/exec_utils_test.cc b/runtime/exec_utils_test.cc
index 68edfa8..a9c1ea2 100644
--- a/runtime/exec_utils_test.cc
+++ b/runtime/exec_utils_test.cc
@@ -36,8 +36,10 @@
     command.push_back("/usr/bin/id");
   }
   std::string error_msg;
-  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
-    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+  if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
+    // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether the following code works with ASan.
     EXPECT_TRUE(Exec(command, &error_msg));
   }
   EXPECT_EQ(0U, error_msg.size()) << error_msg;
@@ -50,8 +52,10 @@
   std::vector<std::string> command;
   command.push_back("bogus");
   std::string error_msg;
-  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
-    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+  if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
+    // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether the following code works with ASan.
     EXPECT_FALSE(Exec(command, &error_msg));
     EXPECT_FALSE(error_msg.empty());
   }
@@ -72,8 +76,10 @@
   }
   command.push_back(kModifiedVariable);
   std::string error_msg;
-  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
-    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+  if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
+    // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether the following code works with ASan.
     EXPECT_FALSE(Exec(command, &error_msg));
     EXPECT_NE(0U, error_msg.size()) << error_msg;
   }
@@ -97,8 +103,10 @@
   }
   command.push_back(kDeletedVariable);
   std::string error_msg;
-  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
-    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+  if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
+    // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether the following code works with ASan.
     EXPECT_TRUE(Exec(command, &error_msg));
     EXPECT_EQ(0U, error_msg.size()) << error_msg;
   }
diff --git a/runtime/gc/accounting/bitmap.h b/runtime/gc/accounting/bitmap.h
index d039d88..2d83a8a 100644
--- a/runtime/gc/accounting/bitmap.h
+++ b/runtime/gc/accounting/bitmap.h
@@ -23,8 +23,8 @@
 #include <set>
 #include <vector>
 
+#include "base/globals.h"
 #include "base/mutex.h"
-#include "globals.h"
 
 namespace art {
 
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index 766c976..f3548f7 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -19,8 +19,8 @@
 
 #include <memory>
 
+#include "base/globals.h"
 #include "base/mutex.h"
-#include "globals.h"
 
 namespace art {
 
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index 766e0f5..7a3c06a 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -18,11 +18,11 @@
 #define ART_RUNTIME_GC_ACCOUNTING_MOD_UNION_TABLE_H_
 
 #include "base/allocator.h"
+#include "base/globals.h"
 #include "base/safe_map.h"
 #include "base/tracking_safe_map.h"
 #include "bitmap.h"
 #include "card_table.h"
-#include "globals.h"
 #include "mirror/object_reference.h"
 
 #include <set>
diff --git a/runtime/gc/accounting/read_barrier_table.h b/runtime/gc/accounting/read_barrier_table.h
index 57e4db9..4b5a8c6 100644
--- a/runtime/gc/accounting/read_barrier_table.h
+++ b/runtime/gc/accounting/read_barrier_table.h
@@ -20,10 +20,10 @@
 #include <sys/mman.h>  // For the PROT_* and MAP_* constants.
 
 #include "base/bit_utils.h"
+#include "base/globals.h"
 #include "base/mem_map.h"
 #include "base/mutex.h"
 #include "gc/space/space.h"
-#include "globals.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index 437aecc..1237f6e 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -23,8 +23,8 @@
 #include <set>
 #include <vector>
 
+#include "base/globals.h"
 #include "base/mutex.h"
-#include "globals.h"
 
 namespace art {
 
diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc
index 1ca3fd6..22529b8 100644
--- a/runtime/gc/accounting/space_bitmap_test.cc
+++ b/runtime/gc/accounting/space_bitmap_test.cc
@@ -19,9 +19,9 @@
 #include <stdint.h>
 #include <memory>
 
+#include "base/globals.h"
 #include "base/mutex.h"
 #include "common_runtime_test.h"
-#include "globals.h"
 #include "space_bitmap-inl.h"
 
 namespace art {
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 6e5cf0e..30213d5 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -30,8 +30,8 @@
 
 #include "base/allocator.h"
 #include "base/bit_utils.h"
+#include "base/globals.h"
 #include "base/mutex.h"
-#include "globals.h"
 #include "thread.h"
 
 namespace art {
@@ -625,7 +625,7 @@
 
   // 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
+  // Do not check memory when running under a memory tool. In a normal
   // build with kCheckZeroMemory the whole test should be optimized away.
   // TODO: Unprotect before checks.
   ALWAYS_INLINE bool ShouldCheckZeroMemory();
@@ -768,7 +768,7 @@
   // greater than or equal to this value, release pages.
   const size_t page_release_size_threshold_;
 
-  // Whether this allocator is running under Valgrind.
+  // Whether this allocator is running on a memory tool.
   bool is_running_on_memory_tool_;
 
   // The base address of the memory region that's managed by this allocator.
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index 6e345fb..b331e97 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -146,8 +146,8 @@
         return MarkUnevacFromSpaceRegion(from_ref, region_space_bitmap_);
       default:
         // The reference is in an unused region.
-        region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
         LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(holder, offset, from_ref);
+        region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
         heap_->GetVerification()->LogHeapCorruption(holder, offset, from_ref, /* fatal */ true);
         UNREACHABLE();
     }
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 1e136bc..681ac2e 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -39,7 +39,7 @@
 #include "gc/space/space-inl.h"
 #include "indirect_reference_table.h"
 #include "intern_table.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mark_sweep-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object-refvisitor-inl.h"
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index 508d765..ee7ac7d 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -18,8 +18,8 @@
 
 #include <android-base/logging.h>
 
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 #include <ostream>
 
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 948d233..6756868 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -272,7 +272,7 @@
     }
     case kAllocatorTypeRosAlloc: {
       if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) {
-        // If running on valgrind or asan, we should be using the instrumented path.
+        // If running on ASan, we should be using the instrumented path.
         size_t max_bytes_tl_bulk_allocated = rosalloc_space_->MaxBytesBulkAllocatedFor(alloc_size);
         if (UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type,
                                                max_bytes_tl_bulk_allocated,
@@ -303,7 +303,7 @@
     }
     case kAllocatorTypeDlMalloc: {
       if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) {
-        // If running on valgrind, we should be using the instrumented path.
+        // If running on ASan, we should be using the instrumented path.
         ret = dlmalloc_space_->Alloc(self,
                                      alloc_size,
                                      bytes_allocated,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index e85824d..12021b7 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -71,9 +71,9 @@
 #include "heap-visit-objects-inl.h"
 #include "image.h"
 #include "intern_table.h"
-#include "java_vm_ext.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
+#include "jni/java_vm_ext.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object-refvisitor-inl.h"
@@ -2248,7 +2248,8 @@
       // Add a new bin with the remaining space.
       AddBin(size - alloc_size, pos + alloc_size);
     }
-    // Copy the object over to its new location. Don't use alloc_size to avoid valgrind error.
+    // Copy the object over to its new location.
+    // Historical note: We did not use `alloc_size` to avoid a Valgrind error.
     memcpy(reinterpret_cast<void*>(forward_address), obj, obj_size);
     if (kUseBakerReadBarrier) {
       obj->AssertReadBarrierState();
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 99ebab9..609d2ab 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -27,6 +27,7 @@
 #include "allocator_type.h"
 #include "arch/instruction_set.h"
 #include "base/atomic.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "base/runtime_debug.h"
@@ -37,7 +38,6 @@
 #include "gc/collector_type.h"
 #include "gc/gc_cause.h"
 #include "gc/space/large_object_space.h"
-#include "globals.h"
 #include "handle.h"
 #include "obj_ptr.h"
 #include "offsets.h"
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 356f3ec..5be7b32 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -19,7 +19,7 @@
 #include "base/time_utils.h"
 #include "base/utils.h"
 #include "collector/garbage_collector.h"
-#include "java_vm_ext.h"
+#include "jni/java_vm_ext.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/reference-inl.h"
diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h
index 1d984eb..c6c7836 100644
--- a/runtime/gc/reference_processor.h
+++ b/runtime/gc/reference_processor.h
@@ -17,8 +17,8 @@
 #ifndef ART_RUNTIME_GC_REFERENCE_PROCESSOR_H_
 #define ART_RUNTIME_GC_REFERENCE_PROCESSOR_H_
 
+#include "base/globals.h"
 #include "base/mutex.h"
-#include "globals.h"
 #include "jni.h"
 #include "reference_queue.h"
 
diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h
index af77881..09ab51a 100644
--- a/runtime/gc/reference_queue.h
+++ b/runtime/gc/reference_queue.h
@@ -22,9 +22,9 @@
 #include <vector>
 
 #include "base/atomic.h"
+#include "base/globals.h"
 #include "base/mutex.h"
 #include "base/timing_logger.h"
-#include "globals.h"
 #include "jni.h"
 #include "obj_ptr.h"
 #include "offsets.h"
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e2154b8..dbe09e8 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1418,7 +1418,8 @@
 
     CHECK(image_header.GetOatDataBegin() != nullptr);
 
-    std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_filename,
+    std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
+                                                    oat_filename,
                                                     oat_filename,
                                                     image_header.GetOatDataBegin(),
                                                     image_header.GetOatFileBegin(),
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index fcc47d4..f202a43 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -43,7 +43,8 @@
   args.push_back("--oat-file=" + oat_location);
   ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
 
-  std::unique_ptr<OatFile> oat(OatFile::Open(oat_location.c_str(),
+  std::unique_ptr<OatFile> oat(OatFile::Open(/* zip_fd */ -1,
+                                             oat_location.c_str(),
                                              oat_location.c_str(),
                                              nullptr,
                                              nullptr,
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 512cde4..a24ca32 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -45,8 +45,9 @@
   }
 
   ~MemoryToolLargeObjectMapSpace() OVERRIDE {
-    // Keep valgrind happy if there is any large objects such as dex cache arrays which aren't
-    // freed since they are held live by the class linker.
+    // Historical note: We were deleting large objects to keep Valgrind happy if there were
+    // any large objects such as Dex cache arrays which aren't freed since they are held live
+    // by the class linker.
     MutexLock mu(Thread::Current(), lock_);
     for (auto& m : large_objects_) {
       delete m.second.mem_map;
diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h
index 8282f3d..c022171 100644
--- a/runtime/gc/space/memory_tool_malloc_space-inl.h
+++ b/runtime/gc/space/memory_tool_malloc_space-inl.h
@@ -30,11 +30,14 @@
 namespace memory_tool_details {
 
 template <size_t kMemoryToolRedZoneBytes, 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_tl_bulk_allocated,
-                                         size_t* bytes_allocated_out, size_t* usable_size_out,
-                                         size_t* bytes_tl_bulk_allocated_out) {
+inline mirror::Object* AdjustForMemoryTool(void* obj_with_rdz,
+                                           size_t num_bytes,
+                                           size_t bytes_allocated,
+                                           size_t usable_size,
+                                           size_t bytes_tl_bulk_allocated,
+                                           size_t* bytes_allocated_out,
+                                           size_t* usable_size_out,
+                                           size_t* bytes_tl_bulk_allocated_out) {
   if (bytes_allocated_out != nullptr) {
     *bytes_allocated_out = bytes_allocated;
   }
@@ -84,24 +87,31 @@
           bool kUseObjSizeForUsable>
 mirror::Object*
 MemoryToolMallocSpace<S,
-                    kMemoryToolRedZoneBytes,
-                    kAdjustForRedzoneInAllocSize,
-                    kUseObjSizeForUsable>::AllocWithGrowth(
-    Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out,
+                      kMemoryToolRedZoneBytes,
+                      kAdjustForRedzoneInAllocSize,
+                      kUseObjSizeForUsable>::AllocWithGrowth(
+    Thread* self,
+    size_t num_bytes,
+    size_t* bytes_allocated_out,
+    size_t* usable_size_out,
     size_t* bytes_tl_bulk_allocated_out) {
   size_t bytes_allocated;
   size_t usable_size;
   size_t bytes_tl_bulk_allocated;
-  void* obj_with_rdz = S::AllocWithGrowth(self, num_bytes + 2 * kMemoryToolRedZoneBytes,
-                                          &bytes_allocated, &usable_size,
+  void* obj_with_rdz = S::AllocWithGrowth(self,
+                                          num_bytes + 2 * kMemoryToolRedZoneBytes,
+                                          &bytes_allocated,
+                                          &usable_size,
                                           &bytes_tl_bulk_allocated);
   if (obj_with_rdz == nullptr) {
     return nullptr;
   }
 
-  return memory_tool_details::AdjustForValgrind<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
-      obj_with_rdz, num_bytes,
-      bytes_allocated, usable_size,
+  return memory_tool_details::AdjustForMemoryTool<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
+      obj_with_rdz,
+      num_bytes,
+      bytes_allocated,
+      usable_size,
       bytes_tl_bulk_allocated,
       bytes_allocated_out,
       usable_size_out,
@@ -113,27 +123,35 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 mirror::Object* MemoryToolMallocSpace<S,
-                                    kMemoryToolRedZoneBytes,
-                                    kAdjustForRedzoneInAllocSize,
-                                    kUseObjSizeForUsable>::Alloc(
-    Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out,
+                                      kMemoryToolRedZoneBytes,
+                                      kAdjustForRedzoneInAllocSize,
+                                      kUseObjSizeForUsable>::Alloc(
+    Thread* self,
+    size_t num_bytes,
+    size_t* bytes_allocated_out,
+    size_t* usable_size_out,
     size_t* bytes_tl_bulk_allocated_out) {
   size_t bytes_allocated;
   size_t usable_size;
   size_t bytes_tl_bulk_allocated;
-  void* obj_with_rdz = S::Alloc(self, num_bytes + 2 * kMemoryToolRedZoneBytes,
-                                &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated);
+  void* obj_with_rdz = S::Alloc(self,
+                                num_bytes + 2 * kMemoryToolRedZoneBytes,
+                                &bytes_allocated,
+                                &usable_size,
+                                &bytes_tl_bulk_allocated);
   if (obj_with_rdz == nullptr) {
     return nullptr;
   }
 
-  return memory_tool_details::AdjustForValgrind<kMemoryToolRedZoneBytes,
-                                             kUseObjSizeForUsable>(obj_with_rdz, num_bytes,
-                                                                   bytes_allocated, usable_size,
-                                                                   bytes_tl_bulk_allocated,
-                                                                   bytes_allocated_out,
-                                                                   usable_size_out,
-                                                                   bytes_tl_bulk_allocated_out);
+  return memory_tool_details::AdjustForMemoryTool<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
+      obj_with_rdz,
+      num_bytes,
+      bytes_allocated,
+      usable_size,
+      bytes_tl_bulk_allocated,
+      bytes_allocated_out,
+      usable_size_out,
+      bytes_tl_bulk_allocated_out);
 }
 
 template <typename S,
@@ -141,24 +159,31 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 mirror::Object* MemoryToolMallocSpace<S,
-                                    kMemoryToolRedZoneBytes,
-                                    kAdjustForRedzoneInAllocSize,
-                                    kUseObjSizeForUsable>::AllocThreadUnsafe(
-    Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out,
+                                      kMemoryToolRedZoneBytes,
+                                      kAdjustForRedzoneInAllocSize,
+                                      kUseObjSizeForUsable>::AllocThreadUnsafe(
+    Thread* self,
+    size_t num_bytes,
+    size_t* bytes_allocated_out,
+    size_t* usable_size_out,
     size_t* bytes_tl_bulk_allocated_out) {
   size_t bytes_allocated;
   size_t usable_size;
   size_t bytes_tl_bulk_allocated;
-  void* obj_with_rdz = S::AllocThreadUnsafe(self, num_bytes + 2 * kMemoryToolRedZoneBytes,
-                                            &bytes_allocated, &usable_size,
+  void* obj_with_rdz = S::AllocThreadUnsafe(self,
+                                            num_bytes + 2 * kMemoryToolRedZoneBytes,
+                                            &bytes_allocated,
+                                            &usable_size,
                                             &bytes_tl_bulk_allocated);
   if (obj_with_rdz == nullptr) {
     return nullptr;
   }
 
-  return memory_tool_details::AdjustForValgrind<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
-      obj_with_rdz, num_bytes,
-      bytes_allocated, usable_size,
+  return memory_tool_details::AdjustForMemoryTool<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
+      obj_with_rdz,
+      num_bytes,
+      bytes_allocated,
+      usable_size,
       bytes_tl_bulk_allocated,
       bytes_allocated_out,
       usable_size_out,
@@ -170,12 +195,14 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 size_t MemoryToolMallocSpace<S,
-                           kMemoryToolRedZoneBytes,
-                           kAdjustForRedzoneInAllocSize,
-                           kUseObjSizeForUsable>::AllocationSize(
+                             kMemoryToolRedZoneBytes,
+                             kAdjustForRedzoneInAllocSize,
+                             kUseObjSizeForUsable>::AllocationSize(
     mirror::Object* obj, size_t* usable_size) {
-  size_t result = S::AllocationSize(reinterpret_cast<mirror::Object*>(
-      reinterpret_cast<uint8_t*>(obj) - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)),
+  size_t result = S::AllocationSize(
+      reinterpret_cast<mirror::Object*>(
+          reinterpret_cast<uint8_t*>(obj)
+              - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)),
       usable_size);
   if (usable_size != nullptr) {
     if (kUseObjSizeForUsable) {
@@ -192,10 +219,9 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 size_t MemoryToolMallocSpace<S,
-                           kMemoryToolRedZoneBytes,
-                           kAdjustForRedzoneInAllocSize,
-                           kUseObjSizeForUsable>::Free(
-    Thread* self, mirror::Object* ptr) {
+                             kMemoryToolRedZoneBytes,
+                             kAdjustForRedzoneInAllocSize,
+                             kUseObjSizeForUsable>::Free(Thread* self, mirror::Object* ptr) {
   void* obj_after_rdz = reinterpret_cast<void*>(ptr);
   uint8_t* obj_with_rdz = reinterpret_cast<uint8_t*>(obj_after_rdz) - kMemoryToolRedZoneBytes;
 
@@ -220,10 +246,10 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 size_t MemoryToolMallocSpace<S,
-                           kMemoryToolRedZoneBytes,
-                           kAdjustForRedzoneInAllocSize,
-                           kUseObjSizeForUsable>::FreeList(
-    Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
+                             kMemoryToolRedZoneBytes,
+                             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]);
@@ -238,11 +264,12 @@
           bool kUseObjSizeForUsable>
 template <typename... Params>
 MemoryToolMallocSpace<S,
-                    kMemoryToolRedZoneBytes,
-                    kAdjustForRedzoneInAllocSize,
-                    kUseObjSizeForUsable>::MemoryToolMallocSpace(
-    MemMap* mem_map, size_t initial_size, Params... params) : S(mem_map, initial_size, params...) {
-  // Don't want to change the valgrind states of the mem map here as the allocator is already
+                      kMemoryToolRedZoneBytes,
+                      kAdjustForRedzoneInAllocSize,
+                      kUseObjSizeForUsable>::MemoryToolMallocSpace(
+                          MemMap* mem_map, size_t initial_size, Params... params)
+                          : S(mem_map, initial_size, params...) {
+  // Don't want to change the memory tool states of the mem map here as the allocator is already
   // initialized at this point and that may interfere with what the allocator does internally. Note
   // that the tail beyond the initial size is mprotected.
 }
@@ -252,9 +279,9 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 size_t MemoryToolMallocSpace<S,
-                           kMemoryToolRedZoneBytes,
-                           kAdjustForRedzoneInAllocSize,
-                           kUseObjSizeForUsable>::MaxBytesBulkAllocatedFor(size_t num_bytes) {
+                             kMemoryToolRedZoneBytes,
+                             kAdjustForRedzoneInAllocSize,
+                             kUseObjSizeForUsable>::MaxBytesBulkAllocatedFor(size_t num_bytes) {
   return S::MaxBytesBulkAllocatedFor(num_bytes + 2 * kMemoryToolRedZoneBytes);
 }
 
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 74abe1c..0701330 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -34,7 +34,7 @@
 static constexpr bool kProtectClearedRegions = kIsTargetBuild;
 
 // Wether we poison memory areas occupied by dead objects in unevacuated regions.
-static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = kIsDebugBuild;
+static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = true;
 
 // Special 32-bit value used to poison memory areas occupied by dead
 // objects in unevacuated regions. Dereferencing this value is expected
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index ab18b1b..a129171 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -36,7 +36,7 @@
 // region space, but from the last allocated region. This allocation
 // strategy reduces region reuse and should help catch some GC bugs
 // earlier.
-static constexpr bool kCyclicRegionAllocation = kIsDebugBuild;
+static constexpr bool kCyclicRegionAllocation = true;
 
 // A space that consists of equal-sized regions.
 class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
@@ -299,10 +299,17 @@
    public:
     Region()
         : idx_(static_cast<size_t>(-1)),
-          begin_(nullptr), top_(nullptr), end_(nullptr),
-          state_(RegionState::kRegionStateAllocated), type_(RegionType::kRegionTypeToSpace),
-          objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)),
-          is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {}
+          live_bytes_(static_cast<size_t>(-1)),
+          begin_(nullptr),
+          thread_(nullptr),
+          top_(nullptr),
+          end_(nullptr),
+          objects_allocated_(0),
+          alloc_time_(0),
+          is_newly_allocated_(false),
+          is_a_tlab_(false),
+          state_(RegionState::kRegionStateAllocated),
+          type_(RegionType::kRegionTypeToSpace) {}
 
     void Init(size_t idx, uint8_t* begin, uint8_t* end) {
       idx_ = idx;
@@ -496,22 +503,22 @@
 
    private:
     size_t idx_;                        // The region's index in the region space.
+    size_t live_bytes_;                 // The live bytes. Used to compute the live percent.
     uint8_t* begin_;                    // The begin address of the region.
+    Thread* thread_;                    // The owning thread if it's a tlab.
     // Note that `top_` can be higher than `end_` in the case of a
     // large region, where an allocated object spans multiple regions
     // (large region + one or more large tail regions).
     Atomic<uint8_t*> top_;              // The current position of the allocation.
     uint8_t* end_;                      // The end address of the region.
-    RegionState state_;                 // The region state (see RegionState).
-    RegionType type_;                   // The region type (see RegionType).
     Atomic<size_t> objects_allocated_;  // The number of objects allocated.
     uint32_t alloc_time_;               // The allocation time of the region.
     // Note that newly allocated and evacuated regions use -1 as
     // special value for `live_bytes_`.
-    size_t live_bytes_;                 // The live bytes. Used to compute the live percent.
     bool is_newly_allocated_;           // True if it's allocated after the last collection.
     bool is_a_tlab_;                    // True if it's a tlab.
-    Thread* thread_;                    // The owning thread if it's a tlab.
+    RegionState state_;                 // The region state (see RegionState).
+    RegionType type_;                   // The region type (see RegionType).
 
     friend class RegionSpace;
   };
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index e786536..d698cf2 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -77,7 +77,7 @@
 
   // Everything is set so record in immutable structure and leave
   uint8_t* begin = mem_map->Begin();
-  // TODO: Fix RosAllocSpace to support Valgrind/ASan. There is currently some issues with
+  // TODO: Fix RosAllocSpace to support ASan. There is currently some issues with
   // AllocationSize caused by redzones. b/12944686
   if (running_on_memory_tool) {
     return new MemoryToolMallocSpace<RosAllocSpace, kDefaultMemoryToolRedZoneBytes, false, true>(
@@ -382,12 +382,12 @@
   size_t size = obj->SizeOf<kVerifyNone>();
   bool add_redzones = false;
   if (kMaybeIsRunningOnMemoryTool) {
-    add_redzones = RUNNING_ON_MEMORY_TOOL ? kMemoryToolAddsRedzones : 0;
+    add_redzones = kRunningOnMemoryTool ? kMemoryToolAddsRedzones : 0;
     if (add_redzones) {
       size += 2 * kDefaultMemoryToolRedZoneBytes;
     }
   } else {
-    DCHECK_EQ(RUNNING_ON_MEMORY_TOOL, 0U);
+    DCHECK(!kRunningOnMemoryTool);
   }
   size_t size_by_size = rosalloc_->UsableSize(size);
   if (kIsDebugBuild) {
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index 9d16b87..4c17233 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -159,8 +159,8 @@
 
   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,
-                          RUNNING_ON_MEMORY_TOOL != 0);
+    return CreateRosAlloc(
+        base, morecore_start, initial_size, maximum_size, low_memory_mode, kRunningOnMemoryTool);
   }
   static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size,
                                              size_t maximum_size, bool low_memory_mode,
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index d888935..4f43d9f 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -21,12 +21,12 @@
 #include <string>
 
 #include "base/atomic.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/mem_map.h"
 #include "base/mutex.h"
 #include "gc/accounting/space_bitmap.h"
 #include "gc/collector/object_byte_pair.h"
-#include "globals.h"
 
 namespace art {
 namespace mirror {
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index 1fe3fb2..d5861ed 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -20,8 +20,8 @@
 #include <stdint.h>
 #include <memory>
 
+#include "base/globals.h"
 #include "common_runtime_test.h"
-#include "globals.h"
 #include "mirror/array-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
diff --git a/runtime/gc/task_processor.h b/runtime/gc/task_processor.h
index f6b5607..6db3c37 100644
--- a/runtime/gc/task_processor.h
+++ b/runtime/gc/task_processor.h
@@ -20,8 +20,8 @@
 #include <memory>
 #include <set>
 
+#include "base/globals.h"
 #include "base/mutex.h"
-#include "globals.h"
 #include "thread_pool.h"
 
 namespace art {
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 46630db..464c2b7 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -90,16 +90,24 @@
 DEFINE_CHECK_EQ(static_cast<size_t>(MIN_LARGE_OBJECT_THRESHOLD), (static_cast<size_t>(art::gc::Heap::kMinLargeObjectThreshold)))
 #define LOCK_WORD_STATE_SHIFT 30
 DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_STATE_SHIFT), (static_cast<int32_t>(art::LockWord::kStateShift)))
-#define LOCK_WORD_STATE_MASK 0xc0000000
-DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_STATE_MASK), (static_cast<uint32_t>(art::LockWord::kStateMaskShifted)))
+#define LOCK_WORD_STATE_MASK_SHIFTED 0xc0000000
+DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_STATE_MASK_SHIFTED), (static_cast<uint32_t>(art::LockWord::kStateMaskShifted)))
 #define LOCK_WORD_READ_BARRIER_STATE_SHIFT 28
 DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_READ_BARRIER_STATE_SHIFT), (static_cast<int32_t>(art::LockWord::kReadBarrierStateShift)))
 #define LOCK_WORD_READ_BARRIER_STATE_MASK 0x10000000
 DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_READ_BARRIER_STATE_MASK), (static_cast<uint32_t>(art::LockWord::kReadBarrierStateMaskShifted)))
 #define LOCK_WORD_READ_BARRIER_STATE_MASK_TOGGLED 0xefffffff
 DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_READ_BARRIER_STATE_MASK_TOGGLED), (static_cast<uint32_t>(art::LockWord::kReadBarrierStateMaskShiftedToggled)))
-#define LOCK_WORD_THIN_LOCK_COUNT_ONE 65536
-DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_THIN_LOCK_COUNT_ONE), (static_cast<int32_t>(art::LockWord::kThinLockCountOne)))
+#define LOCK_WORD_THIN_LOCK_COUNT_SIZE 12
+DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_THIN_LOCK_COUNT_SIZE), (static_cast<int32_t>(art::LockWord::kThinLockCountSize)))
+#define LOCK_WORD_THIN_LOCK_COUNT_SHIFT 16
+DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_THIN_LOCK_COUNT_SHIFT), (static_cast<int32_t>(art::LockWord::kThinLockCountShift)))
+#define LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED 0xfff0000
+DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED), (static_cast<uint32_t>(art::LockWord::kThinLockCountMaskShifted)))
+#define LOCK_WORD_THIN_LOCK_COUNT_ONE 0x10000
+DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_THIN_LOCK_COUNT_ONE), (static_cast<uint32_t>(art::LockWord::kThinLockCountOne)))
+#define LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED 0xffff
+DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED), (static_cast<uint32_t>(art::LockWord::kThinLockOwnerMaskShifted)))
 #define LOCK_WORD_STATE_FORWARDING_ADDRESS 0x3
 DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_STATE_FORWARDING_ADDRESS), (static_cast<uint32_t>(art::LockWord::kStateForwardingAddress)))
 #define LOCK_WORD_STATE_FORWARDING_ADDRESS_OVERFLOW 0x40000000
@@ -110,6 +118,8 @@
 DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_GC_STATE_MASK_SHIFTED), (static_cast<uint32_t>(art::LockWord::kGCStateMaskShifted)))
 #define LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED 0xcfffffff
 DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED), (static_cast<uint32_t>(art::LockWord::kGCStateMaskShiftedToggled)))
+#define LOCK_WORD_GC_STATE_SIZE 2
+DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_GC_STATE_SIZE), (static_cast<int32_t>(art::LockWord::kGCStateSize)))
 #define LOCK_WORD_GC_STATE_SHIFT 28
 DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_GC_STATE_SHIFT), (static_cast<int32_t>(art::LockWord::kGCStateShift)))
 #define LOCK_WORD_MARK_BIT_SHIFT 29
diff --git a/runtime/globals.h b/runtime/globals.h
deleted file mode 100644
index bdc2177..0000000
--- a/runtime/globals.h
+++ /dev/null
@@ -1,23 +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.
- */
-
-#ifndef ART_RUNTIME_GLOBALS_H_
-#define ART_RUNTIME_GLOBALS_H_
-
-// TODO: remove this file in favor of libartbase/base/globals.h
-#include "base/globals.h"
-
-#endif  // ART_RUNTIME_GLOBALS_H_
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 0e72f27..e41d1d3 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -22,11 +22,31 @@
 #include "thread-current-inl.h"
 #include "well_known_classes.h"
 
+#ifdef ART_TARGET_ANDROID
+#include <metricslogger/metrics_logger.h>
+using android::metricslogger::ComplexEventLogger;
+using android::metricslogger::ACTION_HIDDEN_API_ACCESSED;
+using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD;
+using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED;
+using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE;
+#endif
+
 namespace art {
 namespace hiddenapi {
 
+// Set to true if we should always print a warning in logcat for all hidden API accesses, not just
+// dark grey and black. This can be set to true for developer preview / beta builds, but should be
+// false for public release builds.
+// Note that when flipping this flag, you must also update the expectations of test 674-hiddenapi
+// as it affects whether or not we warn for light grey APIs that have been added to the exemptions
+// list.
+static constexpr bool kLogAllAccesses = false;
+
 static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
   switch (value) {
+    case kNone:
+      LOG(FATAL) << "Internal access to hidden API should not be logged";
+      UNREACHABLE();
     case kReflection:
       os << "reflection";
       break;
@@ -46,42 +66,47 @@
 
 // GetMemberAction-related static_asserts.
 static_assert(
-    EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) &&
     EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
     EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
     "Mismatch between EnforcementPolicy and ApiList enums");
 static_assert(
-    EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList &&
+    EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList &&
     EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
     "EnforcementPolicy values ordering not correct");
 
 namespace detail {
 
 MemberSignature::MemberSignature(ArtField* field) {
-  member_type_ = "field";
-  signature_parts_ = {
-    field->GetDeclaringClass()->GetDescriptor(&tmp_),
-    "->",
-    field->GetName(),
-    ":",
-    field->GetTypeDescriptor()
-  };
+  class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_);
+  member_name_ = field->GetName();
+  type_signature_ = field->GetTypeDescriptor();
+  type_ = kField;
 }
 
 MemberSignature::MemberSignature(ArtMethod* method) {
-  member_type_ = "method";
-  signature_parts_ = {
-    method->GetDeclaringClass()->GetDescriptor(&tmp_),
-    "->",
-    method->GetName(),
-    method->GetSignature().ToString()
-  };
+  // If this is a proxy method, print the signature of the interface method.
+  method = method->GetInterfaceMethodIfProxy(
+      Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+
+  class_name_ = method->GetDeclaringClass()->GetDescriptor(&tmp_);
+  member_name_ = method->GetName();
+  type_signature_ = method->GetSignature().ToString();
+  type_ = kMethod;
+}
+
+inline std::vector<const char*> MemberSignature::GetSignatureParts() const {
+  if (type_ == kField) {
+    return { class_name_.c_str(), "->", member_name_.c_str(), ":", type_signature_.c_str() };
+  } else {
+    DCHECK_EQ(type_, kMethod);
+    return { class_name_.c_str(), "->", member_name_.c_str(), type_signature_.c_str() };
+  }
 }
 
 bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const {
   size_t pos = 0;
-  for (const std::string& part : signature_parts_) {
-    size_t count = std::min(prefix.length() - pos, part.length());
+  for (const char* part : GetSignatureParts()) {
+    size_t count = std::min(prefix.length() - pos, strlen(part));
     if (prefix.compare(pos, count, part, 0, count) == 0) {
       pos += count;
     } else {
@@ -103,42 +128,124 @@
 }
 
 void MemberSignature::Dump(std::ostream& os) const {
-  for (std::string part : signature_parts_) {
+  for (const char* part : GetSignatureParts()) {
     os << part;
   }
 }
 
 void MemberSignature::WarnAboutAccess(AccessMethod access_method,
                                       HiddenApiAccessFlags::ApiList list) {
-  LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable<MemberSignature>(*this)
-               << " (" << list << ", " << access_method << ")";
+  LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ")
+               << Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")";
+}
+#ifdef ART_TARGET_ANDROID
+// Convert an AccessMethod enum to a value for logging from the proto enum.
+// This method may look odd (the enum values are current the same), but it
+// prevents coupling the internal enum to the proto enum (which should never
+// be changed) so that we are free to change the internal one if necessary in
+// future.
+inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
+  switch (access_method) {
+    case kNone:
+      return android::metricslogger::ACCESS_METHOD_NONE;
+    case kReflection:
+      return android::metricslogger::ACCESS_METHOD_REFLECTION;
+    case kJNI:
+      return android::metricslogger::ACCESS_METHOD_JNI;
+    case kLinking:
+      return android::metricslogger::ACCESS_METHOD_LINKING;
+    default:
+      DCHECK(false);
+  }
+}
+#endif
+
+void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) {
+#ifdef ART_TARGET_ANDROID
+  if (access_method == kLinking || access_method == kNone) {
+    // Linking warnings come from static analysis/compilation of the bytecode
+    // and can contain false positives (i.e. code that is never run). We choose
+    // not to log these in the event log.
+    // None does not correspond to actual access, so should also be ignored.
+    return;
+  }
+  ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED);
+  log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method));
+  if (action_taken == kDeny) {
+    log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
+  }
+  std::ostringstream signature_str;
+  Dump(signature_str);
+  log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str());
+  log_maker.Record();
+#else
+  UNUSED(access_method);
+  UNUSED(action_taken);
+#endif
+}
+
+static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
+  return true;
+}
+
+static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtMethod* method) {
+  return !method->IsIntrinsic();
 }
 
 template<typename T>
-Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) {
+static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (CanUpdateMemberAccessFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) {
+    member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+        member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+  }
+}
+
+template<typename T>
+Action GetMemberActionImpl(T* member,
+                           HiddenApiAccessFlags::ApiList api_list,
+                           Action action,
+                           AccessMethod access_method) {
+  DCHECK_NE(action, kAllow);
+
   // Get the signature, we need it later.
   MemberSignature member_signature(member);
 
   Runtime* runtime = Runtime::Current();
 
-  if (action == kDeny) {
-    // If we were about to deny, check for an exemption first.
-    // Exempted APIs are treated as light grey list.
+  // Check for an exemption first. Exempted APIs are treated as white list.
+  // We only do this if we're about to deny, or if the app is debuggable. This is because:
+  // - we only print a warning for light greylist violations for debuggable apps
+  // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs.
+  // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever
+  //   possible.
+  const bool shouldWarn = kLogAllAccesses || runtime->IsJavaDebuggable();
+  if (shouldWarn || action == kDeny) {
     if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
-      action = kAllowButWarn;
+      action = kAllow;
       // Avoid re-examining the exemption list next time.
-      // Note this results in the warning below showing "light greylist", which
-      // seems like what one would expect. Exemptions effectively add new members to
-      // the light greylist.
-      member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
-              member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist));
+      // Note this results in no warning for the member, which seems like what one would expect.
+      // Exemptions effectively adds new members to the whitelist.
+      MaybeWhitelistMember(runtime, member);
+      return kAllow;
+    }
+
+    if (access_method != kNone) {
+      // Print a log message with information about this class member access.
+      // We do this if we're about to block access, or the app is debuggable.
+      member_signature.WarnAboutAccess(access_method, api_list);
     }
   }
 
-  // Print a log message with information about this class member access.
-  // We do this regardless of whether we block the access or not.
-  member_signature.WarnAboutAccess(access_method,
-      HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags()));
+  if (kIsTargetBuild && !kIsTargetLinux) {
+    uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
+    // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
+    static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
+    if (eventLogSampleRate != 0 &&
+        (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
+      member_signature.LogAccessToEventLog(access_method, action);
+    }
+  }
 
   if (action == kDeny) {
     // Block access
@@ -148,16 +255,16 @@
   // Allow access to this member but print a warning.
   DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
 
-  // Depending on a runtime flag, we might move the member into whitelist and
-  // skip the warning the next time the member is accessed.
-  if (runtime->ShouldDedupeHiddenApiWarnings()) {
-    member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
-        member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
-  }
+  if (access_method != kNone) {
+    // Depending on a runtime flag, we might move the member into whitelist and
+    // skip the warning the next time the member is accessed.
+    MaybeWhitelistMember(runtime, member);
 
-  // If this action requires a UI warning, set the appropriate flag.
-  if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
-    runtime->SetPendingHiddenApiWarning(true);
+    // If this action requires a UI warning, set the appropriate flag.
+    if (shouldWarn &&
+        (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag())) {
+      runtime->SetPendingHiddenApiWarning(true);
+    }
   }
 
   return action;
@@ -165,9 +272,11 @@
 
 // Need to instantiate this.
 template Action GetMemberActionImpl<ArtField>(ArtField* member,
+                                              HiddenApiAccessFlags::ApiList api_list,
                                               Action action,
                                               AccessMethod access_method);
 template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member,
+                                               HiddenApiAccessFlags::ApiList api_list,
                                                Action action,
                                                AccessMethod access_method);
 }  // namespace detail
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index ffdeacb..580224e 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -33,7 +33,7 @@
 // frameworks/base/core/java/android/content/pm/ApplicationInfo.java
 enum class EnforcementPolicy {
   kNoChecks             = 0,
-  kAllLists             = 1,  // ban anything but whitelist
+  kJustWarn             = 1,  // keep checks enabled, but allow everything (enables logging)
   kDarkGreyAndBlackList = 2,  // ban dark grey & blacklist
   kBlacklistOnly        = 3,  // ban blacklist violations only
   kMax = kBlacklistOnly,
@@ -53,22 +53,37 @@
 };
 
 enum AccessMethod {
+  kNone,  // internal test that does not correspond to an actual access by app
   kReflection,
   kJNI,
   kLinking,
 };
 
-inline Action GetActionFromAccessFlags(uint32_t access_flags) {
+// Do not change the values of items in this enum, as they are written to the
+// event log for offline analysis. Any changes will interfere with that analysis.
+enum AccessContextFlags {
+  // Accessed member is a field if this bit is set, else a method
+  kMemberIsField = 1 << 0,
+  // Indicates if access was denied to the member, instead of just printing a warning.
+  kAccessDenied  = 1 << 1,
+};
+
+inline Action GetActionFromAccessFlags(HiddenApiAccessFlags::ApiList api_list) {
+  if (api_list == HiddenApiAccessFlags::kWhitelist) {
+    return kAllow;
+  }
+
   EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
   if (policy == EnforcementPolicy::kNoChecks) {
     // Exit early. Nothing to enforce.
     return kAllow;
   }
 
-  HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags);
-  if (api_list == HiddenApiAccessFlags::kWhitelist) {
-    return kAllow;
+  // if policy is "just warn", always warn. We returned above for whitelist APIs.
+  if (policy == EnforcementPolicy::kJustWarn) {
+    return kAllowButWarn;
   }
+  DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList);
   // The logic below relies on equality of values in the enums EnforcementPolicy and
   // HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc.
   if (static_cast<int>(policy) > static_cast<int>(api_list)) {
@@ -80,6 +95,22 @@
   }
 }
 
+class ScopedHiddenApiEnforcementPolicySetting {
+ public:
+  explicit ScopedHiddenApiEnforcementPolicySetting(EnforcementPolicy new_policy)
+      : initial_policy_(Runtime::Current()->GetHiddenApiEnforcementPolicy()) {
+    Runtime::Current()->SetHiddenApiEnforcementPolicy(new_policy);
+  }
+
+  ~ScopedHiddenApiEnforcementPolicySetting() {
+    Runtime::Current()->SetHiddenApiEnforcementPolicy(initial_policy_);
+  }
+
+ private:
+  const EnforcementPolicy initial_policy_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiEnforcementPolicySetting);
+};
+
 // Implementation details. DO NOT ACCESS DIRECTLY.
 namespace detail {
 
@@ -87,9 +118,18 @@
 // is used as a helper when matching prefixes, and when logging the signature.
 class MemberSignature {
  private:
-  std::string member_type_;
-  std::vector<std::string> signature_parts_;
+  enum MemberType {
+    kField,
+    kMethod,
+  };
+
+  std::string class_name_;
+  std::string member_name_;
+  std::string type_signature_;
   std::string tmp_;
+  MemberType type_;
+
+  inline std::vector<const char*> GetSignatureParts() const;
 
  public:
   explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -105,45 +145,72 @@
   bool IsExempted(const std::vector<std::string>& exemptions);
 
   void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list);
+
+  void LogAccessToEventLog(AccessMethod access_method, Action action_taken);
 };
 
 template<typename T>
-Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method)
+Action GetMemberActionImpl(T* member,
+                           HiddenApiAccessFlags::ApiList api_list,
+                           Action action,
+                           AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Returns true if the caller is either loaded by the boot strap class loader or comes from
 // a dex file located in ${ANDROID_ROOT}/framework/.
 ALWAYS_INLINE
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader,
-                                  ObjPtr<mirror::DexCache> caller_dex_cache)
+inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller,
+                            ObjPtr<mirror::ClassLoader> caller_class_loader,
+                            ObjPtr<mirror::DexCache> caller_dex_cache)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (caller_class_loader.IsNull()) {
+    // Boot class loader.
     return true;
-  } else if (caller_dex_cache.IsNull()) {
-    return false;
-  } else {
-    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
-    return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile();
   }
+
+  if (!caller_dex_cache.IsNull()) {
+    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
+    if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) {
+      // Caller is in a platform dex file.
+      return true;
+    }
+  }
+
+  if (!caller.IsNull() &&
+      caller->ShouldSkipHiddenApiChecks() &&
+      Runtime::Current()->IsJavaDebuggable()) {
+    // We are in debuggable mode and this caller has been marked trusted.
+    return true;
+  }
+
+  return false;
 }
 
 }  // namespace detail
 
 // Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is in the
-// platform or not. Because different users of this function determine this
-// in a different way, `fn_caller_in_platform(self)` is called and should
-// return true if the caller is located in the platform.
+// reflective query. The decision is based on whether the caller is trusted or
+// not. Because different users of this function determine this in a different
+// way, `fn_caller_is_trusted(self)` is called and should return true if the
+// caller is allowed to access the platform.
 // This function might print warnings into the log if the member is hidden.
 template<typename T>
 inline Action GetMemberAction(T* member,
                               Thread* self,
-                              std::function<bool(Thread*)> fn_caller_in_platform,
+                              std::function<bool(Thread*)> fn_caller_is_trusted,
                               AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
 
-  Action action = GetActionFromAccessFlags(member->GetAccessFlags());
+  // Decode hidden API access flags.
+  // NB Multiple threads might try to access (and overwrite) these simultaneously,
+  // causing a race. We only do that if access has not been denied, so the race
+  // cannot change Java semantics. We should, however, decode the access flags
+  // once and use it throughout this function, otherwise we may get inconsistent
+  // results, e.g. print whitelist warnings (b/78327881).
+  HiddenApiAccessFlags::ApiList api_list = member->GetHiddenApiAccessFlags();
+
+  Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags());
   if (action == kAllow) {
     // Nothing to do.
     return action;
@@ -151,19 +218,18 @@
 
   // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
   // This can be *very* expensive. Save it for last.
-  if (fn_caller_in_platform(self)) {
-    // Caller in the platform. Exit.
+  if (fn_caller_is_trusted(self)) {
+    // Caller is trusted. Exit.
     return kAllow;
   }
 
   // Member is hidden and caller is not in the platform.
-  return detail::GetMemberActionImpl(member, action, access_method);
+  return detail::GetMemberActionImpl(member, api_list, action, access_method);
 }
 
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
+inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) {
   return !caller.IsNull() &&
-      detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
+      detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache());
 }
 
 // Returns true if access to `member` should be denied to a caller loaded with
@@ -175,10 +241,11 @@
                               ObjPtr<mirror::DexCache> caller_dex_cache,
                               AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
+  bool is_caller_trusted =
+      detail::IsCallerTrusted(/* caller */ nullptr, caller_class_loader, caller_dex_cache);
   return GetMemberAction(member,
                          /* thread */ nullptr,
-                         [caller_in_platform] (Thread*) { return caller_in_platform; },
+                         [is_caller_trusted] (Thread*) { return is_caller_trusted; },
                          access_method);
 }
 
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 5a31dd4..ab0c290 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -17,11 +17,13 @@
 #include "hidden_api.h"
 
 #include "common_runtime_test.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
+#include "proxy_test.h"
 
 namespace art {
 
 using hiddenapi::detail::MemberSignature;
+using hiddenapi::GetActionFromAccessFlags;
 
 class HiddenApiTest : public CommonRuntimeTest {
  protected:
@@ -30,7 +32,7 @@
     CommonRuntimeTest::SetUp();
     self_ = Thread::Current();
     self_->TransitionFromSuspendedToRunnable();
-    LoadDex("HiddenApiSignatures");
+    jclass_loader_ = LoadDex("HiddenApiSignatures");
     bool started = runtime_->Start();
     CHECK(started);
 
@@ -68,6 +70,7 @@
 
  protected:
   Thread* self_;
+  jobject jclass_loader_;
   ArtField* class1_field1_;
   ArtField* class1_field12_;
   ArtMethod* class1_init_;
@@ -84,6 +87,44 @@
   ArtMethod* class3_method1_i_;
 };
 
+TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
+  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), hiddenapi::kAllow);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), hiddenapi::kAllow);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), hiddenapi::kAllow);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), hiddenapi::kAllow);
+
+  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist),
+            hiddenapi::kAllow);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist),
+            hiddenapi::kAllowButWarn);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist),
+            hiddenapi::kAllowButWarn);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist),
+            hiddenapi::kAllowButWarn);
+
+  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist),
+            hiddenapi::kAllow);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist),
+            hiddenapi::kAllowButWarn);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist),
+            hiddenapi::kDeny);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist),
+            hiddenapi::kDeny);
+
+  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist),
+            hiddenapi::kAllow);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist),
+            hiddenapi::kAllowButWarn);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist),
+            hiddenapi::kAllowButWarnAndToast);
+  ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist),
+            hiddenapi::kDeny);
+}
+
 TEST_F(HiddenApiTest, CheckMembersRead) {
   ASSERT_NE(nullptr, class1_field1_);
   ASSERT_NE(nullptr, class1_field12_);
@@ -272,4 +313,56 @@
   ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
 }
 
+TEST_F(HiddenApiTest, CheckMemberSignatureForProxyClass) {
+  ScopedObjectAccess soa(self_);
+  StackHandleScope<4> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader_)));
+
+  // Find interface we will create a proxy for.
+  Handle<mirror::Class> h_iface(hs.NewHandle(
+      class_linker_->FindClass(soa.Self(), "Lmypackage/packagea/Interface;", class_loader)));
+  ASSERT_TRUE(h_iface != nullptr);
+
+  // Create the proxy class.
+  std::vector<mirror::Class*> interfaces;
+  interfaces.push_back(h_iface.Get());
+  Handle<mirror::Class> proxyClass = hs.NewHandle(proxy_test::GenerateProxyClass(
+      soa, jclass_loader_, runtime_->GetClassLinker(), "$Proxy1234", interfaces));
+  ASSERT_TRUE(proxyClass != nullptr);
+  ASSERT_TRUE(proxyClass->IsProxyClass());
+  ASSERT_TRUE(proxyClass->IsInitialized());
+
+  // Find the "method" virtual method.
+  ArtMethod* method = nullptr;
+  for (auto& m : proxyClass->GetDeclaredVirtualMethods(kRuntimePointerSize)) {
+    if (strcmp("method", m.GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetName()) == 0) {
+      method = &m;
+      break;
+    }
+  }
+  ASSERT_TRUE(method != nullptr);
+
+  // Find the "interfaces" static field. This is generated for all proxies.
+  ArtField* field = nullptr;
+  for (size_t i = 0; i < proxyClass->NumStaticFields(); ++i) {
+    ArtField* f = proxyClass->GetStaticField(i);
+    if (strcmp("interfaces", f->GetName()) == 0) {
+      field = f;
+      break;
+    }
+  }
+  ASSERT_TRUE(field != nullptr);
+
+  // Test the signature. We expect the signature from the interface class.
+  std::ostringstream ss_method;
+  MemberSignature(method).Dump(ss_method);
+  ASSERT_EQ("Lmypackage/packagea/Interface;->method()V", ss_method.str());
+
+  // Test the signature. We expect the signature of the proxy class.
+  std::ostringstream ss_field;
+  MemberSignature(field).Dump(ss_field);
+  ASSERT_EQ("L$Proxy1234;->interfaces:[Ljava/lang/Class;", ss_field.str());
+}
+
 }  // namespace art
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index aa716f1..3b64874 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -42,6 +42,7 @@
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/array_ref.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "base/os.h"
@@ -59,7 +60,6 @@
 #include "gc/scoped_gc_critical_section.h"
 #include "gc/space/space.h"
 #include "gc_root.h"
-#include "globals.h"
 #include "jdwp/jdwp.h"
 #include "jdwp/jdwp_priv.h"
 #include "mirror/class-inl.h"
diff --git a/runtime/image.h b/runtime/image.h
index fe544cc..8acd5bc 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -20,7 +20,7 @@
 #include <string.h>
 
 #include "base/enums.h"
-#include "globals.h"
+#include "base/globals.h"
 #include "mirror/object.h"
 
 namespace art {
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 6143ba6..950a54d 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -19,8 +19,8 @@
 #include "base/mutator_locked_dumpable.h"
 #include "base/systrace.h"
 #include "base/utils.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "nth_caller_visitor.h"
 #include "reference_table.h"
 #include "runtime.h"
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 836bbe7..3171eeb 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -24,7 +24,7 @@
 #include "dex/dex_file.h"
 #include "gc/scoped_gc_critical_section.h"
 #include "handle_scope-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "jvalue.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 8a85ee4..5a50ec5 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -24,7 +24,7 @@
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "intrinsics_enum.h"
 #include "jit/jit.h"
-#include "jvalue.h"
+#include "jvalue-inl.h"
 #include "method_handles-inl.h"
 #include "method_handles.h"
 #include "mirror/array-inl.h"
@@ -37,6 +37,7 @@
 #include "stack.h"
 #include "thread-inl.h"
 #include "transaction.h"
+#include "var_handles.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -626,7 +627,8 @@
 
   // The vRegH value gives the index of the proto_id associated with this
   // signature polymorphic call site.
-  const uint32_t callsite_proto_id = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
+  const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
+  const dex::ProtoIndex callsite_proto_id(vRegH);
 
   // Call through to the classlinker and ask it to resolve the static type associated
   // with the callsite. This information is stored in the dex cache so it's
@@ -724,38 +726,6 @@
   }
 }
 
-static bool DoVarHandleInvokeChecked(Thread* self,
-                                     Handle<mirror::VarHandle> var_handle,
-                                     Handle<mirror::MethodType> callsite_type,
-                                     mirror::VarHandle::AccessMode access_mode,
-                                     ShadowFrame& shadow_frame,
-                                     InstructionOperands* operands,
-                                     JValue* result)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType()
-  // which is only required if we need to convert argument and/or
-  // return types.
-  StackHandleScope<1> hs(self);
-  Handle<mirror::MethodType> accessor_type(hs.NewHandle(
-      var_handle->GetMethodTypeForAccessMode(self, access_mode)));
-  const size_t num_vregs = accessor_type->NumberOfVRegs();
-  const int num_params = accessor_type->GetPTypes()->GetLength();
-  ShadowFrameAllocaUniquePtr accessor_frame =
-      CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
-  ShadowFrameGetter getter(shadow_frame, operands);
-  static const uint32_t kFirstDestinationReg = 0;
-  ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
-  if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) {
-    return false;
-  }
-  RangeInstructionOperands accessor_operands(kFirstDestinationReg,
-                                             kFirstDestinationReg + num_vregs);
-  if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) {
-    return false;
-  }
-  return ConvertReturnValue(callsite_type, accessor_type, result);
-}
-
 static bool DoVarHandleInvokeCommon(Thread* self,
                                     ShadowFrame& shadow_frame,
                                     const Instruction* inst,
@@ -768,59 +738,43 @@
     return false;
   }
 
-  bool is_var_args = inst->HasVarArgs();
-  const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc();
-  ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC));
-  if (receiver.IsNull()) {
-    ThrowNullPointerExceptionFromDexPC();
-    return false;
-  }
-
   StackHandleScope<2> hs(self);
-  Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr())));
-  if (!var_handle->IsAccessModeSupported(access_mode)) {
-    ThrowUnsupportedOperationException();
-    return false;
-  }
-
-  const uint32_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc();
+  bool is_var_args = inst->HasVarArgs();
+  const uint16_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc();
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
   Handle<mirror::MethodType> callsite_type(hs.NewHandle(
-      class_linker->ResolveMethodType(self, vRegH, shadow_frame.GetMethod())));
+      class_linker->ResolveMethodType(self, dex::ProtoIndex(vRegH), shadow_frame.GetMethod())));
   // This implies we couldn't resolve one or more types in this VarHandle.
   if (UNLIKELY(callsite_type == nullptr)) {
     CHECK(self->IsExceptionPending());
     return false;
   }
 
-  if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) {
-    ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode),
-                                  callsite_type.Get());
-    return false;
-  }
-
+  const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc();
+  ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC));
+  Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr())));
   if (is_var_args) {
     uint32_t args[Instruction::kMaxVarArgRegs];
     inst->GetVarArgs(args, inst_data);
     VarArgsInstructionOperands all_operands(args, inst->VRegA_45cc());
     NoReceiverInstructionOperands operands(&all_operands);
-    return DoVarHandleInvokeChecked(self,
-                                    var_handle,
-                                    callsite_type,
-                                    access_mode,
-                                    shadow_frame,
-                                    &operands,
-                                    result);
+    return VarHandleInvokeAccessor(self,
+                                   shadow_frame,
+                                   var_handle,
+                                   callsite_type,
+                                   access_mode,
+                                   &operands,
+                                   result);
   } else {
     RangeInstructionOperands all_operands(inst->VRegC_4rcc(), inst->VRegA_4rcc());
     NoReceiverInstructionOperands operands(&all_operands);
-    return DoVarHandleInvokeChecked(self,
-                                    var_handle,
-                                    callsite_type,
-                                    access_mode,
-                                    shadow_frame,
-                                    &operands,
-                                    result);
+    return VarHandleInvokeAccessor(self,
+                                   shadow_frame,
+                                   var_handle,
+                                   callsite_type,
+                                   access_mode,
+                                   &operands,
+                                   result);
   }
 }
 
@@ -965,9 +919,10 @@
       StackHandleScope<2> hs(self);
       Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
       Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
-      uint32_t index = static_cast<uint32_t>(encoded_value->GetI());
+      dex::ProtoIndex proto_idx(encoded_value->GetC());
       ClassLinker* cl = Runtime::Current()->GetClassLinker();
-      ObjPtr<mirror::MethodType> o = cl->ResolveMethodType(self, index, dex_cache, class_loader);
+      ObjPtr<mirror::MethodType> o =
+          cl->ResolveMethodType(self, proto_idx, dex_cache, class_loader);
       if (UNLIKELY(o.IsNull())) {
         DCHECK(self->IsExceptionPending());
         return false;
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 0818e06..67a0349 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -217,7 +217,7 @@
 }
 
 static inline ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self,
-                                                           uint32_t method_type_index,
+                                                           dex::ProtoIndex method_type_index,
                                                            ArtMethod* referrer)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 283885e..5c7838c 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -565,7 +565,7 @@
         PREAMBLE();
         ClassLinker* cl = Runtime::Current()->GetClassLinker();
         ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self,
-                                                              inst->VRegB_21c(),
+                                                              dex::ProtoIndex(inst->VRegB_21c()),
                                                               shadow_frame.GetMethod());
         if (UNLIKELY(mt == nullptr)) {
           HANDLE_PENDING_EXCEPTION();
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 2a9ef2c..1b39a74 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -408,7 +408,8 @@
                                        ShadowFrame* shadow_frame,
                                        Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  ObjPtr<mirror::MethodType> mt = ResolveMethodType(self, index, shadow_frame->GetMethod());
+  ObjPtr<mirror::MethodType> mt =
+      ResolveMethodType(self, dex::ProtoIndex(index), shadow_frame->GetMethod());
   if (UNLIKELY(mt == nullptr)) {
     return true;
   }
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 791ebf0..0e429a6 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -510,7 +510,7 @@
     result->SetZ(false);
     return;
   }
-  mirror::String* class_name = nullptr;
+  ObjPtr<mirror::String> class_name = nullptr;
   if (!annotations::GetInnerClass(klass, &class_name)) {
     result->SetZ(false);
     return;
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index fd43562..aeb5f4b 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -864,11 +864,6 @@
 }
 
 TEST_F(UnstartedRuntimeTest, Pow) {
-  // Valgrind seems to get this wrong, actually. Disable for valgrind.
-  if (RUNNING_ON_MEMORY_TOOL != 0 && kMemoryToolIsValgrind) {
-    return;
-  }
-
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
 
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index 510f5f0..df1eb2b 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -17,7 +17,7 @@
 #include "object_registry.h"
 
 #include "handle_scope-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class.h"
 #include "mirror/throwable.h"
 #include "obj_ptr-inl.h"
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 813430f..736729c 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -27,11 +27,13 @@
 #include "debugger.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "interpreter/interpreter.h"
-#include "java_vm_ext.h"
 #include "jit_code_cache.h"
+#include "jni/java_vm_ext.h"
+#include "mirror/method_handle_impl.h"
+#include "mirror/var_handle.h"
 #include "oat_file_manager.h"
 #include "oat_quick_method_header.h"
-#include "profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 #include "profile_saver.h"
 #include "runtime.h"
 #include "runtime_options.h"
@@ -44,8 +46,6 @@
 namespace jit {
 
 static constexpr bool kEnableOnStackReplacement = true;
-// At what priority to schedule jit threads. 9 is the lowest foreground priority on device.
-static constexpr int kJitPoolThreadPthreadPriority = 9;
 
 // Different compilation threshold constants. These can be overridden on the command line.
 static constexpr size_t kJitDefaultCompileThreshold           = 10000;  // Non-debug default.
@@ -78,6 +78,8 @@
       options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown);
   jit_options->profile_saver_options_ =
       options.GetOrDefault(RuntimeArgumentMap::ProfileSaverOpts);
+  jit_options->thread_pool_pthread_priority_ =
+      options.GetOrDefault(RuntimeArgumentMap::JITPoolThreadPthreadPriority);
 
   if (options.Exists(RuntimeArgumentMap::JITCompileThreshold)) {
     jit_options->compile_threshold_ = *options.Get(RuntimeArgumentMap::JITCompileThreshold);
@@ -165,34 +167,27 @@
   cumulative_timings_.AddLogger(logger);
 }
 
-Jit::Jit() : dump_info_on_shutdown_(false),
-             cumulative_timings_("JIT timings"),
-             memory_use_("Memory used for compilation", 16),
-             lock_("JIT memory use lock"),
-             use_jit_compilation_(true),
-             hot_method_threshold_(0),
-             warm_method_threshold_(0),
-             osr_method_threshold_(0),
-             priority_thread_weight_(0),
-             invoke_transition_weight_(0) {}
+Jit::Jit(JitOptions* options) : options_(options),
+                                cumulative_timings_("JIT timings"),
+                                memory_use_("Memory used for compilation", 16),
+                                lock_("JIT memory use lock") {}
 
 Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
   DCHECK(options->UseJitCompilation() || options->GetProfileSaverOptions().IsEnabled());
-  std::unique_ptr<Jit> jit(new Jit);
-  jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown();
+  std::unique_ptr<Jit> jit(new Jit(options));
   if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
     return nullptr;
   }
+  bool code_cache_only_for_profile_data = !options->UseJitCompilation();
   jit->code_cache_.reset(JitCodeCache::Create(
       options->GetCodeCacheInitialCapacity(),
       options->GetCodeCacheMaxCapacity(),
       jit->generate_debug_info_,
+      code_cache_only_for_profile_data,
       error_msg));
   if (jit->GetCodeCache() == nullptr) {
     return nullptr;
   }
-  jit->use_jit_compilation_ = options->UseJitCompilation();
-  jit->profile_saver_options_ = options->GetProfileSaverOptions();
   VLOG(jit) << "JIT created with initial_capacity="
       << PrettySize(options->GetCodeCacheInitialCapacity())
       << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity())
@@ -200,12 +195,6 @@
       << ", profile_saver_options=" << options->GetProfileSaverOptions();
 
 
-  jit->hot_method_threshold_ = options->GetCompileThreshold();
-  jit->warm_method_threshold_ = options->GetWarmupThreshold();
-  jit->osr_method_threshold_ = options->GetOsrThreshold();
-  jit->priority_thread_weight_ = options->GetPriorityThreadWeight();
-  jit->invoke_transition_weight_ = options->GetInvokeTransitionWeight();
-
   jit->CreateThreadPool();
 
   // Notify native debugger about the classes already loaded before the creation of the jit.
@@ -326,7 +315,7 @@
   constexpr bool kJitPoolNeedsPeers = true;
   thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers));
 
-  thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
+  thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority());
   Start();
 }
 
@@ -343,7 +332,7 @@
     }
 
     // When running sanitized, let all tasks finish to not leak. Otherwise just clear the queue.
-    if (!RUNNING_ON_MEMORY_TOOL) {
+    if (!kRunningOnMemoryTool) {
       pool->StopWorkers(self);
       pool->RemoveAllTasks(self);
     }
@@ -356,8 +345,8 @@
 
 void Jit::StartProfileSaver(const std::string& filename,
                             const std::vector<std::string>& code_paths) {
-  if (profile_saver_options_.IsEnabled()) {
-    ProfileSaver::Start(profile_saver_options_,
+  if (options_->GetSaveProfilingInfo()) {
+    ProfileSaver::Start(options_->GetProfileSaverOptions(),
                         filename,
                         code_cache_.get(),
                         code_paths);
@@ -365,8 +354,8 @@
 }
 
 void Jit::StopProfileSaver() {
-  if (profile_saver_options_.IsEnabled() && ProfileSaver::IsStarted()) {
-    ProfileSaver::Stop(dump_info_on_shutdown_);
+  if (options_->GetSaveProfilingInfo() && ProfileSaver::IsStarted()) {
+    ProfileSaver::Stop(options_->DumpJitInfoOnShutdown());
   }
 }
 
@@ -379,8 +368,8 @@
 }
 
 Jit::~Jit() {
-  DCHECK(!profile_saver_options_.IsEnabled() || !ProfileSaver::IsStarted());
-  if (dump_info_on_shutdown_) {
+  DCHECK(!options_->GetSaveProfilingInfo() || !ProfileSaver::IsStarted());
+  if (options_->DumpJitInfoOnShutdown()) {
     DumpInfo(LOG_STREAM(INFO));
     Runtime::Current()->DumpDeoptimizations(LOG_STREAM(INFO));
   }
@@ -484,11 +473,10 @@
       return false;
     }
 
-    CodeInfo code_info = osr_method->GetOptimizedCodeInfo();
-    CodeInfoEncoding encoding = code_info.ExtractEncoding();
+    CodeInfo code_info(osr_method);
 
     // Find stack map starting at the target dex_pc.
-    StackMap stack_map = code_info.GetOsrStackMapForDexPc(dex_pc + dex_pc_offset, encoding);
+    StackMap stack_map = code_info.GetOsrStackMapForDexPc(dex_pc + dex_pc_offset);
     if (!stack_map.IsValid()) {
       // There is no OSR stack map for this dex pc offset. Just return to the interpreter in the
       // hope that the next branch has one.
@@ -505,7 +493,7 @@
     // We found a stack map, now fill the frame with dex register values from the interpreter's
     // shadow frame.
     DexRegisterMap vreg_map =
-        code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs);
+        code_info.GetDexRegisterMapOf(stack_map, number_of_vregs);
 
     frame_size = osr_method->GetFrameSizeInBytes();
 
@@ -527,7 +515,7 @@
     } else {
       for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
         DexRegisterLocation::Kind location =
-            vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding);
+            vreg_map.GetLocationKind(vreg, number_of_vregs, code_info);
         if (location == DexRegisterLocation::Kind::kNone) {
           // Dex register is dead or uninitialized.
           continue;
@@ -543,15 +531,14 @@
         int32_t vreg_value = shadow_frame->GetVReg(vreg);
         int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg,
                                                              number_of_vregs,
-                                                             code_info,
-                                                             encoding);
+                                                             code_info);
         DCHECK_LT(slot_offset, static_cast<int32_t>(frame_size));
         DCHECK_GT(slot_offset, 0);
         (reinterpret_cast<int32_t*>(memory))[slot_offset / sizeof(int32_t)] = vreg_value;
       }
     }
 
-    native_pc = stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA) +
+    native_pc = stack_map.GetNativePcOffset(kRuntimeISA) +
         osr_method->GetEntryPoint();
     VLOG(jit) << "Jumping to "
               << method_name
@@ -638,36 +625,54 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
 };
 
+static bool IgnoreSamplesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (method->IsClassInitializer() || !method->IsCompilable()) {
+    // We do not want to compile such methods.
+    return true;
+  }
+  if (method->IsNative()) {
+    ObjPtr<mirror::Class> klass = method->GetDeclaringClass();
+    if (klass == mirror::MethodHandle::StaticClass() || klass == mirror::VarHandle::StaticClass()) {
+      // MethodHandle and VarHandle invocation methods are required to throw an
+      // UnsupportedOperationException if invoked reflectively. We achieve this by having native
+      // implementations that arise the exception. We need to disable JIT compilation of these JNI
+      // methods as it can lead to transitioning between JIT compiled JNI stubs and generic JNI
+      // stubs. Since these stubs have different stack representations we can then crash in stack
+      // walking (b/78151261).
+      return true;
+    }
+  }
+  return false;
+}
+
 void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) {
   if (thread_pool_ == nullptr) {
     // Should only see this when shutting down.
     DCHECK(Runtime::Current()->IsShuttingDown(self));
     return;
   }
-
-  if (method->IsClassInitializer() || !method->IsCompilable()) {
-    // We do not want to compile such methods.
+  if (IgnoreSamplesForMethod(method)) {
     return;
   }
-  if (hot_method_threshold_ == 0) {
+  if (HotMethodThreshold() == 0) {
     // Tests might request JIT on first use (compiled synchronously in the interpreter).
     return;
   }
   DCHECK(thread_pool_ != nullptr);
-  DCHECK_GT(warm_method_threshold_, 0);
-  DCHECK_GT(hot_method_threshold_, warm_method_threshold_);
-  DCHECK_GT(osr_method_threshold_, hot_method_threshold_);
-  DCHECK_GE(priority_thread_weight_, 1);
-  DCHECK_LE(priority_thread_weight_, hot_method_threshold_);
+  DCHECK_GT(WarmMethodThreshold(), 0);
+  DCHECK_GT(HotMethodThreshold(), WarmMethodThreshold());
+  DCHECK_GT(OSRMethodThreshold(), HotMethodThreshold());
+  DCHECK_GE(PriorityThreadWeight(), 1);
+  DCHECK_LE(PriorityThreadWeight(), HotMethodThreshold());
 
-  int32_t starting_count = method->GetCounter();
+  uint16_t starting_count = method->GetCounter();
   if (Jit::ShouldUsePriorityThreadWeight(self)) {
-    count *= priority_thread_weight_;
+    count *= PriorityThreadWeight();
   }
-  int32_t new_count = starting_count + count;   // int32 here to avoid wrap-around;
+  uint32_t new_count = starting_count + count;
   // Note: Native method have no "warm" state or profiling info.
-  if (LIKELY(!method->IsNative()) && starting_count < warm_method_threshold_) {
-    if ((new_count >= warm_method_threshold_) &&
+  if (LIKELY(!method->IsNative()) && starting_count < WarmMethodThreshold()) {
+    if ((new_count >= WarmMethodThreshold()) &&
         (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) {
       bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false);
       if (success) {
@@ -688,23 +693,23 @@
       }
     }
     // Avoid jumping more than one state at a time.
-    new_count = std::min(new_count, hot_method_threshold_ - 1);
-  } else if (use_jit_compilation_) {
-    if (starting_count < hot_method_threshold_) {
-      if ((new_count >= hot_method_threshold_) &&
+    new_count = std::min(new_count, static_cast<uint32_t>(HotMethodThreshold() - 1));
+  } else if (UseJitCompilation()) {
+    if (starting_count < HotMethodThreshold()) {
+      if ((new_count >= HotMethodThreshold()) &&
           !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
         DCHECK(thread_pool_ != nullptr);
         thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
       }
       // Avoid jumping more than one state at a time.
-      new_count = std::min(new_count, osr_method_threshold_ - 1);
-    } else if (starting_count < osr_method_threshold_) {
+      new_count = std::min(new_count, static_cast<uint32_t>(OSRMethodThreshold() - 1));
+    } else if (starting_count < OSRMethodThreshold()) {
       if (!with_backedges) {
         // If the samples don't contain any back edge, we don't increment the hotness.
         return;
       }
       DCHECK(!method->IsNative());  // No back edges reported for native methods.
-      if ((new_count >= osr_method_threshold_) &&  !code_cache_->IsOsrCompiled(method)) {
+      if ((new_count >= OSRMethodThreshold()) &&  !code_cache_->IsOsrCompiled(method)) {
         DCHECK(thread_pool_ != nullptr);
         thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
       }
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 6d27cfe..edaf348 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -44,6 +44,110 @@
 
 static constexpr int16_t kJitCheckForOSR = -1;
 static constexpr int16_t kJitHotnessDisabled = -2;
+// At what priority to schedule jit threads. 9 is the lowest foreground priority on device.
+// See android/os/Process.java.
+static constexpr int kJitPoolThreadPthreadDefaultPriority = 9;
+
+class JitOptions {
+ public:
+  static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options);
+
+  uint16_t GetCompileThreshold() const {
+    return compile_threshold_;
+  }
+
+  uint16_t GetWarmupThreshold() const {
+    return warmup_threshold_;
+  }
+
+  uint16_t GetOsrThreshold() const {
+    return osr_threshold_;
+  }
+
+  uint16_t GetPriorityThreadWeight() const {
+    return priority_thread_weight_;
+  }
+
+  uint16_t GetInvokeTransitionWeight() const {
+    return invoke_transition_weight_;
+  }
+
+  size_t GetCodeCacheInitialCapacity() const {
+    return code_cache_initial_capacity_;
+  }
+
+  size_t GetCodeCacheMaxCapacity() const {
+    return code_cache_max_capacity_;
+  }
+
+  bool DumpJitInfoOnShutdown() const {
+    return dump_info_on_shutdown_;
+  }
+
+  const ProfileSaverOptions& GetProfileSaverOptions() const {
+    return profile_saver_options_;
+  }
+
+  bool GetSaveProfilingInfo() const {
+    return profile_saver_options_.IsEnabled();
+  }
+
+  int GetThreadPoolPthreadPriority() const {
+    return thread_pool_pthread_priority_;
+  }
+
+  bool UseJitCompilation() const {
+    return use_jit_compilation_;
+  }
+
+  void SetUseJitCompilation(bool b) {
+    use_jit_compilation_ = b;
+  }
+
+  void SetSaveProfilingInfo(bool save_profiling_info) {
+    profile_saver_options_.SetEnabled(save_profiling_info);
+  }
+
+  void SetWaitForJitNotificationsToSaveProfile(bool value) {
+    profile_saver_options_.SetWaitForJitNotificationsToSave(value);
+  }
+
+  void SetProfileAOTCode(bool value) {
+    profile_saver_options_.SetProfileAOTCode(value);
+  }
+
+  void SetJitAtFirstUse() {
+    use_jit_compilation_ = true;
+    compile_threshold_ = 0;
+  }
+
+ private:
+  bool use_jit_compilation_;
+  size_t code_cache_initial_capacity_;
+  size_t code_cache_max_capacity_;
+  uint16_t compile_threshold_;
+  uint16_t warmup_threshold_;
+  uint16_t osr_threshold_;
+  uint16_t priority_thread_weight_;
+  uint16_t invoke_transition_weight_;
+  bool dump_info_on_shutdown_;
+  int thread_pool_pthread_priority_;
+  ProfileSaverOptions profile_saver_options_;
+
+  JitOptions()
+      : use_jit_compilation_(false),
+        code_cache_initial_capacity_(0),
+        code_cache_max_capacity_(0),
+        compile_threshold_(0),
+        warmup_threshold_(0),
+        osr_threshold_(0),
+        priority_thread_weight_(0),
+        invoke_transition_weight_(0),
+        dump_info_on_shutdown_(false),
+        thread_pool_pthread_priority_(kJitPoolThreadPthreadDefaultPriority) {}
+
+  DISALLOW_COPY_AND_ASSIGN(JitOptions);
+};
 
 class Jit {
  public:
@@ -77,29 +181,29 @@
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  size_t OSRMethodThreshold() const {
-    return osr_method_threshold_;
+  uint16_t OSRMethodThreshold() const {
+    return options_->GetOsrThreshold();
   }
 
-  size_t HotMethodThreshold() const {
-    return hot_method_threshold_;
+  uint16_t HotMethodThreshold() const {
+    return options_->GetCompileThreshold();
   }
 
-  size_t WarmMethodThreshold() const {
-    return warm_method_threshold_;
+  uint16_t WarmMethodThreshold() const {
+    return options_->GetWarmupThreshold();
   }
 
   uint16_t PriorityThreadWeight() const {
-    return priority_thread_weight_;
+    return options_->GetPriorityThreadWeight();
   }
 
   // Returns false if we only need to save profile information and not compile methods.
   bool UseJitCompilation() const {
-    return use_jit_compilation_;
+    return options_->UseJitCompilation();
   }
 
   bool GetSaveProfilingInfo() const {
-    return profile_saver_options_.IsEnabled();
+    return options_->GetSaveProfilingInfo();
   }
 
   // Wait until there is no more pending compilation tasks.
@@ -120,12 +224,12 @@
 
   void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    AddSamples(self, caller, invoke_transition_weight_, false);
+    AddSamples(self, caller, options_->GetInvokeTransitionWeight(), false);
   }
 
   void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    AddSamples(self, callee, invoke_transition_weight_, false);
+    AddSamples(self, callee, options_->GetInvokeTransitionWeight(), false);
   }
 
   // Starts the profile saver if the config options allow profile recording.
@@ -177,7 +281,7 @@
   void Start();
 
  private:
-  Jit();
+  explicit Jit(JitOptions* options);
 
   static bool LoadCompiler(std::string* error_msg);
 
@@ -189,100 +293,22 @@
   static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool);
   static void (*jit_types_loaded_)(void*, mirror::Class**, size_t count);
 
+  // We make this static to simplify the interaction with libart-compiler.so.
+  static bool generate_debug_info_;
+
+  const JitOptions* const options_;
+
+  std::unique_ptr<jit::JitCodeCache> code_cache_;
+  std::unique_ptr<ThreadPool> thread_pool_;
+
   // Performance monitoring.
-  bool dump_info_on_shutdown_;
   CumulativeLogger cumulative_timings_;
   Histogram<uint64_t> memory_use_ GUARDED_BY(lock_);
   Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 
-  std::unique_ptr<jit::JitCodeCache> code_cache_;
-
-  bool use_jit_compilation_;
-  ProfileSaverOptions profile_saver_options_;
-  static bool generate_debug_info_;
-  uint16_t hot_method_threshold_;
-  uint16_t warm_method_threshold_;
-  uint16_t osr_method_threshold_;
-  uint16_t priority_thread_weight_;
-  uint16_t invoke_transition_weight_;
-  std::unique_ptr<ThreadPool> thread_pool_;
-
   DISALLOW_COPY_AND_ASSIGN(Jit);
 };
 
-class JitOptions {
- public:
-  static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options);
-  size_t GetCompileThreshold() const {
-    return compile_threshold_;
-  }
-  size_t GetWarmupThreshold() const {
-    return warmup_threshold_;
-  }
-  size_t GetOsrThreshold() const {
-    return osr_threshold_;
-  }
-  uint16_t GetPriorityThreadWeight() const {
-    return priority_thread_weight_;
-  }
-  size_t GetInvokeTransitionWeight() const {
-    return invoke_transition_weight_;
-  }
-  size_t GetCodeCacheInitialCapacity() const {
-    return code_cache_initial_capacity_;
-  }
-  size_t GetCodeCacheMaxCapacity() const {
-    return code_cache_max_capacity_;
-  }
-  bool DumpJitInfoOnShutdown() const {
-    return dump_info_on_shutdown_;
-  }
-  const ProfileSaverOptions& GetProfileSaverOptions() const {
-    return profile_saver_options_;
-  }
-  bool GetSaveProfilingInfo() const {
-    return profile_saver_options_.IsEnabled();
-  }
-  bool UseJitCompilation() const {
-    return use_jit_compilation_;
-  }
-  void SetUseJitCompilation(bool b) {
-    use_jit_compilation_ = b;
-  }
-  void SetSaveProfilingInfo(bool save_profiling_info) {
-    profile_saver_options_.SetEnabled(save_profiling_info);
-  }
-  void SetJitAtFirstUse() {
-    use_jit_compilation_ = true;
-    compile_threshold_ = 0;
-  }
-
- private:
-  bool use_jit_compilation_;
-  size_t code_cache_initial_capacity_;
-  size_t code_cache_max_capacity_;
-  size_t compile_threshold_;
-  size_t warmup_threshold_;
-  size_t osr_threshold_;
-  uint16_t priority_thread_weight_;
-  size_t invoke_transition_weight_;
-  bool dump_info_on_shutdown_;
-  ProfileSaverOptions profile_saver_options_;
-
-  JitOptions()
-      : use_jit_compilation_(false),
-        code_cache_initial_capacity_(0),
-        code_cache_max_capacity_(0),
-        compile_threshold_(0),
-        warmup_threshold_(0),
-        osr_threshold_(0),
-        priority_thread_weight_(0),
-        invoke_transition_weight_(0),
-        dump_info_on_shutdown_(false) {}
-
-  DISALLOW_COPY_AND_ASSIGN(JitOptions);
-};
-
 // Helper class to stop the JIT for a given scope. This will wait for the JIT to quiesce.
 class ScopedJitSuspend {
  public:
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 6dcc871..d8aa00c 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -41,7 +41,7 @@
 #include "oat_file-inl.h"
 #include "oat_quick_method_header.h"
 #include "object_callbacks.h"
-#include "profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread-current-inl.h"
@@ -50,7 +50,6 @@
 namespace art {
 namespace jit {
 
-static constexpr int kProtAll = PROT_READ | PROT_WRITE | PROT_EXEC;
 static constexpr int kProtData = PROT_READ | PROT_WRITE;
 static constexpr int kProtCode = PROT_READ | PROT_EXEC;
 
@@ -161,6 +160,7 @@
 JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
                                    size_t max_capacity,
                                    bool generate_debug_info,
+                                   bool used_only_for_profile_data,
                                    std::string* error_msg) {
   ScopedTrace trace(__PRETTY_FUNCTION__);
   CHECK_GE(max_capacity, initial_capacity);
@@ -184,6 +184,15 @@
     return nullptr;
   }
 
+  // Decide how we should map the code and data sections.
+  // If we use the code cache just for profiling we do not need to map the code section as
+  // executable.
+  // NOTE 1: this is yet another workaround to bypass strict SElinux policies in order to be able
+  //         to profile system server.
+  // NOTE 2: We could just not create the code section at all but we will need to
+  //         special case too many cases.
+  int memmap_flags_prot_code = used_only_for_profile_data ? (kProtCode & ~PROT_EXEC) : kProtCode;
+
   std::string error_str;
   // Map name specific for android_os_Debug.cpp accounting.
   // Map in low 4gb to simplify accessing root tables for x86_64.
@@ -216,8 +225,11 @@
   DCHECK_EQ(code_size + data_size, max_capacity);
   uint8_t* divider = data_map->Begin() + data_size;
 
-  MemMap* code_map =
-      data_map->RemapAtEnd(divider, "jit-code-cache", kProtAll, &error_str, use_ashmem);
+  MemMap* code_map = data_map->RemapAtEnd(
+      divider,
+      "jit-code-cache",
+      memmap_flags_prot_code | PROT_WRITE,
+      &error_str, use_ashmem);
   if (code_map == nullptr) {
     std::ostringstream oss;
     oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity;
@@ -229,7 +241,13 @@
   code_size = initial_capacity - data_size;
   DCHECK_EQ(code_size + data_size, initial_capacity);
   return new JitCodeCache(
-      code_map, data_map.release(), code_size, data_size, max_capacity, garbage_collect_code);
+      code_map,
+      data_map.release(),
+      code_size,
+      data_size,
+      max_capacity,
+      garbage_collect_code,
+      memmap_flags_prot_code);
 }
 
 JitCodeCache::JitCodeCache(MemMap* code_map,
@@ -237,7 +255,8 @@
                            size_t initial_code_capacity,
                            size_t initial_data_capacity,
                            size_t max_capacity,
-                           bool garbage_collect_code)
+                           bool garbage_collect_code,
+                           int memmap_flags_prot_code)
     : lock_("Jit code cache", kJitCodeCacheLock),
       lock_cond_("Jit code cache condition variable", lock_),
       collection_in_progress_(false),
@@ -258,7 +277,8 @@
       histogram_code_memory_use_("Memory used for compiled code", 16),
       histogram_profiling_info_memory_use_("Memory used for profiling info", 16),
       is_weak_access_enabled_(true),
-      inline_cache_cond_("Jit inline cache condition variable", lock_) {
+      inline_cache_cond_("Jit inline cache condition variable", lock_),
+      memmap_flags_prot_code_(memmap_flags_prot_code) {
 
   DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
   code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/);
@@ -274,7 +294,7 @@
               "mprotect jit code cache",
               code_map_->Begin(),
               code_map_->Size(),
-              kProtCode);
+              memmap_flags_prot_code_);
   CheckedCall(mprotect,
               "mprotect jit data cache",
               data_map_->Begin(),
@@ -327,19 +347,30 @@
 
 class ScopedCodeCacheWrite : ScopedTrace {
  public:
-  explicit ScopedCodeCacheWrite(MemMap* code_map)
+  explicit ScopedCodeCacheWrite(const JitCodeCache* const code_cache)
       : ScopedTrace("ScopedCodeCacheWrite"),
-        code_map_(code_map) {
+        code_cache_(code_cache) {
     ScopedTrace trace("mprotect all");
-    CheckedCall(mprotect, "make code writable", code_map_->Begin(), code_map_->Size(), kProtAll);
+    CheckedCall(
+        mprotect,
+        "make code writable",
+        code_cache_->code_map_->Begin(),
+        code_cache_->code_map_->Size(),
+        code_cache_->memmap_flags_prot_code_ | PROT_WRITE);
   }
+
   ~ScopedCodeCacheWrite() {
     ScopedTrace trace("mprotect code");
-    CheckedCall(mprotect, "make code protected", code_map_->Begin(), code_map_->Size(), kProtCode);
+    CheckedCall(
+        mprotect,
+        "make code protected",
+        code_cache_->code_map_->Begin(),
+        code_cache_->code_map_->Size(),
+        code_cache_->memmap_flags_prot_code_);
   }
 
  private:
-  MemMap* const code_map_;
+  const JitCodeCache* const code_cache_;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedCodeCacheWrite);
 };
@@ -532,7 +563,7 @@
   }
 }
 
-void JitCodeCache::FreeCode(const void* code_ptr) {
+void JitCodeCache::FreeCodeAndData(const void* code_ptr) {
   uintptr_t allocation = FromCodeToAllocation(code_ptr);
   // Notify native debugger that we are about to remove the code.
   // It does nothing if we are not using native debugger.
@@ -557,9 +588,9 @@
   // so it's possible for the same method_header to start representing
   // different compile code.
   MutexLock mu(Thread::Current(), lock_);
-  ScopedCodeCacheWrite scc(code_map_.get());
+  ScopedCodeCacheWrite scc(this);
   for (const OatQuickMethodHeader* method_header : method_headers) {
-    FreeCode(method_header->GetCode());
+    FreeCodeAndData(method_header->GetCode());
   }
 }
 
@@ -576,7 +607,7 @@
     // with the classlinker_classes_lock_ held, and suspending ourselves could
     // lead to a deadlock.
     {
-      ScopedCodeCacheWrite scc(code_map_.get());
+      ScopedCodeCacheWrite scc(this);
       for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) {
         it->second.RemoveMethodsIn(alloc);
         if (it->second.GetMethods().empty()) {
@@ -715,7 +746,7 @@
     MutexLock mu(self, lock_);
     WaitForPotentialCollectionToComplete(self);
     {
-      ScopedCodeCacheWrite scc(code_map_.get());
+      ScopedCodeCacheWrite scc(this);
       memory = AllocateCode(total_size);
       if (memory == nullptr) {
         return nullptr;
@@ -878,14 +909,14 @@
   }
 
   bool in_cache = false;
-  ScopedCodeCacheWrite ccw(code_map_.get());
+  ScopedCodeCacheWrite ccw(this);
   if (UNLIKELY(method->IsNative())) {
     auto it = jni_stubs_map_.find(JniStubKey(method));
     if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) {
       in_cache = true;
       if (it->second.GetMethods().empty()) {
         if (release_memory) {
-          FreeCode(it->second.GetCode());
+          FreeCodeAndData(it->second.GetCode());
         }
         jni_stubs_map_.erase(it);
       } else {
@@ -897,7 +928,7 @@
       if (it->second == method) {
         in_cache = true;
         if (release_memory) {
-          FreeCode(it->first);
+          FreeCodeAndData(it->first);
         }
         it = method_code_map_.erase(it);
       } else {
@@ -1105,7 +1136,7 @@
   DCHECK_EQ(per_space_footprint * 2, new_footprint);
   mspace_set_footprint_limit(data_mspace_, per_space_footprint);
   {
-    ScopedCodeCacheWrite scc(code_map_.get());
+    ScopedCodeCacheWrite scc(this);
     mspace_set_footprint_limit(code_mspace_, per_space_footprint);
   }
 }
@@ -1273,7 +1304,7 @@
   std::unordered_set<OatQuickMethodHeader*> method_headers;
   {
     MutexLock mu(self, lock_);
-    ScopedCodeCacheWrite scc(code_map_.get());
+    ScopedCodeCacheWrite scc(this);
     // Iterate over all compiled code and remove entries that are not marked.
     for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) {
       JniStubData* data = &it->second;
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index f1c99fb..958e8e8 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -68,6 +68,7 @@
 namespace jit {
 
 class JitInstrumentationCache;
+class ScopedCodeCacheWrite;
 
 // Alignment in bits that will suit all architectures.
 static constexpr int kJitCodeAlignment = 16;
@@ -88,6 +89,7 @@
   static JitCodeCache* Create(size_t initial_capacity,
                               size_t max_capacity,
                               bool generate_debug_info,
+                              bool used_only_for_profile_data,
                               std::string* error_msg);
   ~JitCodeCache();
 
@@ -270,7 +272,8 @@
                size_t initial_code_capacity,
                size_t initial_data_capacity,
                size_t max_capacity,
-               bool garbage_collect_code);
+               bool garbage_collect_code,
+               int memmap_flags_prot_code);
 
   // Internal version of 'CommitCode' that will not retry if the
   // allocation fails. Return null if the allocation fails.
@@ -314,8 +317,8 @@
       REQUIRES(lock_)
       REQUIRES(Locks::mutator_lock_);
 
-  // Free in the mspace allocations for `code_ptr`.
-  void FreeCode(const void* code_ptr) REQUIRES(lock_);
+  // Free code and data allocations for `code_ptr`.
+  void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_);
 
   // Number of bytes allocated in the code cache.
   size_t CodeCacheSizeLocked() REQUIRES(lock_);
@@ -354,10 +357,10 @@
       REQUIRES(lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void FreeCode(uint8_t* code) REQUIRES(lock_);
   uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_);
-  void FreeData(uint8_t* data) REQUIRES(lock_);
+  void FreeCode(uint8_t* code) REQUIRES(lock_);
   uint8_t* AllocateData(size_t data_size) REQUIRES(lock_);
+  void FreeData(uint8_t* data) REQUIRES(lock_);
 
   bool IsWeakAccessEnabled(Thread* self) const;
   void WaitUntilInlineCacheAccessible(Thread* self)
@@ -442,7 +445,12 @@
   // Condition to wait on for accessing inline caches.
   ConditionVariable inline_cache_cond_ GUARDED_BY(lock_);
 
+  // Mapping flags for the code section.
+  const int memmap_flags_prot_code_;
+
   friend class art::JitJniStubTestHelper;
+  friend class ScopedCodeCacheWrite;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache);
 };
 
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 53f4864..6ccda8b 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -37,8 +37,9 @@
 #include "gc/collector_type.h"
 #include "gc/gc_cause.h"
 #include "gc/scoped_gc_critical_section.h"
-#include "jit/profile_compilation_info.h"
+#include "jit/profiling_info.h"
 #include "oat_file_manager.h"
+#include "profile/profile_compilation_info.h"
 #include "scoped_thread_state_change-inl.h"
 
 namespace art {
@@ -46,6 +47,10 @@
 ProfileSaver* ProfileSaver::instance_ = nullptr;
 pthread_t ProfileSaver::profiler_pthread_ = 0U;
 
+static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize ==
+              InlineCache::kIndividualCacheSize,
+              "InlineCache and ProfileCompilationInfo do not agree on kIndividualCacheSize");
+
 // At what priority to schedule the saver threads. 9 is the lowest foreground priority on device.
 static constexpr int kProfileSaverPthreadPriority = 9;
 
@@ -126,6 +131,11 @@
   }
   FetchAndCacheResolvedClassesAndMethods(/*startup*/ true);
 
+
+  // When we save without waiting for JIT notifications we use a simple
+  // exponential back off policy bounded by max_wait_without_jit.
+  uint32_t max_wait_without_jit = options_.GetMinSavePeriodMs() * 16;
+  uint64_t cur_wait_without_jit = options_.GetMinSavePeriodMs();
   // Loop for the profiled methods.
   while (!ShuttingDown(self)) {
     uint64_t sleep_start = NanoTime();
@@ -133,7 +143,14 @@
       uint64_t sleep_time = 0;
       {
         MutexLock mu(self, wait_lock_);
-        period_condition_.Wait(self);
+        if (options_.GetWaitForJitNotificationsToSave()) {
+          period_condition_.Wait(self);
+        } else {
+          period_condition_.TimedWait(self, cur_wait_without_jit, 0);
+          if (cur_wait_without_jit < max_wait_without_jit) {
+            cur_wait_without_jit *= 2;
+          }
+        }
         sleep_time = NanoTime() - sleep_start;
       }
       // Check if the thread was woken up for shutdown.
@@ -511,10 +528,24 @@
       uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
       uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
 
-      info.AddMethods(profile_methods, ProfileCompilationInfo::MethodHotness::kFlagPostStartup);
+      // Try to add the method data. Note this may fail is the profile loaded from disk contains
+      // outdated data (e.g. the previous profiled dex files might have been updated).
+      // If this happens we clear the profile data and for the save to ensure the file is cleared.
+      if (!info.AddMethods(profile_methods,
+              ProfileCompilationInfo::MethodHotness::kFlagPostStartup)) {
+        LOG(WARNING) << "Could not add methods to the existing profiler. "
+            << "Clearing the profile data.";
+        info.ClearData();
+        force_save = true;
+      }
+
       auto profile_cache_it = profile_cache_.find(filename);
       if (profile_cache_it != profile_cache_.end()) {
-        info.MergeWith(*(profile_cache_it->second));
+        if (!info.MergeWith(*(profile_cache_it->second))) {
+          LOG(WARNING) << "Could not merge the profile. Clearing the profile data.";
+          info.ClearData();
+          force_save = true;
+        }
       }
 
       int64_t delta_number_of_methods =
@@ -592,7 +623,13 @@
   return nullptr;
 }
 
-static bool ShouldProfileLocation(const std::string& location) {
+static bool ShouldProfileLocation(const std::string& location, bool profile_aot_code) {
+  if (profile_aot_code) {
+    // If we have to profile all the code, irrespective of its compilation state, return true
+    // right away.
+    return true;
+  }
+
   OatFileManager& oat_manager = Runtime::Current()->GetOatFileManager();
   const OatFile* oat_file = oat_manager.FindOpenedOatFileFromDexLocation(location);
   if (oat_file == nullptr) {
@@ -624,7 +661,7 @@
 
   std::vector<std::string> code_paths_to_profile;
   for (const std::string& location : code_paths) {
-    if (ShouldProfileLocation(location))  {
+    if (ShouldProfileLocation(location, options.GetProfileAOTCode()))  {
       code_paths_to_profile.push_back(location);
     }
   }
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index afbb3c1..02c8cd1 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -21,7 +21,7 @@
 #include "base/safe_map.h"
 #include "dex/method_reference.h"
 #include "jit_code_cache.h"
-#include "profile_compilation_info.h"
+#include "profile/profile_compilation_info.h"
 #include "profile_saver_options.h"
 
 namespace art {
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index d1e14e2..18f7899 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -41,7 +41,9 @@
     min_notification_before_wake_(kMinNotificationBeforeWake),
     max_notification_before_wake_(kMaxNotificationBeforeWake),
     profile_path_(""),
-    profile_boot_class_path_(false) {}
+    profile_boot_class_path_(false),
+    profile_aot_code_(false),
+    wait_for_jit_notifications_to_save_(true) {}
 
   ProfileSaverOptions(
       bool enabled,
@@ -53,7 +55,9 @@
       uint32_t min_notification_before_wake,
       uint32_t max_notification_before_wake,
       const std::string& profile_path,
-      bool profile_boot_class_path)
+      bool profile_boot_class_path,
+      bool profile_aot_code = false,
+      bool wait_for_jit_notifications_to_save = true)
   : enabled_(enabled),
     min_save_period_ms_(min_save_period_ms),
     save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms),
@@ -63,7 +67,9 @@
     min_notification_before_wake_(min_notification_before_wake),
     max_notification_before_wake_(max_notification_before_wake),
     profile_path_(profile_path),
-    profile_boot_class_path_(profile_boot_class_path) {}
+    profile_boot_class_path_(profile_boot_class_path),
+    profile_aot_code_(profile_aot_code),
+    wait_for_jit_notifications_to_save_(wait_for_jit_notifications_to_save) {}
 
   bool IsEnabled() const {
     return enabled_;
@@ -103,6 +109,18 @@
   bool GetProfileBootClassPath() const {
     return profile_boot_class_path_;
   }
+  bool GetProfileAOTCode() const {
+    return profile_aot_code_;
+  }
+  void SetProfileAOTCode(bool value) {
+    profile_aot_code_ = value;
+  }
+  bool GetWaitForJitNotificationsToSave() const {
+    return wait_for_jit_notifications_to_save_;
+  }
+  void SetWaitForJitNotificationsToSave(bool value) {
+    wait_for_jit_notifications_to_save_ = value;
+  }
 
   friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) {
     os << "enabled_" << pso.enabled_
@@ -113,7 +131,9 @@
         << ", min_classes_to_save_" << pso.min_classes_to_save_
         << ", min_notification_before_wake_" << pso.min_notification_before_wake_
         << ", max_notification_before_wake_" << pso.max_notification_before_wake_
-        << ", profile_boot_class_path_" << pso.profile_boot_class_path_;
+        << ", profile_boot_class_path_" << pso.profile_boot_class_path_
+        << ", profile_aot_code_" << pso.profile_aot_code_
+        << ", wait_for_jit_notifications_to_save_" << pso.wait_for_jit_notifications_to_save_;
     return os;
   }
 
@@ -129,6 +149,8 @@
   uint32_t max_notification_before_wake_;
   std::string profile_path_;
   bool profile_boot_class_path_;
+  bool profile_aot_code_;
+  bool wait_for_jit_notifications_to_save_;
 };
 
 }  // namespace art
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
index 9126bea..2cb569c 100644
--- a/runtime/jit/profiling_info.cc
+++ b/runtime/jit/profiling_info.cc
@@ -26,12 +26,12 @@
 namespace art {
 
 ProfilingInfo::ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& entries)
-      : number_of_inline_caches_(entries.size()),
-        method_(method),
-        is_method_being_compiled_(false),
-        is_osr_method_being_compiled_(false),
+      : method_(method),
+        saved_entry_point_(nullptr),
+        number_of_inline_caches_(entries.size()),
         current_inline_uses_(0),
-        saved_entry_point_(nullptr) {
+        is_method_being_compiled_(false),
+        is_osr_method_being_compiled_(false) {
   memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache));
   for (size_t i = 0; i < number_of_inline_caches_; ++i) {
     cache_[i].dex_pc_ = entries[i];
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 788fa1f..a3dae83 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -132,28 +132,28 @@
  private:
   ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& entries);
 
-  // Number of instructions we are profiling in the ArtMethod.
-  const uint32_t number_of_inline_caches_;
-
   // Method this profiling info is for.
   // Not 'const' as JVMTI introduces obsolete methods that we implement by creating new ArtMethods.
   // See JitCodeCache::MoveObsoleteMethod.
   ArtMethod* method_;
 
+  // Entry point of the corresponding ArtMethod, while the JIT code cache
+  // is poking for the liveness of compiled code.
+  const void* saved_entry_point_;
+
+  // Number of instructions we are profiling in the ArtMethod.
+  const uint32_t number_of_inline_caches_;
+
+  // When the compiler inlines the method associated to this ProfilingInfo,
+  // it updates this counter so that the GC does not try to clear the inline caches.
+  uint16_t current_inline_uses_;
+
   // Whether the ArtMethod is currently being compiled. This flag
   // is implicitly guarded by the JIT code cache lock.
   // TODO: Make the JIT code cache lock global.
   bool is_method_being_compiled_;
   bool is_osr_method_being_compiled_;
 
-  // When the compiler inlines the method associated to this ProfilingInfo,
-  // it updates this counter so that the GC does not try to clear the inline caches.
-  uint16_t current_inline_uses_;
-
-  // Entry point of the corresponding ArtMethod, while the JIT code cache
-  // is poking for the liveness of compiled code.
-  const void* saved_entry_point_;
-
   // Dynamically allocated array of size `number_of_inline_caches_`.
   InlineCache cache_[0];
 
diff --git a/runtime/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
similarity index 100%
rename from runtime/java_vm_ext.cc
rename to runtime/jni/java_vm_ext.cc
diff --git a/runtime/java_vm_ext.h b/runtime/jni/java_vm_ext.h
similarity index 98%
rename from runtime/java_vm_ext.h
rename to runtime/jni/java_vm_ext.h
index ac20afe..408d354 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/jni/java_vm_ext.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_JAVA_VM_EXT_H_
-#define ART_RUNTIME_JAVA_VM_EXT_H_
+#ifndef ART_RUNTIME_JNI_JAVA_VM_EXT_H_
+#define ART_RUNTIME_JNI_JAVA_VM_EXT_H_
 
 #include "jni.h"
 
@@ -262,4 +262,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_JAVA_VM_EXT_H_
+#endif  // ART_RUNTIME_JNI_JAVA_VM_EXT_H_
diff --git a/runtime/java_vm_ext_test.cc b/runtime/jni/java_vm_ext_test.cc
similarity index 99%
rename from runtime/java_vm_ext_test.cc
rename to runtime/jni/java_vm_ext_test.cc
index a15ec56..74e4a30 100644
--- a/runtime/java_vm_ext_test.cc
+++ b/runtime/jni/java_vm_ext_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 
 #include <pthread.h>
 
diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni/jni_env_ext-inl.h
similarity index 92%
rename from runtime/jni_env_ext-inl.h
rename to runtime/jni/jni_env_ext-inl.h
index 14f708b..7609a9e 100644
--- a/runtime/jni_env_ext-inl.h
+++ b/runtime/jni/jni_env_ext-inl.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_JNI_ENV_EXT_INL_H_
-#define ART_RUNTIME_JNI_ENV_EXT_INL_H_
+#ifndef ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_
+#define ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_
 
 #include "jni_env_ext.h"
 
@@ -51,4 +51,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_JNI_ENV_EXT_INL_H_
+#endif  // ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_
diff --git a/runtime/jni_env_ext.cc b/runtime/jni/jni_env_ext.cc
similarity index 100%
rename from runtime/jni_env_ext.cc
rename to runtime/jni/jni_env_ext.cc
diff --git a/runtime/jni_env_ext.h b/runtime/jni/jni_env_ext.h
similarity index 98%
rename from runtime/jni_env_ext.h
rename to runtime/jni/jni_env_ext.h
index 291ac48..3a007ad 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni/jni_env_ext.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_JNI_ENV_EXT_H_
-#define ART_RUNTIME_JNI_ENV_EXT_H_
+#ifndef ART_RUNTIME_JNI_JNI_ENV_EXT_H_
+#define ART_RUNTIME_JNI_JNI_ENV_EXT_H_
 
 #include <jni.h>
 
@@ -229,4 +229,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_JNI_ENV_EXT_H_
+#endif  // ART_RUNTIME_JNI_JNI_ENV_EXT_H_
diff --git a/runtime/jni_internal.cc b/runtime/jni/jni_internal.cc
similarity index 99%
rename from runtime/jni_internal.cc
rename to runtime/jni/jni_internal.cc
index 9dbcded..cd66a60 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -80,15 +80,15 @@
 // things not rendering correctly. E.g. b/16858794
 static constexpr bool kWarnJniAbort = false;
 
-static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
-  return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1));
+static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+  return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames */ 1));
 }
 
 template<typename T>
 ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   hiddenapi::Action action = hiddenapi::GetMemberAction(
-      member, self, IsCallerInPlatformDex, hiddenapi::kJNI);
+      member, self, IsCallerTrusted, hiddenapi::kJNI);
   if (action != hiddenapi::kAllow) {
     hiddenapi::NotifyHiddenApiListener(member);
   }
diff --git a/runtime/jni_internal.h b/runtime/jni/jni_internal.h
similarity index 92%
rename from runtime/jni_internal.h
rename to runtime/jni/jni_internal.h
index 2c90b3b..d042661 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni/jni_internal.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_JNI_INTERNAL_H_
-#define ART_RUNTIME_JNI_INTERNAL_H_
+#ifndef ART_RUNTIME_JNI_JNI_INTERNAL_H_
+#define ART_RUNTIME_JNI_JNI_INTERNAL_H_
 
 #include <jni.h>
 #include <iosfwd>
@@ -59,4 +59,4 @@
 
 std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs);
 
-#endif  // ART_RUNTIME_JNI_INTERNAL_H_
+#endif  // ART_RUNTIME_JNI_JNI_INTERNAL_H_
diff --git a/runtime/jni_internal_test.cc b/runtime/jni/jni_internal_test.cc
similarity index 100%
rename from runtime/jni_internal_test.cc
rename to runtime/jni/jni_internal_test.cc
diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc
deleted file mode 100644
index 4c45e38..0000000
--- a/runtime/jobject_comparator.cc
+++ /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.
- */
-
-#include "jobject_comparator.h"
-
-#include "mirror/array-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/object-inl.h"
-#include "scoped_thread_state_change-inl.h"
-
-namespace art {
-
-bool JobjectComparator::operator()(jobject jobj1, jobject jobj2) const {
-  // Ensure null references and cleared jweaks appear at the end.
-  if (jobj1 == nullptr) {
-    return true;
-  } else if (jobj2 == nullptr) {
-    return false;
-  }
-  ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<2> hs(soa.Self());
-  Handle<mirror::Object> obj1(hs.NewHandle(soa.Decode<mirror::Object>(jobj1)));
-  Handle<mirror::Object> obj2(hs.NewHandle(soa.Decode<mirror::Object>(jobj2)));
-  if (obj1 == nullptr) {
-    return true;
-  } else if (obj2 == nullptr) {
-    return false;
-  }
-  // Sort by class...
-  if (obj1->GetClass() != obj2->GetClass()) {
-    return obj1->GetClass()->IdentityHashCode() < obj2->GetClass()->IdentityHashCode();
-  }
-  // ...then by size...
-  const size_t count1 = obj1->SizeOf();
-  const size_t count2 = obj2->SizeOf();
-  if (count1 != count2) {
-    return count1 < count2;
-  }
-  // ...and finally by identity hash code.
-  return obj1->IdentityHashCode() < obj2->IdentityHashCode();
-}
-
-}  // namespace art
diff --git a/runtime/jobject_comparator.h b/runtime/jobject_comparator.h
deleted file mode 100644
index 698d667..0000000
--- a/runtime/jobject_comparator.h
+++ /dev/null
@@ -1,30 +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_RUNTIME_JOBJECT_COMPARATOR_H_
-#define ART_RUNTIME_JOBJECT_COMPARATOR_H_
-
-#include <jni.h>
-
-namespace art {
-
-struct JobjectComparator {
-  bool operator()(jobject jobj1, jobject jobj2) const;
-};
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_JOBJECT_COMPARATOR_H_
diff --git a/runtime/jvalue-inl.h b/runtime/jvalue-inl.h
index 25e34b2..5bd4f17 100644
--- a/runtime/jvalue-inl.h
+++ b/runtime/jvalue-inl.h
@@ -19,7 +19,7 @@
 
 #include "jvalue.h"
 
-#include "obj_ptr.h"
+#include "obj_ptr-inl.h"
 
 namespace art {
 
diff --git a/runtime/jvalue.h b/runtime/jvalue.h
index 266abcf..b42d995 100644
--- a/runtime/jvalue.h
+++ b/runtime/jvalue.h
@@ -33,7 +33,7 @@
   // We default initialize JValue instances to all-zeros.
   JValue() : j(0) {}
 
-  template<typename T> static JValue FromPrimitive(T v);
+  template<typename T> ALWAYS_INLINE static JValue FromPrimitive(T v);
 
   int8_t GetB() const { return b; }
   void SetB(int8_t new_b) {
@@ -62,6 +62,7 @@
   mirror::Object* GetL() const REQUIRES_SHARED(Locks::mutator_lock_) {
     return l;
   }
+  ALWAYS_INLINE
   void SetL(ObjPtr<mirror::Object> new_l) REQUIRES_SHARED(Locks::mutator_lock_);
 
   int16_t GetS() const { return s; }
diff --git a/runtime/lock_word.h b/runtime/lock_word.h
index 09d856f..ce7fe34 100644
--- a/runtime/lock_word.h
+++ b/runtime/lock_word.h
@@ -75,16 +75,18 @@
     // Remaining bits are the recursive lock count.
     kThinLockCountSize = 32 - kThinLockOwnerSize - kStateSize - kReadBarrierStateSize -
         kMarkBitStateSize,
-    // Thin lock bits. Owner in lowest bits.
 
+    // Thin lock bits. Owner in lowest bits.
     kThinLockOwnerShift = 0,
     kThinLockOwnerMask = (1 << kThinLockOwnerSize) - 1,
+    kThinLockOwnerMaskShifted = kThinLockOwnerMask << kThinLockOwnerShift,
     kThinLockMaxOwner = kThinLockOwnerMask,
     // Count in higher bits.
     kThinLockCountShift = kThinLockOwnerSize + kThinLockOwnerShift,
     kThinLockCountMask = (1 << kThinLockCountSize) - 1,
     kThinLockMaxCount = kThinLockCountMask,
     kThinLockCountOne = 1 << kThinLockCountShift,  // == 65536 (0x10000)
+    kThinLockCountMaskShifted = kThinLockCountMask << kThinLockCountShift,
 
     // State in the highest bits.
     kStateShift = kReadBarrierStateSize + kThinLockCountSize + kThinLockCountShift +
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index 41c8384..00a8c00 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -22,7 +22,7 @@
 #include "common_throws.h"
 #include "dex/dex_instruction.h"
 #include "interpreter/interpreter_common.h"
-#include "jvalue.h"
+#include "jvalue-inl.h"
 #include "mirror/class.h"
 #include "mirror/method_type.h"
 #include "mirror/object.h"
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 64ab789..1d45aae 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -20,7 +20,6 @@
 
 #include "common_dex_operations.h"
 #include "jvalue-inl.h"
-#include "jvalue.h"
 #include "mirror/emulated_stack_frame.h"
 #include "mirror/method_handle_impl-inl.h"
 #include "mirror/method_type.h"
diff --git a/runtime/method_info.h b/runtime/method_info.h
index b00ddc6..6f74678 100644
--- a/runtime/method_info.h
+++ b/runtime/method_info.h
@@ -21,7 +21,7 @@
 
 #include "base/leb128.h"
 #include "base/macros.h"
-#include "base/memory_region.h"
+#include "base/bit_memory_region.h"
 
 namespace art {
 
@@ -35,8 +35,8 @@
   explicit MethodInfo(const uint8_t* ptr) {
     if (ptr != nullptr) {
       num_method_indices_ = DecodeUnsignedLeb128(&ptr);
-      region_ = MemoryRegion(const_cast<uint8_t*>(ptr),
-                             num_method_indices_ * sizeof(MethodIndexType));
+      region_ = BitMemoryRegion(
+          MemoryRegion(const_cast<uint8_t*>(ptr), num_method_indices_ * sizeof(MethodIndexType)));
     }
   }
 
@@ -44,7 +44,7 @@
   MethodInfo(uint8_t* ptr, size_t num_method_indices) : num_method_indices_(num_method_indices) {
     DCHECK(ptr != nullptr);
     ptr = EncodeUnsignedLeb128(ptr, num_method_indices_);
-    region_ = MemoryRegion(ptr, num_method_indices_ * sizeof(MethodIndexType));
+    region_ = BitMemoryRegion(MemoryRegion(ptr, num_method_indices_ * sizeof(MethodIndexType)));
   }
 
   static size_t ComputeSize(size_t num_method_indices) {
@@ -71,7 +71,7 @@
 
  private:
   size_t num_method_indices_ = 0u;
-  MemoryRegion region_;
+  BitMemoryRegion region_;
 };
 
 }  // namespace art
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 51d1376..98e25eb 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -210,6 +210,15 @@
     return (GetAccessFlags() & kAccClassIsFinalizable) != 0;
   }
 
+  ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0;
+  }
+
+  ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint32_t flags = GetAccessFlags();
+    SetAccessFlags(flags | kAccSkipHiddenApiChecks);
+  }
+
   ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId());
     uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 7a4876c..72f1443 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -127,23 +127,23 @@
   }
 }
 
-inline uint32_t DexCache::MethodTypeSlotIndex(uint32_t proto_idx) {
+inline uint32_t DexCache::MethodTypeSlotIndex(dex::ProtoIndex proto_idx) {
   DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-  DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
-  const uint32_t slot_idx = proto_idx % kDexCacheMethodTypeCacheSize;
+  DCHECK_LT(proto_idx.index_, GetDexFile()->NumProtoIds());
+  const uint32_t slot_idx = proto_idx.index_ % kDexCacheMethodTypeCacheSize;
   DCHECK_LT(slot_idx, NumResolvedMethodTypes());
   return slot_idx;
 }
 
-inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) {
+inline MethodType* DexCache::GetResolvedMethodType(dex::ProtoIndex proto_idx) {
   return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load(
-      std::memory_order_relaxed).GetObjectForIndex(proto_idx);
+      std::memory_order_relaxed).GetObjectForIndex(proto_idx.index_);
 }
 
-inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) {
+inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) {
   DCHECK(resolved != nullptr);
   GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store(
-      MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed);
+      MethodTypeDexCachePair(resolved, proto_idx.index_), std::memory_order_relaxed);
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index eb4db00..661f954 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -17,10 +17,10 @@
 #include "dex_cache-inl.h"
 
 #include "art_method-inl.h"
+#include "base/globals.h"
 #include "class_linker.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/heap.h"
-#include "globals.h"
 #include "linear_alloc.h"
 #include "oat_file.h"
 #include "object-inl.h"
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index d940964..9aff9ec 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -307,9 +307,9 @@
   ALWAYS_INLINE void ClearResolvedField(uint32_t idx, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+  MethodType* GetResolvedMethodType(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved)
+  void SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -432,7 +432,7 @@
   uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
   uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_);
   uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_);
-  uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+  uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
   void Init(const DexFile* dex_file,
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index d2bff2c..97e0ce6 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -169,9 +169,9 @@
 
   for (size_t i = 0; i < dex_file.NumProtoIds(); ++i) {
     const MethodTypeDexCachePair pair = method_types_cache[i].load(std::memory_order_relaxed);
-    if (pair.index == method1_id.proto_idx_) {
+    if (dex::ProtoIndex(pair.index) == method1_id.proto_idx_) {
       ASSERT_EQ(method1_type.Get(), pair.object.Read());
-    } else if (pair.index == method2_id.proto_idx_) {
+    } else if (dex::ProtoIndex(pair.index) == method2_id.proto_idx_) {
       ASSERT_EQ(method2_type.Get(), pair.object.Read());
     } else {
       ASSERT_TRUE(false);
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
index 039bbf2..aeecf75 100644
--- a/runtime/mirror/method_handles_lookup.cc
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -20,7 +20,7 @@
 #include "dex/modifiers.h"
 #include "gc_root-inl.h"
 #include "handle_scope.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/method_handle_impl.h"
 #include "object-inl.h"
 #include "well_known_classes.h"
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index d00c90b..82045c7 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -20,7 +20,7 @@
 #include "base/atomic.h"
 #include "base/casts.h"
 #include "base/enums.h"
-#include "globals.h"
+#include "base/globals.h"
 #include "obj_ptr.h"
 #include "object_reference.h"
 #include "offsets.h"
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index 356fef0..77154e2 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -18,8 +18,8 @@
 #define ART_RUNTIME_MIRROR_OBJECT_REFERENCE_H_
 
 #include "base/atomic.h"
+#include "base/globals.h"
 #include "base/mutex.h"  // For Locks::mutator_lock_.
-#include "globals.h"
 #include "heap_poisoning.h"
 #include "obj_ptr.h"
 
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index a79c0a2..44c819a 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -22,7 +22,7 @@
 #include "class_linker.h"
 #include "gc_root-inl.h"
 #include "intrinsics_enum.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "jvalue-inl.h"
 #include "method_handles.h"
 #include "method_type.h"
@@ -1425,21 +1425,24 @@
   return GetField32(AccessModesBitMaskOffset());
 }
 
-bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) {
-  StackHandleScope<3> hs(Thread::Current());
-  Handle<Class> mt_rtype(hs.NewHandle(method_type->GetRType()));
-  Handle<VarHandle> vh(hs.NewHandle(this));
-  Handle<Class> var_type(hs.NewHandle(vh->GetVarType()));
+VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode access_mode,
+                                                                MethodType* method_type) {
+  MatchKind match = MatchKind::kExact;
+
+  ObjPtr<VarHandle> vh = this;
+  ObjPtr<Class> var_type = vh->GetVarType();
+  ObjPtr<Class> mt_rtype = method_type->GetRType();
   AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
 
-  // Check return type first.
-  if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) {
-    // The result of the operation will be discarded. The return type
-    // of the VarHandle is immaterial.
-  } else {
-    ObjPtr<Class> vh_rtype(GetReturnType(access_mode_template, var_type.Get()));
-    if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) {
-      return false;
+  // Check return type first. If the return type of the method
+  // of the VarHandle is immaterial.
+  if (mt_rtype->GetPrimitiveType() != Primitive::Type::kPrimVoid) {
+    ObjPtr<Class> vh_rtype = GetReturnType(access_mode_template, var_type.Ptr());
+    if (vh_rtype != mt_rtype) {
+      if (!IsReturnTypeConvertible(vh_rtype, mt_rtype)) {
+        return MatchKind::kNone;
+      }
+      match = MatchKind::kWithConversions;
     }
   }
 
@@ -1447,21 +1450,25 @@
   ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters];
   const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes,
                                                       access_mode_template,
-                                                      var_type.Get(),
+                                                      var_type,
                                                       GetCoordinateType0(),
                                                       GetCoordinateType1());
   if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) {
-    return false;
+    return MatchKind::kNone;
   }
 
   // Check the parameter types are compatible.
   ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes();
   for (int32_t i = 0; i < vh_ptypes_count; ++i) {
-    if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) {
-      return false;
+    if (mt_ptypes->Get(i) == vh_ptypes[i]) {
+      continue;
     }
+    if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) {
+      return MatchKind::kNone;
+    }
+    match = MatchKind::kWithConversions;
   }
-  return true;
+  return match;
 }
 
 bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode,
@@ -1508,7 +1515,7 @@
 MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self,
                                                   ObjPtr<VarHandle> var_handle,
                                                   AccessMode access_mode) {
-  // This is a static as the var_handle might be moved by the GC during it's execution.
+  // This is a static method as the var_handle might be moved by the GC during it's execution.
   AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
 
   StackHandleScope<3> hs(self);
@@ -1538,9 +1545,40 @@
   return GetMethodTypeForAccessMode(self, this, access_mode);
 }
 
+std::string VarHandle::PrettyDescriptorForAccessMode(AccessMode access_mode) {
+  // Effect MethodType::PrettyDescriptor() without first creating a method type first.
+  std::ostringstream oss;
+  oss << '(';
+
+  AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
+  ObjPtr<Class> var_type = GetVarType();
+  ObjPtr<Class> ctypes[2] = { GetCoordinateType0(), GetCoordinateType1() };
+  const int32_t ptypes_count = GetNumberOfParameters(access_mode_template, ctypes[0], ctypes[1]);
+  int32_t ptypes_done = 0;
+  for (ObjPtr<Class> ctype : ctypes) {
+    if (!ctype.IsNull()) {
+      if (ptypes_done != 0) {
+        oss << ", ";
+      }
+      oss << ctype->PrettyDescriptor();;
+      ptypes_done++;
+    }
+  }
+  while (ptypes_done != ptypes_count) {
+    if (ptypes_done != 0) {
+      oss << ", ";
+    }
+    oss << var_type->PrettyDescriptor();
+    ptypes_done++;
+  }
+  ObjPtr<Class> rtype = GetReturnType(access_mode_template, var_type);
+  oss << ')' << rtype->PrettyDescriptor();
+  return oss.str();
+}
+
 bool VarHandle::Access(AccessMode access_mode,
                        ShadowFrame* shadow_frame,
-                       InstructionOperands* operands,
+                       const InstructionOperands* const operands,
                        JValue* result) {
   Class* klass = GetClass();
   if (klass == FieldVarHandle::StaticClass()) {
@@ -1671,7 +1709,7 @@
 
 bool FieldVarHandle::Access(AccessMode access_mode,
                             ShadowFrame* shadow_frame,
-                            InstructionOperands* operands,
+                            const InstructionOperands* const operands,
                             JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
   ArtField* field = GetField();
@@ -1743,7 +1781,7 @@
 
 bool ArrayElementVarHandle::Access(AccessMode access_mode,
                                    ShadowFrame* shadow_frame,
-                                   InstructionOperands* operands,
+                                   const InstructionOperands* const operands,
                                    JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
 
@@ -1856,7 +1894,7 @@
 
 bool ByteArrayViewVarHandle::Access(AccessMode access_mode,
                                     ShadowFrame* shadow_frame,
-                                    InstructionOperands* operands,
+                                    const InstructionOperands* const operands,
                                     JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
 
@@ -1965,7 +2003,7 @@
 
 bool ByteBufferViewVarHandle::Access(AccessMode access_mode,
                                      ShadowFrame* shadow_frame,
-                                     InstructionOperands* operands,
+                                     const InstructionOperands* const operands,
                                      JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
 
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
index d46d900..5186d43 100644
--- a/runtime/mirror/var_handle.h
+++ b/runtime/mirror/var_handle.h
@@ -99,14 +99,16 @@
     return (GetAccessModesBitMask() & (1u << static_cast<uint32_t>(accessMode))) != 0;
   }
 
-  // Returns true if the MethodType specified is compatible with the
-  // method type associated with the specified AccessMode. The
-  // supplied MethodType is assumed to be from the point of invocation
-  // so it is valid for the supplied MethodType to have a void return
-  // value when the return value for the AccessMode is non-void. This
-  // corresponds to the result of the accessor being discarded.
-  bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  enum MatchKind : uint8_t {
+    kNone,
+    kWithConversions,
+    kExact
+  };
+
+  // Returns match information on the compatability between the exact method type for
+  // 'access_mode' and the provided 'method_type'.
+  MatchKind GetMethodTypeMatchForAccessMode(AccessMode access_mode, MethodType* method_type)
+        REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true if the MethodType specified is compatible with the
   // specified access_mode if the first parameter of method_type is
@@ -122,9 +124,14 @@
   MethodType* GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Returns a string representing the descriptor of the MethodType associated with
+  // this AccessMode.
+  std::string PrettyDescriptorForAccessMode(AccessMode access_mode)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -192,7 +199,7 @@
  public:
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -225,7 +232,7 @@
  public:
     bool Access(AccessMode access_mode,
                 ShadowFrame* shadow_frame,
-                InstructionOperands* operands,
+                const InstructionOperands* const operands,
                 JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -248,7 +255,7 @@
  public:
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -281,7 +288,7 @@
  public:
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index d9fa07f..005aba3 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -246,6 +246,47 @@
   return MethodType::Create(self, rtype, ptypes);
 }
 
+static bool AccessModeMatch(VarHandle* vh,
+                            VarHandle::AccessMode access_mode,
+                            MethodType* method_type,
+                            VarHandle::MatchKind expected_match)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return vh->GetMethodTypeMatchForAccessMode(access_mode, method_type) == expected_match;
+}
+
+template <typename VH>
+static bool AccessModeExactMatch(Handle<VH> vh,
+                                 VarHandle::AccessMode access_mode,
+                                 const char* descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return AccessModeMatch(vh.Get(),
+                         access_mode,
+                         MethodTypeOf(descriptor),
+                         VarHandle::MatchKind::kExact);
+}
+
+template <typename VH>
+static bool AccessModeWithConversionsMatch(Handle<VH> vh,
+                                          VarHandle::AccessMode access_mode,
+                                          const char* descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return AccessModeMatch(vh.Get(),
+                         access_mode,
+                         MethodTypeOf(descriptor),
+                         VarHandle::MatchKind::kWithConversions);
+}
+
+template <typename VH>
+static bool AccessModeNoMatch(Handle<VH> vh,
+                              VarHandle::AccessMode access_mode,
+                              const char* descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return AccessModeMatch(vh.Get(),
+                         access_mode,
+                         MethodTypeOf(descriptor),
+                         VarHandle::MatchKind::kNone);
+}
+
 TEST_F(VarHandleTest, InstanceFieldVarHandle) {
   Thread * const self = Thread::Current();
   ScopedObjectAccess soa(self);
@@ -296,47 +337,53 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;)D"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;S)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("(Ljava/lang/Integer;II)I")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Z"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Ljava/lang/Boolean;"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;IB)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIII)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)J"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;BS)F"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIII)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S"));
   }
 
   // Check synthesized method types match expected forms.
@@ -430,48 +477,47 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(F)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(F)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("(II)Ljava/lang/String;")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIJ)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(ID)I"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIJ)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
@@ -594,50 +640,46 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;Ljava/lang/String;)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;Ljava/lang/String;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(
-        vh->IsMethodTypeCompatible(
-            access_mode,
-            MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("([Ljava/lang/String;III)I")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;III)I"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;II)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;II)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
@@ -747,50 +789,46 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)C")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BC)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)C"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BC)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(
-        vh->IsMethodTypeCompatible(
-            access_mode,
-            MethodTypeOf("([BICC)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("([BIII)I")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIII)I"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)C")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BII)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)C"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BII)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)C")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)C"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIC)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
@@ -900,50 +938,46 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)D")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;D)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)D"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;D)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(
-        vh->IsMethodTypeCompatible(
-            access_mode,
-            MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("(Ljava/nio/ByteBuffer;IDI)D")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDI)D"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;II)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)D"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;II)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)D"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 8320d9c..cdba6b2 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -35,7 +35,7 @@
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_loader.h"
 #include "jit/debugger_interface.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
 #include "mirror/string.h"
@@ -816,6 +816,28 @@
   return static_cast<jlong>(file_size);
 }
 
+static void DexFile_setTrusted(JNIEnv* env, jclass, jobject j_cookie) {
+  Runtime* runtime = Runtime::Current();
+  ScopedObjectAccess soa(env);
+
+  // Currently only allow this for debuggable apps.
+  if (!runtime->IsJavaDebuggable()) {
+    ThrowSecurityException("Can't exempt class, process is not debuggable.");
+    return;
+  }
+
+  std::vector<const DexFile*> dex_files;
+  const OatFile* oat_file;
+  if (!ConvertJavaArrayToDexFiles(env, j_cookie, dex_files, oat_file)) {
+    Thread::Current()->AssertPendingException();
+    return;
+  }
+
+  for (const DexFile* dex_file : dex_files) {
+    const_cast<DexFile*>(dex_file)->SetIsPlatformDexFile();
+  }
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
   NATIVE_METHOD(DexFile,
@@ -854,7 +876,8 @@
                 "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
   NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
   NATIVE_METHOD(DexFile, getDexFileOptimizationStatus,
-                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;")
+                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
+  NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V")
 };
 
 void register_dalvik_system_DexFile(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 3692a30..f1e267b 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -35,8 +35,8 @@
 #include "gc/space/zygote_space.h"
 #include "handle_scope-inl.h"
 #include "hprof/hprof.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "mirror/class.h"
 #include "mirror/object_array-inl.h"
 #include "native_util.h"
@@ -588,6 +588,25 @@
   Runtime::Current()->AttachAgent(env, filename, classloader);
 }
 
+static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) {
+  Runtime* runtime = Runtime::Current();
+  ScopedObjectAccess soa(env);
+
+  if (!runtime->IsJavaDebuggable()) {
+    ThrowSecurityException("Can't exempt class, process is not debuggable.");
+    return;
+  }
+
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::Class> h_caller(hs.NewHandle(soa.Decode<mirror::Class>(j_caller)));
+  if (h_caller.IsNull()) {
+    ThrowNullPointerException("argument is null");
+    return;
+  }
+
+  h_caller->SetSkipHiddenApiChecks();
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
   NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
@@ -623,6 +642,7 @@
   NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
   NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
   NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
+  NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
 };
 
 void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index a5ade6f..6c82019 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -41,8 +41,8 @@
 #include "gc/space/image_space.h"
 #include "gc/task_processor.h"
 #include "intern_table.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
@@ -93,6 +93,10 @@
   Runtime::Current()->SetHiddenApiExemptions(exemptions_vec);
 }
 
+static void VMRuntime_setHiddenApiAccessLogSamplingRate(JNIEnv*, jclass, jint rate) {
+  Runtime::Current()->SetHiddenApiEventLogSampleRate(rate);
+}
+
 static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass,
                                             jint length) {
   ScopedFastNativeObjectAccess soa(env);
@@ -181,6 +185,10 @@
   return Runtime::Current()->IsNativeDebuggable();
 }
 
+static jboolean VMRuntime_isJavaDebuggable(JNIEnv*, jobject) {
+  return Runtime::Current()->IsJavaDebuggable();
+}
+
 static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) {
   DCHECK(WellKnownClasses::java_lang_String != nullptr);
 
@@ -678,6 +686,12 @@
 #endif
 }
 
+static void VMRuntime_setDedupeHiddenApiWarnings(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                 jclass klass ATTRIBUTE_UNUSED,
+                                                 jboolean dedupe) {
+  Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe);
+}
+
 static JNINativeMethod gMethods[] = {
   FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
   NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -688,9 +702,11 @@
   NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"),
   NATIVE_METHOD(VMRuntime, hasUsedHiddenApi, "()Z"),
   NATIVE_METHOD(VMRuntime, setHiddenApiExemptions, "([Ljava/lang/String;)V"),
+  NATIVE_METHOD(VMRuntime, setHiddenApiAccessLogSamplingRate, "(I)V"),
   NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),
   FAST_NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"),
   FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"),
+  NATIVE_METHOD(VMRuntime, isJavaDebuggable, "()Z"),
   NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"),
   FAST_NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
   FAST_NATIVE_METHOD(VMRuntime, newUnpaddedArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
@@ -718,6 +734,7 @@
   NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"),
   NATIVE_METHOD(VMRuntime, didPruneDalvikCache, "()Z"),
   NATIVE_METHOD(VMRuntime, setSystemDaemonThreadPriority, "()V"),
+  NATIVE_METHOD(VMRuntime, setDedupeHiddenApiWarnings, "(Z)V"),
 };
 
 void register_dalvik_system_VMRuntime(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index ed0eb97..3919227 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -22,7 +22,7 @@
 
 #include "art_method-inl.h"
 #include "gc/task_processor.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index cf0a72a..38c65f5 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -28,9 +28,9 @@
 #include "base/runtime_debug.h"
 #include "debugger.h"
 #include "hidden_api.h"
-#include "java_vm_ext.h"
 #include "jit/jit.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "native_util.h"
 #include "nativehelper/jni_macros.h"
 #include "nativehelper/scoped_utf_chars.h"
@@ -177,6 +177,7 @@
   DEBUG_GENERATE_MINI_DEBUG_INFO     = 1 << 11,
   HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
                                      | (1 << 13),
+  PROFILE_SYSTEM_SERVER              = 1 << 14,
 
   // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value
   // corresponding to hiddenapi::EnforcementPolicy
@@ -308,6 +309,9 @@
       (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT);
   runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK;
 
+  bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER;
+  runtime_flags &= ~PROFILE_SYSTEM_SERVER;
+
   if (runtime_flags != 0) {
     LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags);
   }
@@ -363,6 +367,13 @@
       << "Child zygote processes should be forked with EnforcementPolicy::kDisable";
   Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy);
   Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings);
+  if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks &&
+      Runtime::Current()->GetHiddenApiEventLogSampleRate() != 0) {
+    // Hidden API checks are enabled, and we are sampling access for the event log. Initialize the
+    // random seed, to ensure the sampling is actually random. We do this post-fork, as doing it
+    // pre-fork would result in the same sequence for every forked process.
+    std::srand(static_cast<uint32_t>(NanoTime()));
+  }
 
   // Clear the hidden API warning flag, in case it was set.
   Runtime::Current()->SetPendingHiddenApiWarning(false);
@@ -385,7 +396,11 @@
         env, is_system_server, action, isa_string.c_str());
   } else {
     Runtime::Current()->InitNonZygoteOrPostFork(
-        env, is_system_server, Runtime::NativeBridgeAction::kUnload, nullptr);
+        env,
+        is_system_server,
+        Runtime::NativeBridgeAction::kUnload,
+        /*isa*/ nullptr,
+        profile_system_server);
   }
 }
 
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index bfd7f69..9f595b1 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -28,7 +28,7 @@
 #include "dex/dex_file_annotations.h"
 #include "dex/utf.h"
 #include "hidden_api.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/field-inl.h"
@@ -52,7 +52,7 @@
 
 // Returns true if the first caller outside of the Class class or java.lang.invoke package
 // is in a platform DEX file.
-static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
   // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke.
   // This is very expensive. Save this till the last.
   struct FirstExternalCallerVisitor : public StackVisitor {
@@ -99,7 +99,7 @@
   FirstExternalCallerVisitor visitor(self);
   visitor.WalkStack();
   return visitor.caller != nullptr &&
-         hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass());
+         hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass());
 }
 
 // Returns true if the first non-ClassClass caller up the stack is not allowed to
@@ -107,7 +107,7 @@
 ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
-  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self);
+  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self);
 }
 
 // Returns true if the first non-ClassClass caller up the stack should not be
@@ -116,7 +116,7 @@
 ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   hiddenapi::Action action = hiddenapi::GetMemberAction(
-      member, self, IsCallerInPlatformDex, hiddenapi::kReflection);
+      member, self, IsCallerTrusted, hiddenapi::kReflection);
   if (action != hiddenapi::kAllow) {
     hiddenapi::NotifyHiddenApiListener(member);
   }
@@ -128,19 +128,20 @@
 // the criteria. Some reflection calls only return public members
 // (public_only == true), some members should be hidden from non-boot class path
 // callers (enforce_hidden_api == true).
+template<typename T>
 ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
                                          bool enforce_hidden_api,
-                                         uint32_t access_flags) {
-  if (public_only && ((access_flags & kAccPublic) == 0)) {
+                                         T* member)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
     return false;
   }
 
-  if (enforce_hidden_api &&
-      hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) {
-    return false;
-  }
-
-  return true;
+  return hiddenapi::GetMemberAction(member,
+                                    nullptr,
+                                    [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; },
+                                    hiddenapi::kNone)
+      != hiddenapi::kDeny;
 }
 
 ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
@@ -269,12 +270,12 @@
   bool enforce_hidden_api = ShouldEnforceHiddenApi(self);
   // Lets go subtract all the non discoverable fields.
   for (ArtField& field : ifields) {
-    if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+    if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
       --array_size;
     }
   }
   for (ArtField& field : sfields) {
-    if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+    if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
       --array_size;
     }
   }
@@ -285,7 +286,7 @@
     return nullptr;
   }
   for (ArtField& field : ifields) {
-    if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+    if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
       auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
                                                                                    &field,
                                                                                    force_resolve);
@@ -300,7 +301,7 @@
     }
   }
   for (ArtField& field : sfields) {
-    if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+    if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
       auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
                                                                                    &field,
                                                                                    force_resolve);
@@ -521,7 +522,7 @@
   DCHECK(m != nullptr);
   return m->IsConstructor() &&
          !m->IsStatic() &&
-         IsDiscoverable(public_only, enforce_hidden_api, m->GetAccessFlags());
+         IsDiscoverable(public_only, enforce_hidden_api, m);
 }
 
 static jobjectArray Class_getDeclaredConstructorsInternal(
@@ -591,7 +592,7 @@
     uint32_t modifiers = m.GetAccessFlags();
     // Add non-constructor declared methods.
     if ((modifiers & kAccConstructor) == 0 &&
-        IsDiscoverable(public_only, enforce_hidden_api, modifiers)) {
+        IsDiscoverable(public_only, enforce_hidden_api, &m)) {
       ++num_methods;
     }
   }
@@ -605,7 +606,7 @@
   for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
     uint32_t modifiers = m.GetAccessFlags();
     if ((modifiers & kAccConstructor) == 0 &&
-        IsDiscoverable(public_only, enforce_hidden_api, modifiers)) {
+        IsDiscoverable(public_only, enforce_hidden_api, &m)) {
       DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
       DCHECK(!Runtime::Current()->IsActiveTransaction());
       auto* method =
@@ -647,7 +648,7 @@
     // Return an empty array instead of a null pointer.
     ObjPtr<mirror::Class>  annotation_array_class =
         soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
-    mirror::ObjectArray<mirror::Object>* empty_array =
+    ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array =
         mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(),
                                                    annotation_array_class.Ptr(),
                                                    0);
@@ -660,7 +661,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  mirror::ObjectArray<mirror::Class>* classes = nullptr;
+  ObjPtr<mirror::ObjectArray<mirror::Class>> classes = nullptr;
   if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) {
     classes = annotations::GetDeclaredClasses(klass);
   }
@@ -737,7 +738,7 @@
   if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
-  mirror::String* class_name = nullptr;
+  ObjPtr<mirror::String> class_name = nullptr;
   if (!annotations::GetInnerClass(klass, &class_name)) {
     return nullptr;
   }
@@ -762,7 +763,7 @@
   if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
     return false;
   }
-  mirror::String* class_name = nullptr;
+  ObjPtr<mirror::String> class_name = nullptr;
   if (!annotations::GetInnerClass(klass, &class_name)) {
     return false;
   }
diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc
index d52bf04..208ccf6 100644
--- a/runtime/native/java_lang_Object.cc
+++ b/runtime/native/java_lang_Object.cc
@@ -18,7 +18,7 @@
 
 #include "nativehelper/jni_macros.h"
 
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/object-inl.h"
 #include "native_util.h"
 #include "scoped_fast_native_object_access-inl.h"
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index b5aea7c..8976058 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -19,7 +19,7 @@
 #include "nativehelper/jni_macros.h"
 
 #include "common_throws.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/array.h"
 #include "mirror/object-inl.h"
 #include "mirror/string-inl.h"
diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc
index 136a02f..07e875e 100644
--- a/runtime/native/java_lang_StringFactory.cc
+++ b/runtime/native/java_lang_StringFactory.cc
@@ -17,7 +17,7 @@
 #include "java_lang_StringFactory.h"
 
 #include "common_throws.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/object-inl.h"
 #include "mirror/string.h"
 #include "native_util.h"
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 390f026..2c4184c 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -20,7 +20,7 @@
 
 #include "common_throws.h"
 #include "gc/accounting/card_table-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/array.h"
 #include "mirror/class-inl.h"
 #include "mirror/class.h"
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index 9a52f70..9edb0c2 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -17,7 +17,7 @@
 #include "java_lang_Thread.h"
 
 #include "common_throws.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/object.h"
 #include "monitor.h"
 #include "native_util.h"
diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc
index 03b7f9d..b5ef7d8 100644
--- a/runtime/native/java_lang_Throwable.cc
+++ b/runtime/native/java_lang_Throwable.cc
@@ -18,7 +18,7 @@
 
 #include "nativehelper/jni_macros.h"
 
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "native_util.h"
 #include "scoped_fast_native_object_access-inl.h"
 #include "thread.h"
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 44585fc..0630737 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -20,7 +20,7 @@
 #include "class_linker.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file_loader.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
 #include "native_util.h"
diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
index 2e3b4d4..1f2bf09 100644
--- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc
+++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
@@ -20,7 +20,7 @@
 
 #include "art_method.h"
 #include "handle_scope-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/field.h"
 #include "mirror/method.h"
 #include "mirror/method_handle_impl.h"
diff --git a/runtime/native/java_lang_ref_FinalizerReference.cc b/runtime/native/java_lang_ref_FinalizerReference.cc
index 72af5f7..c89188c 100644
--- a/runtime/native/java_lang_ref_FinalizerReference.cc
+++ b/runtime/native/java_lang_ref_FinalizerReference.cc
@@ -20,7 +20,7 @@
 
 #include "gc/heap.h"
 #include "gc/reference_processor.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/object-inl.h"
 #include "mirror/reference-inl.h"
 #include "native_util.h"
diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc
index 524a18c..fc018d1 100644
--- a/runtime/native/java_lang_ref_Reference.cc
+++ b/runtime/native/java_lang_ref_Reference.cc
@@ -20,7 +20,7 @@
 
 #include "gc/heap.h"
 #include "gc/reference_processor.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/object-inl.h"
 #include "mirror/reference-inl.h"
 #include "native_util.h"
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index d28f741..8bcda10 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -22,7 +22,7 @@
 #include "common_throws.h"
 #include "dex/dex_file-inl.h"
 #include "handle_scope-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "native_util.h"
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 8612438..13a8d28 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -23,7 +23,7 @@
 #include "class_linker-inl.h"
 #include "class_linker.h"
 #include "dex/dex_file_annotations.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
@@ -38,7 +38,7 @@
   ScopedFastNativeObjectAccess soa(env);
   ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod)
       ->GetInterfaceMethodIfProxy(kRuntimePointerSize);
-  mirror::ObjectArray<mirror::Class>* result_array =
+  ObjPtr<mirror::ObjectArray<mirror::Class>> result_array =
       annotations::GetExceptionTypesForMethod(method);
   if (result_array == nullptr) {
     // Return an empty array instead of a null pointer.
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index b129c66..9a2d302 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -22,7 +22,7 @@
 #include "art_method-inl.h"
 #include "dex/dex_file_annotations.h"
 #include "handle.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 13275d9..2559984 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -26,7 +26,8 @@
 #include "common_throws.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_annotations.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
+#include "jvalue-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/field-inl.h"
 #include "native_util.h"
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index 4355c06..52e0494 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -23,7 +23,7 @@
 #include "class_linker-inl.h"
 #include "class_linker.h"
 #include "dex/dex_file_annotations.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
@@ -62,7 +62,7 @@
         klass->GetProxyThrows()->Get(throws_index);
     return soa.AddLocalReference<jobjectArray>(declared_exceptions->Clone(soa.Self()));
   } else {
-    mirror::ObjectArray<mirror::Class>* result_array =
+    ObjPtr<mirror::ObjectArray<mirror::Class>> result_array =
         annotations::GetExceptionTypesForMethod(method);
     if (result_array == nullptr) {
       // Return an empty array instead of a null pointer
diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc
index b80b20c..263a567 100644
--- a/runtime/native/java_lang_reflect_Parameter.cc
+++ b/runtime/native/java_lang_reflect_Parameter.cc
@@ -24,7 +24,7 @@
 #include "common_throws.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_annotations.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "native_util.h"
 #include "scoped_fast_native_object_access-inl.h"
 
diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc
index 691ed28..f723ed2 100644
--- a/runtime/native/java_lang_reflect_Proxy.cc
+++ b/runtime/native/java_lang_reflect_Proxy.cc
@@ -19,7 +19,7 @@
 #include "nativehelper/jni_macros.h"
 
 #include "class_linker.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/object_array.h"
 #include "mirror/string.h"
diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc
index c003297..fa288ed 100644
--- a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc
+++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc
@@ -21,7 +21,7 @@
 #include "arch/instruction_set.h"
 #include "base/atomic.h"
 #include "base/quasi_atomic.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "native_util.h"
 
 namespace art {
diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc
index f3aba25..2429804 100644
--- a/runtime/native/libcore_util_CharsetUtils.cc
+++ b/runtime/native/libcore_util_CharsetUtils.cc
@@ -18,7 +18,7 @@
 
 #include <string.h>
 
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/string-inl.h"
 #include "mirror/string.h"
 #include "native_util.h"
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index 8f8fd71..419aed8 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -20,7 +20,7 @@
 
 #include "base/array_ref.h"
 #include "debugger.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "native_util.h"
 #include "nativehelper/jni_macros.h"
 #include "nativehelper/scoped_primitive_array.h"
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index fbee7b3..028675d 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -22,7 +22,7 @@
 #include "base/mutex.h"
 #include "debugger.h"
 #include "gc/heap.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "native_util.h"
 #include "nativehelper/jni_macros.h"
 #include "nativehelper/scoped_local_ref.h"
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index fb00ae3..d41a195 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -27,7 +27,7 @@
 #include "base/quasi_atomic.h"
 #include "common_throws.h"
 #include "gc/accounting/card_table-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/array.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index 7d72805..def48e8 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -25,7 +25,7 @@
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
 #include "dex/dex_file-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "sigchain.h"
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 14f3f45..b3a47c3 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -289,8 +289,10 @@
                      ArtMethod* current_method,
                      void* ucontext_ptr,
                      bool skip_frames) {
-  // b/18119146
-  if (RUNNING_ON_MEMORY_TOOL != 0) {
+  // Historical note: This was disabled when running under Valgrind (b/18119146).
+  // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+  // check whether this test works with ASan.
+  if (kRunningOnMemoryTool) {
     return;
   }
 
diff --git a/runtime/non_debuggable_classes.cc b/runtime/non_debuggable_classes.cc
index 8484e2c..f42a2d6 100644
--- a/runtime/non_debuggable_classes.cc
+++ b/runtime/non_debuggable_classes.cc
@@ -16,7 +16,7 @@
 
 #include "non_debuggable_classes.h"
 
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "obj_ptr-inl.h"
diff --git a/runtime/oat.h b/runtime/oat.h
index 0318606..7b8f71a 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: Use rMR as temp in Baker RB introspection marking.
-  static constexpr uint8_t kOatVersion[] = { '1', '4', '1', '\0' };
+  // Last oat version changed reason: Refactor stackmap encoding.
+  static constexpr uint8_t kOatVersion[] = { '1', '4', '4', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index c7a558c..ffbc26c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -97,7 +97,8 @@
   virtual ~OatFileBase() {}
 
   template <typename kOatFileBaseSubType>
-  static OatFileBase* OpenOatFile(const std::string& vdex_filename,
+  static OatFileBase* OpenOatFile(int zip_fd,
+                                  const std::string& vdex_filename,
                                   const std::string& elf_filename,
                                   const std::string& location,
                                   uint8_t* requested_base,
@@ -109,7 +110,8 @@
                                   std::string* error_msg);
 
   template <typename kOatFileBaseSubType>
-  static OatFileBase* OpenOatFile(int vdex_fd,
+  static OatFileBase* OpenOatFile(int zip_fd,
+                                  int vdex_fd,
                                   int oat_fd,
                                   const std::string& vdex_filename,
                                   const std::string& oat_filename,
@@ -160,7 +162,7 @@
 
   virtual void PreSetup(const std::string& elf_filename) = 0;
 
-  bool Setup(const char* abs_dex_location, std::string* error_msg);
+  bool Setup(int zip_fd, const char* abs_dex_location, std::string* error_msg);
 
   // Setters exposed for ElfOatFile.
 
@@ -181,7 +183,8 @@
 };
 
 template <typename kOatFileBaseSubType>
-OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename,
+OatFileBase* OatFileBase::OpenOatFile(int zip_fd,
+                                      const std::string& vdex_filename,
                                       const std::string& elf_filename,
                                       const std::string& location,
                                       uint8_t* requested_base,
@@ -208,13 +211,13 @@
     return nullptr;
   }
 
+  ret->PreSetup(elf_filename);
+
   if (!ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) {
     return nullptr;
   }
 
-  ret->PreSetup(elf_filename);
-
-  if (!ret->Setup(abs_dex_location, error_msg)) {
+  if (!ret->Setup(zip_fd, abs_dex_location, error_msg)) {
     return nullptr;
   }
 
@@ -222,7 +225,8 @@
 }
 
 template <typename kOatFileBaseSubType>
-OatFileBase* OatFileBase::OpenOatFile(int vdex_fd,
+OatFileBase* OatFileBase::OpenOatFile(int zip_fd,
+                                      int vdex_fd,
                                       int oat_fd,
                                       const std::string& vdex_location,
                                       const std::string& oat_location,
@@ -248,13 +252,13 @@
     return nullptr;
   }
 
+  ret->PreSetup(oat_location);
+
   if (!ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) {
     return nullptr;
   }
 
-  ret->PreSetup(oat_location);
-
-  if (!ret->Setup(abs_dex_location, error_msg)) {
+  if (!ret->Setup(zip_fd, abs_dex_location, error_msg)) {
     return nullptr;
   }
 
@@ -485,7 +489,7 @@
   }
 }
 
-bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
+bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
     std::string cause = GetOatHeader().GetValidationErrorMessage();
     *error_msg = StringPrintf("Invalid oat header for '%s': %s",
@@ -641,12 +645,23 @@
         uncompressed_dex_files_.reset(new std::vector<std::unique_ptr<const DexFile>>());
         // No dex files, load it from location.
         const ArtDexFileLoader dex_file_loader;
-        if (!dex_file_loader.Open(dex_file_location.c_str(),
-                                  dex_file_location,
-                                  /* verify */ false,
-                                  /* verify_checksum */ false,
-                                  error_msg,
-                                  uncompressed_dex_files_.get())) {
+        bool loaded = false;
+        if (zip_fd != -1) {
+          loaded = dex_file_loader.OpenZip(zip_fd,
+                                           dex_file_location,
+                                           /* verify */ false,
+                                           /* verify_checksum */ false,
+                                           error_msg,
+                                           uncompressed_dex_files_.get());
+        } else {
+          loaded = dex_file_loader.Open(dex_file_location.c_str(),
+                                        dex_file_location,
+                                        /* verify */ false,
+                                        /* verify_checksum */ false,
+                                        error_msg,
+                                        uncompressed_dex_files_.get());
+        }
+        if (!loaded) {
           if (Runtime::Current() == nullptr) {
             // If there's no runtime, we're running oatdump, so return
             // a half constructed oat file that oatdump knows how to deal with.
@@ -1144,7 +1159,8 @@
  public:
   ElfOatFile(const std::string& filename, bool executable) : OatFileBase(filename, executable) {}
 
-  static ElfOatFile* OpenElfFile(File* file,
+  static ElfOatFile* OpenElfFile(int zip_fd,
+                                 File* file,
                                  const std::string& location,
                                  uint8_t* requested_base,
                                  uint8_t* oat_file_begin,  // Override base if not null
@@ -1154,7 +1170,8 @@
                                  const char* abs_dex_location,
                                  std::string* error_msg);
 
-  bool InitializeFromElfFile(ElfFile* elf_file,
+  bool InitializeFromElfFile(int zip_fd,
+                             ElfFile* elf_file,
                              VdexFile* vdex_file,
                              const char* abs_dex_location,
                              std::string* error_msg);
@@ -1204,7 +1221,8 @@
   DISALLOW_COPY_AND_ASSIGN(ElfOatFile);
 };
 
-ElfOatFile* ElfOatFile::OpenElfFile(File* file,
+ElfOatFile* ElfOatFile::OpenElfFile(int zip_fd,
+                                    File* file,
                                     const std::string& location,
                                     uint8_t* requested_base,
                                     uint8_t* oat_file_begin,  // Override base if not null
@@ -1231,14 +1249,15 @@
     return nullptr;
   }
 
-  if (!oat_file->Setup(abs_dex_location, error_msg)) {
+  if (!oat_file->Setup(zip_fd, abs_dex_location, error_msg)) {
     return nullptr;
   }
 
   return oat_file.release();
 }
 
-bool ElfOatFile::InitializeFromElfFile(ElfFile* elf_file,
+bool ElfOatFile::InitializeFromElfFile(int zip_fd,
+                                       ElfFile* elf_file,
                                        VdexFile* vdex_file,
                                        const char* abs_dex_location,
                                        std::string* error_msg) {
@@ -1255,7 +1274,7 @@
   SetBegin(elf_file->Begin() + offset);
   SetEnd(elf_file->Begin() + size + offset);
   // Ignore the optional .bss section when opening non-executable.
-  return Setup(abs_dex_location, error_msg);
+  return Setup(zip_fd, abs_dex_location, error_msg);
 }
 
 bool ElfOatFile::Load(const std::string& elf_filename,
@@ -1356,18 +1375,20 @@
   CHECK(!location.empty());
 }
 
-OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file,
+OatFile* OatFile::OpenWithElfFile(int zip_fd,
+                                  ElfFile* elf_file,
                                   VdexFile* vdex_file,
                                   const std::string& location,
                                   const char* abs_dex_location,
                                   std::string* error_msg) {
   std::unique_ptr<ElfOatFile> oat_file(new ElfOatFile(location, false /* executable */));
-  return oat_file->InitializeFromElfFile(elf_file, vdex_file, abs_dex_location, error_msg)
+  return oat_file->InitializeFromElfFile(zip_fd, elf_file, vdex_file, abs_dex_location, error_msg)
       ? oat_file.release()
       : nullptr;
 }
 
-OatFile* OatFile::Open(const std::string& oat_filename,
+OatFile* OatFile::Open(int zip_fd,
+                       const std::string& oat_filename,
                        const std::string& oat_location,
                        uint8_t* requested_base,
                        uint8_t* oat_file_begin,
@@ -1392,7 +1413,8 @@
 
   // Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is
   // disabled.
-  OatFile* with_dlopen = OatFileBase::OpenOatFile<DlOpenOatFile>(vdex_filename,
+  OatFile* with_dlopen = OatFileBase::OpenOatFile<DlOpenOatFile>(zip_fd,
+                                                                 vdex_filename,
                                                                  oat_filename,
                                                                  oat_location,
                                                                  requested_base,
@@ -1421,7 +1443,8 @@
   //
   // Another independent reason is the absolute placement of boot.oat. dlopen on the host usually
   // does honor the virtual address encoded in the ELF file only for ET_EXEC files, not ET_DYN.
-  OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(vdex_filename,
+  OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(zip_fd,
+                                                                vdex_filename,
                                                                 oat_filename,
                                                                 oat_location,
                                                                 requested_base,
@@ -1434,7 +1457,8 @@
   return with_internal;
 }
 
-OatFile* OatFile::Open(int vdex_fd,
+OatFile* OatFile::Open(int zip_fd,
+                       int vdex_fd,
                        int oat_fd,
                        const std::string& oat_location,
                        uint8_t* requested_base,
@@ -1447,7 +1471,8 @@
 
   std::string vdex_location = GetVdexFilename(oat_location);
 
-  OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(vdex_fd,
+  OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(zip_fd,
+                                                                vdex_fd,
                                                                 oat_fd,
                                                                 vdex_location,
                                                                 oat_location,
@@ -1461,12 +1486,14 @@
   return with_internal;
 }
 
-OatFile* OatFile::OpenWritable(File* file,
+OatFile* OatFile::OpenWritable(int zip_fd,
+                               File* file,
                                const std::string& location,
                                const char* abs_dex_location,
                                std::string* error_msg) {
   CheckLocation(location);
-  return ElfOatFile::OpenElfFile(file,
+  return ElfOatFile::OpenElfFile(zip_fd,
+                                 file,
                                  location,
                                  nullptr,
                                  nullptr,
@@ -1477,12 +1504,14 @@
                                  error_msg);
 }
 
-OatFile* OatFile::OpenReadable(File* file,
+OatFile* OatFile::OpenReadable(int zip_fd,
+                               File* file,
                                const std::string& location,
                                const char* abs_dex_location,
                                std::string* error_msg) {
   CheckLocation(location);
-  return ElfOatFile::OpenElfFile(file,
+  return ElfOatFile::OpenElfFile(zip_fd,
+                                 file,
                                  location,
                                  nullptr,
                                  nullptr,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 6494b4c..8e18cee 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -74,7 +74,8 @@
 
   // Opens an oat file contained within the given elf file. This is always opened as
   // non-executable at the moment.
-  static OatFile* OpenWithElfFile(ElfFile* elf_file,
+  static OatFile* OpenWithElfFile(int zip_fd,
+                                  ElfFile* elf_file,
                                   VdexFile* vdex_file,
                                   const std::string& location,
                                   const char* abs_dex_location,
@@ -83,7 +84,8 @@
   // optionally be used to request where the file should be loaded.
   // See the ResolveRelativeEncodedDexLocation for a description of how the
   // abs_dex_location argument is used.
-  static OatFile* Open(const std::string& filename,
+  static OatFile* Open(int zip_fd,
+                       const std::string& filename,
                        const std::string& location,
                        uint8_t* requested_base,
                        uint8_t* oat_file_begin,
@@ -93,8 +95,10 @@
                        std::string* error_msg);
 
   // Similar to OatFile::Open(const std::string...), but accepts input vdex and
-  // odex files as file descriptors.
-  static OatFile* Open(int vdex_fd,
+  // odex files as file descriptors. We also take zip_fd in case the vdex does not
+  // contain the dex code, and we need to read it from the zip file.
+  static OatFile* Open(int zip_fd,
+                       int vdex_fd,
                        int oat_fd,
                        const std::string& oat_location,
                        uint8_t* requested_base,
@@ -109,11 +113,15 @@
   // where relocations may be required. Currently used from
   // ImageWriter which wants to open a writable version from an existing
   // file descriptor for patching.
-  static OatFile* OpenWritable(File* file, const std::string& location,
+  static OatFile* OpenWritable(int zip_fd,
+                               File* file,
+                               const std::string& location,
                                const char* abs_dex_location,
                                std::string* error_msg);
   // Open an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
-  static OatFile* OpenReadable(File* file, const std::string& location,
+  static OatFile* OpenReadable(int zip_fd,
+                               File* file,
+                               const std::string& location,
                                const char* abs_dex_location,
                                std::string* error_msg);
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 718f917..6c869ca 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -36,6 +36,7 @@
 #include "exec_utils.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
+#include "hidden_api.h"
 #include "image.h"
 #include "oat.h"
 #include "runtime.h"
@@ -118,7 +119,7 @@
   std::string error_msg;
   std::string odex_file_name;
   if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) {
-    odex_.Reset(odex_file_name, UseFdToReadFiles(), vdex_fd, oat_fd);
+    odex_.Reset(odex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd);
   } else {
     LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
   }
@@ -823,6 +824,11 @@
     argv.push_back("--compiler-filter=verify-none");
   }
 
+  if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
+    argv.push_back("--runtime-arg");
+    argv.push_back("-Xhidden-api-checks");
+  }
+
   if (runtime->MustRelocateIfPossible()) {
     argv.push_back("--runtime-arg");
     argv.push_back("-Xrelocate");
@@ -860,6 +866,13 @@
   CHECK(oat_filename != nullptr);
   CHECK(error_msg != nullptr);
 
+  // If ANDROID_DATA is not set, return false instead of aborting.
+  // This can occur for preopt when using a class loader context.
+  if (GetAndroidDataSafe(error_msg) == nullptr) {
+    *error_msg = "GetAndroidDataSafe failed: " + *error_msg;
+    return false;
+  }
+
   std::string cache_dir = GetDalvikCache(GetInstructionSetString(isa));
   if (cache_dir.empty()) {
     *error_msg = "Dalvik cache directory does not exist";
@@ -1148,7 +1161,8 @@
       std::string error_msg;
       if (use_fd_) {
         if (oat_fd_ >= 0 && vdex_fd_ >= 0) {
-          file_.reset(OatFile::Open(vdex_fd_,
+          file_.reset(OatFile::Open(zip_fd_,
+                                    vdex_fd_,
                                     oat_fd_,
                                     filename_.c_str(),
                                     nullptr,
@@ -1159,7 +1173,8 @@
                                     &error_msg));
         }
       } else {
-        file_.reset(OatFile::Open(filename_.c_str(),
+        file_.reset(OatFile::Open(/* zip_fd */ -1,
+                                  filename_.c_str(),
                                   filename_.c_str(),
                                   nullptr,
                                   nullptr,
@@ -1215,7 +1230,9 @@
     return false;
   }
 
-  bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext());
+
+  const bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) !=
+      ClassLoaderContext::VerificationResult::kMismatch;
   if (!result) {
     VLOG(oat) << "ClassLoaderContext check failed. Context was "
               << file->GetClassLoaderContext()
@@ -1235,11 +1252,15 @@
   status_attempted_ = false;
 }
 
-void OatFileAssistant::OatFileInfo::Reset(const std::string& filename, bool use_fd, int vdex_fd,
+void OatFileAssistant::OatFileInfo::Reset(const std::string& filename,
+                                          bool use_fd,
+                                          int zip_fd,
+                                          int vdex_fd,
                                           int oat_fd) {
   filename_provided_ = true;
   filename_ = filename;
   use_fd_ = use_fd;
+  zip_fd_ = zip_fd;
   vdex_fd_ = vdex_fd;
   oat_fd_ = oat_fd;
   Reset();
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 8d6ec00..a6d0961 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -378,7 +378,11 @@
 
     // Clear any cached information and switch to getting info about the oat
     // file with the given filename.
-    void Reset(const std::string& filename, bool use_fd, int vdex_fd = -1, int oat_fd = -1);
+    void Reset(const std::string& filename,
+               bool use_fd,
+               int zip_fd = -1,
+               int vdex_fd = -1,
+               int oat_fd = -1);
 
     // Release the loaded oat file for runtime use.
     // Returns null if the oat file hasn't been loaded or is out of date.
@@ -415,6 +419,7 @@
     bool filename_provided_ = false;
     std::string filename_;
 
+    int zip_fd_ = -1;
     int oat_fd_ = -1;
     int vdex_fd_ = -1;
     bool use_fd_ = false;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 99bc0b2..0b3c61d 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -33,6 +33,7 @@
 #include "class_loader_context.h"
 #include "common_runtime_test.h"
 #include "dexopt_test.h"
+#include "hidden_api.h"
 #include "oat_file.h"
 #include "oat_file_manager.h"
 #include "scoped_thread_state_change-inl.h"
@@ -43,6 +44,8 @@
 static const std::string kSpecialSharedLibrary = "&";  // NOLINT [runtime/string] [4]
 static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr;
 
+static constexpr char kDex2oatCmdLineHiddenApiArg[] = " --runtime-arg -Xhidden-api-checks";
+
 class OatFileAssistantTest : public DexoptTest {
  public:
   void VerifyOptimizationStatus(const std::string& file,
@@ -1413,6 +1416,46 @@
       oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
 }
 
+TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiDisabled) {
+  hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
+      hiddenapi::EnforcementPolicy::kNoChecks);
+
+  std::string dex_location = GetScratchDir() + "/TestDexHiddenApiDisabled.jar";
+  Copy(GetDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  std::string error_msg;
+  int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+  EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  EXPECT_NE(nullptr, oat_file.get());
+
+  const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+  EXPECT_NE(nullptr, cmd_line);
+  EXPECT_EQ(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg));
+}
+
+TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiEnabled) {
+  hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
+      hiddenapi::EnforcementPolicy::kBlacklistOnly);
+
+  std::string dex_location = GetScratchDir() + "/TestDexHiddenApiEnabled.jar";
+  Copy(GetDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  std::string error_msg;
+  int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+  EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  EXPECT_NE(nullptr, oat_file.get());
+
+  const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+  EXPECT_NE(nullptr, cmd_line);
+  EXPECT_NE(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg));
+}
+
 TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
   std::string dex_location = GetScratchDir() + "/TestDex.jar";
   std::string context_location = GetScratchDir() + "/ContextDex.jar";
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index f6fb9de..59a1045 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -38,7 +38,7 @@
 #include "gc/scoped_gc_critical_section.h"
 #include "gc/space/image_space.h"
 #include "handle_scope-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
 #include "oat_file.h"
@@ -276,9 +276,19 @@
   }
 }
 
-static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded,
-                           std::vector<const DexFile*>& dex_files_unloaded,
-                           std::string* error_msg /*out*/) {
+static bool CheckClassCollision(const OatFile* oat_file,
+    const ClassLoaderContext* context,
+    std::string* error_msg /*out*/) {
+  std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles();
+
+  // Vector that holds the newly opened dex files live, this is done to prevent leaks.
+  std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
+
+  ScopedTrace st("Collision check");
+  // Add dex files from the oat file to check.
+  std::vector<const DexFile*> dex_files_unloaded;
+  AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files);
+
   // Generate type index information for each dex file.
   std::vector<TypeIndexInfo> loaded_types;
   for (const DexFile* dex_file : dex_files_loaded) {
@@ -355,9 +365,10 @@
 // against the following top element. If the descriptor is the same, it is now checked whether
 // the two elements agree on whether their dex file was from an already-loaded oat-file or the
 // new oat file. Any disagreement indicates a collision.
-bool OatFileManager::HasCollisions(const OatFile* oat_file,
-                                   const ClassLoaderContext* context,
-                                   std::string* error_msg /*out*/) const {
+OatFileManager::CheckCollisionResult OatFileManager::CheckCollision(
+    const OatFile* oat_file,
+    const ClassLoaderContext* context,
+    /*out*/ std::string* error_msg) const {
   DCHECK(oat_file != nullptr);
   DCHECK(error_msg != nullptr);
 
@@ -367,28 +378,59 @@
   // Note that this has correctness implications as we cannot guarantee that the class resolution
   // used during compilation is OK (b/37777332).
   if (context == nullptr) {
-      LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader";
-      return false;
+    LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader";
+    return CheckCollisionResult::kSkippedUnsupportedClassLoader;
   }
 
-  // If the pat file loading context matches the context used during compilation then we accept
+  // If the oat file loading context matches the context used during compilation then we accept
   // the oat file without addition checks
-  if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) {
-    return false;
+  ClassLoaderContext::VerificationResult result = context->VerifyClassLoaderContextMatch(
+      oat_file->GetClassLoaderContext(),
+      /*verify_names*/ true,
+      /*verify_checksums*/ true);
+  switch (result) {
+    case ClassLoaderContext::VerificationResult::kForcedToSkipChecks:
+      return CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary;
+    case ClassLoaderContext::VerificationResult::kMismatch:
+      // Mismatched context, do the actual collision check.
+      break;
+    case ClassLoaderContext::VerificationResult::kVerifies:
+      return CheckCollisionResult::kNoCollisions;
   }
 
   // The class loader context does not match. Perform a full duplicate classes check.
+  return CheckClassCollision(oat_file, context, error_msg)
+      ? CheckCollisionResult::kPerformedHasCollisions : CheckCollisionResult::kNoCollisions;
+}
 
-  std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles();
+bool OatFileManager::AcceptOatFile(CheckCollisionResult result) const {
+  // Take the file only if it has no collisions, or we must take it because of preopting.
+  // Also accept oat files for shared libraries and unsupported class loaders.
+  return result != CheckCollisionResult::kPerformedHasCollisions;
+}
 
-  // Vector that holds the newly opened dex files live, this is done to prevent leaks.
-  std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
-
-  ScopedTrace st("Collision check");
-  // Add dex files from the oat file to check.
-  std::vector<const DexFile*> dex_files_unloaded;
-  AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files);
-  return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg);
+bool OatFileManager::ShouldLoadAppImage(CheckCollisionResult check_collision_result,
+                                        const OatFile* source_oat_file,
+                                        ClassLoaderContext* context,
+                                        std::string* error_msg) {
+  Runtime* const runtime = Runtime::Current();
+  if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) {
+    // If we verified the class loader context (skipping due to the special marker doesn't
+    // count), then also avoid the collision check.
+    bool load_image = check_collision_result == CheckCollisionResult::kNoCollisions;
+    // If we skipped the collision check, we need to reverify to be sure its OK to load the
+    // image.
+    if (!load_image &&
+        check_collision_result ==
+            CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary) {
+      // We can load the app image only if there are no collisions. If we know the
+      // class loader but didn't do the full collision check in HasCollisions(),
+      // do it now. b/77342775
+      load_image = !CheckClassCollision(source_oat_file, context, error_msg);
+    }
+    return load_image;
+  }
+  return false;
 }
 
 std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
@@ -473,16 +515,17 @@
             << reinterpret_cast<uintptr_t>(oat_file.get())
             << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")";
 
+  CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions;
   if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
     // Prevent oat files from being loaded if no class_loader or dex_elements are provided.
     // This can happen when the deprecated DexFile.<init>(String) is called directly, and it
     // could load oat files without checking the classpath, which would be incorrect.
     // Take the file only if it has no collisions, or we must take it because of preopting.
-    bool accept_oat_file =
-        !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg);
+    check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg);
+    bool accept_oat_file = AcceptOatFile(check_collision_result);
     if (!accept_oat_file) {
       // Failed the collision check. Print warning.
-      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
+      if (runtime->IsDexFileFallbackEnabled()) {
         if (!oat_file_assistant.HasOriginalDexFiles()) {
           // We need to fallback but don't have original dex files. We have to
           // fallback to opening the existing oat file. This is potentially
@@ -529,10 +572,11 @@
       // We need to throw away the image space if we are debuggable but the oat-file source of the
       // image is not otherwise we might get classes with inlined methods or other such things.
       std::unique_ptr<gc::space::ImageSpace> image_space;
-      if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) {
+      if (ShouldLoadAppImage(check_collision_result,
+                             source_oat_file,
+                             context.get(),
+                             &error_msg)) {
         image_space = oat_file_assistant.OpenImageSpace(source_oat_file);
-      } else {
-        image_space = nullptr;
       }
       if (image_space != nullptr) {
         ScopedObjectAccess soa(self);
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 038474e..80456e9 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -108,23 +108,39 @@
   void SetOnlyUseSystemOatFiles();
 
  private:
+  enum class CheckCollisionResult {
+    kSkippedUnsupportedClassLoader,
+    kSkippedClassLoaderContextSharedLibrary,
+    kNoCollisions,
+    kPerformedHasCollisions,
+  };
+
   // Check that the class loader context of the given oat file matches the given context.
   // This will perform a check that all class loaders in the chain have the same type and
   // classpath.
   // If the context is null (which means the initial class loader was null or unsupported)
-  // this returns false.
+  // this returns kSkippedUnsupportedClassLoader.
   // If the context does not validate the method will check for duplicate class definitions of
   // the given oat file against the oat files (either from the class loaders if possible or all
   // non-boot oat files otherwise).
-  // Return true if there are any class definition collisions in the oat_file.
-  bool HasCollisions(const OatFile* oat_file,
-                     const ClassLoaderContext* context,
-                     /*out*/ std::string* error_msg) const
+  // Return kPerformedHasCollisions if there are any class definition collisions in the oat_file.
+  CheckCollisionResult CheckCollision(const OatFile* oat_file,
+                                      const ClassLoaderContext* context,
+                                      /*out*/ std::string* error_msg) const
       REQUIRES(!Locks::oat_file_manager_lock_);
 
   const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const
       REQUIRES(Locks::oat_file_manager_lock_);
 
+  // Return true if we should accept the oat file.
+  bool AcceptOatFile(CheckCollisionResult result) const;
+
+  // Return true if we should attempt to load the app image.
+  bool ShouldLoadAppImage(CheckCollisionResult check_collision_result,
+                          const OatFile* source_oat_file,
+                          ClassLoaderContext* context,
+                          std::string* error_msg);
+
   std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
   bool have_non_pic_oat_file_;
 
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index 89812f3..12dfe20 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -74,7 +74,8 @@
   std::string error_msg;
   ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
         dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   oat_location.c_str(),
                                                    oat_location.c_str(),
                                                    nullptr,
                                                    nullptr,
@@ -101,7 +102,8 @@
 
   // Ensure we can load that file. Just a precondition.
   {
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                     oat_location.c_str(),
                                                      oat_location.c_str(),
                                                      nullptr,
                                                      nullptr,
@@ -117,7 +119,8 @@
   Copy(GetTestDexFileName("MainUncompressed"), dex_location);
 
   // And try to load again.
-  std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+  std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
+                                                   oat_location.c_str(),
                                                    oat_location.c_str(),
                                                    nullptr,
                                                    nullptr,
diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc
index 98238e5..aed6bc5 100644
--- a/runtime/oat_quick_method_header.cc
+++ b/runtime/oat_quick_method_header.cc
@@ -19,6 +19,7 @@
 #include "art_method.h"
 #include "dex/dex_file_types.h"
 #include "scoped_thread_state_change-inl.h"
+#include "stack_map.h"
 #include "thread.h"
 
 namespace art {
@@ -42,11 +43,10 @@
   const void* entry_point = GetEntryPoint();
   uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(entry_point);
   if (IsOptimized()) {
-    CodeInfo code_info = GetOptimizedCodeInfo();
-    CodeInfoEncoding encoding = code_info.ExtractEncoding();
-    StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset, encoding);
+    CodeInfo code_info(this);
+    StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset);
     if (stack_map.IsValid()) {
-      return stack_map.GetDexPc(encoding.stack_map.encoding);
+      return stack_map.GetDexPc();
     }
   } else {
     DCHECK(method->IsNative());
@@ -71,18 +71,17 @@
   DCHECK(!method->IsNative());
   DCHECK(IsOptimized());
   // Search for the dex-to-pc mapping in stack maps.
-  CodeInfo code_info = GetOptimizedCodeInfo();
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
+  CodeInfo code_info(this);
 
   // All stack maps are stored in the same CodeItem section, safepoint stack
   // maps first, then catch stack maps. We use `is_for_catch_handler` to select
   // the order of iteration.
   StackMap stack_map =
-      LIKELY(is_for_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc, encoding)
-                                   : code_info.GetStackMapForDexPc(dex_pc, encoding);
+      LIKELY(is_for_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc)
+                                   : code_info.GetStackMapForDexPc(dex_pc);
   if (stack_map.IsValid()) {
     return reinterpret_cast<uintptr_t>(entry_point) +
-           stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA);
+           stack_map.GetNativePcOffset(kRuntimeISA);
   }
   if (abort_on_failure) {
     ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index f0966b7..d6762d6 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -22,7 +22,6 @@
 #include "base/utils.h"
 #include "method_info.h"
 #include "quick/quick_method_frame_info.h"
-#include "stack_map.h"
 
 namespace art {
 
@@ -75,10 +74,6 @@
     return code_ - vmap_table_offset_;
   }
 
-  CodeInfo GetOptimizedCodeInfo() const {
-    return CodeInfo(GetOptimizedCodeInfoPtr());
-  }
-
   const void* GetOptimizedMethodInfoPtr() const {
     DCHECK(IsOptimized());
     return reinterpret_cast<const void*>(code_ - method_info_offset_);
diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h
index 14fdba3..e421d87 100644
--- a/runtime/obj_ptr.h
+++ b/runtime/obj_ptr.h
@@ -20,9 +20,9 @@
 #include <ostream>
 #include <type_traits>
 
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/mutex.h"  // For Locks::mutator_lock_.
-#include "globals.h"
 
 namespace art {
 
diff --git a/runtime/offsets.h b/runtime/offsets.h
index aaf5c0c..4df9b27 100644
--- a/runtime/offsets.h
+++ b/runtime/offsets.h
@@ -20,7 +20,7 @@
 #include <ostream>
 
 #include "base/enums.h"
-#include "globals.h"
+#include "base/globals.h"
 
 namespace art {
 
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 5518eb2..7383d47 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -194,6 +194,9 @@
       .Define("-Xjittransitionweight:_")
           .WithType<unsigned int>()
           .IntoKey(M::JITInvokeTransitionWeight)
+      .Define("-Xjitpthreadpriority:_")
+          .WithType<int>()
+          .IntoKey(M::JITPoolThreadPthreadPriority)
       .Define("-Xjitsaveprofilinginfo")
           .WithType<ProfileSaverOptions>()
           .AppendValues()
@@ -252,12 +255,6 @@
       .Define("-Xstackdumplockprofthreshold:_")
           .WithType<unsigned int>()
           .IntoKey(M::StackDumpLockProfThreshold)
-      .Define("-Xusetombstonedtraces")
-          .WithValue(true)
-          .IntoKey(M::UseTombstonedTraces)
-      .Define("-Xstacktracefile:_")
-          .WithType<std::string>()
-          .IntoKey(M::StackTraceFile)
       .Define("-Xmethod-trace")
           .IntoKey(M::MethodTrace)
       .Define("-Xmethod-trace-file:_")
@@ -699,7 +696,6 @@
   UsageMessage(stream, "The following Dalvik options are supported:\n");
   UsageMessage(stream, "  -Xzygote\n");
   UsageMessage(stream, "  -Xjnitrace:substring (eg NativeClass or nativeMethod)\n");
-  UsageMessage(stream, "  -Xstacktracefile:<filename>\n");
   UsageMessage(stream, "  -Xgc:[no]preverify\n");
   UsageMessage(stream, "  -Xgc:[no]postverify\n");
   UsageMessage(stream, "  -XX:HeapGrowthLimit=N\n");
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 0f8555a..8c77d39 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -23,9 +23,9 @@
 #include <jni.h>
 
 #include "arch/instruction_set.h"
+#include "base/globals.h"
 #include "gc/collector_type.h"
 #include "gc/space/large_object_space.h"
-#include "globals.h"
 // #include "jit/profile_saver_options.h"
 #include "runtime_options.h"
 
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 3ad3a4b..4e0bf89 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -18,97 +18,16 @@
 #include <vector>
 
 #include "art_field-inl.h"
-#include "art_method-inl.h"
 #include "base/enums.h"
-#include "class_linker-inl.h"
 #include "common_compiler_test.h"
-#include "mirror/class-inl.h"
 #include "mirror/field-inl.h"
-#include "mirror/method.h"
+#include "proxy_test.h"
 #include "scoped_thread_state_change-inl.h"
 
 namespace art {
+namespace proxy_test {
 
-class ProxyTest : public CommonCompilerTest {
- public:
-  // Generate a proxy class with the given name and interfaces. This is a simplification from what
-  // libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and
-  // we do not declare exceptions.
-  mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, jobject jclass_loader,
-                                    const char* className,
-                                    const std::vector<mirror::Class*>& interfaces)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::Class* javaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
-    CHECK(javaLangObject != nullptr);
-
-    jclass javaLangClass = soa.AddLocalReference<jclass>(mirror::Class::GetJavaLangClass());
-
-    // Builds the interfaces array.
-    jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass,
-                                                                  nullptr);
-    soa.Self()->AssertNoPendingException();
-    for (size_t i = 0; i < interfaces.size(); ++i) {
-      soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i,
-                                       soa.AddLocalReference<jclass>(interfaces[i]));
-    }
-
-    // Builds the method array.
-    jsize methods_count = 3;  // Object.equals, Object.hashCode and Object.toString.
-    for (mirror::Class* interface : interfaces) {
-      methods_count += interface->NumVirtualMethods();
-    }
-    jobjectArray proxyClassMethods = soa.Env()->NewObjectArray(
-        methods_count, soa.AddLocalReference<jclass>(mirror::Method::StaticClass()), nullptr);
-    soa.Self()->AssertNoPendingException();
-
-    jsize array_index = 0;
-    // Fill the method array
-    DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
-    ArtMethod* method = javaLangObject->FindClassMethod(
-        "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize);
-    CHECK(method != nullptr);
-    CHECK(!method->IsDirect());
-    CHECK(method->GetDeclaringClass() == javaLangObject);
-    DCHECK(!Runtime::Current()->IsActiveTransaction());
-    soa.Env()->SetObjectArrayElement(
-        proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
-            mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
-    method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize);
-    CHECK(method != nullptr);
-    CHECK(!method->IsDirect());
-    CHECK(method->GetDeclaringClass() == javaLangObject);
-    soa.Env()->SetObjectArrayElement(
-        proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
-            mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
-    method = javaLangObject->FindClassMethod(
-        "toString", "()Ljava/lang/String;", kRuntimePointerSize);
-    CHECK(method != nullptr);
-    CHECK(!method->IsDirect());
-    CHECK(method->GetDeclaringClass() == javaLangObject);
-    soa.Env()->SetObjectArrayElement(
-        proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
-            mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
-    // Now adds all interfaces virtual methods.
-    for (mirror::Class* interface : interfaces) {
-      for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) {
-        soa.Env()->SetObjectArrayElement(
-            proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
-                mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), &m)));
-      }
-    }
-    CHECK_EQ(array_index, methods_count);
-
-    // Builds an empty exception array.
-    jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr);
-    soa.Self()->AssertNoPendingException();
-
-    mirror::Class* proxyClass = class_linker_->CreateProxyClass(
-        soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader,
-        proxyClassMethods, proxyClassThrows);
-    soa.Self()->AssertNoPendingException();
-    return proxyClass;
-  }
-};
+class ProxyTest : public CommonRuntimeTest {};
 
 // Creates a proxy class and check ClassHelper works correctly.
 TEST_F(ProxyTest, ProxyClassHelper) {
@@ -129,7 +48,7 @@
   interfaces.push_back(I.Get());
   interfaces.push_back(J.Get());
   Handle<mirror::Class> proxy_class(hs.NewHandle(
-      GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)));
+      GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces)));
   interfaces.clear();  // Don't least possibly stale objects in the array as good practice.
   ASSERT_TRUE(proxy_class != nullptr);
   ASSERT_TRUE(proxy_class->IsProxyClass());
@@ -164,7 +83,8 @@
     std::vector<mirror::Class*> interfaces;
     interfaces.push_back(I.Get());
     interfaces.push_back(J.Get());
-    proxyClass = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces));
+    proxyClass = hs.NewHandle(
+        GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces));
   }
 
   ASSERT_TRUE(proxyClass != nullptr);
@@ -212,8 +132,10 @@
   Handle<mirror::Class> proxyClass1;
   {
     std::vector<mirror::Class*> interfaces;
-    proxyClass0 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy0", interfaces));
-    proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces));
+    proxyClass0 = hs.NewHandle(
+        GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy0", interfaces));
+    proxyClass1 = hs.NewHandle(
+        GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1", interfaces));
   }
 
   ASSERT_TRUE(proxyClass0 != nullptr);
@@ -255,4 +177,5 @@
   EXPECT_EQ(field11->GetArtField(), &static_fields1->At(1));
 }
 
+}  // namespace proxy_test
 }  // namespace art
diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h
new file mode 100644
index 0000000..b559823
--- /dev/null
+++ b/runtime/proxy_test.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 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_PROXY_TEST_H_
+#define ART_RUNTIME_PROXY_TEST_H_
+
+#include <jni.h>
+#include <vector>
+
+#include "art_method-inl.h"
+#include "class_linker-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/method.h"
+
+namespace art {
+namespace proxy_test {
+
+// Generate a proxy class with the given name and interfaces. This is a simplification from what
+// libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and
+// we do not declare exceptions.
+mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa,
+                                  jobject jclass_loader,
+                                  ClassLinker* class_linker,
+                                  const char* className,
+                                  const std::vector<mirror::Class*>& interfaces)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::Class* javaLangObject = class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
+  CHECK(javaLangObject != nullptr);
+
+  jclass javaLangClass = soa.AddLocalReference<jclass>(mirror::Class::GetJavaLangClass());
+
+  // Builds the interfaces array.
+  jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass,
+                                                                nullptr);
+  soa.Self()->AssertNoPendingException();
+  for (size_t i = 0; i < interfaces.size(); ++i) {
+    soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i,
+                                     soa.AddLocalReference<jclass>(interfaces[i]));
+  }
+
+  // Builds the method array.
+  jsize methods_count = 3;  // Object.equals, Object.hashCode and Object.toString.
+  for (mirror::Class* interface : interfaces) {
+    methods_count += interface->NumVirtualMethods();
+  }
+  jobjectArray proxyClassMethods = soa.Env()->NewObjectArray(
+      methods_count, soa.AddLocalReference<jclass>(mirror::Method::StaticClass()), nullptr);
+  soa.Self()->AssertNoPendingException();
+
+  jsize array_index = 0;
+  // Fill the method array
+  DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
+  ArtMethod* method = javaLangObject->FindClassMethod(
+      "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize);
+  CHECK(method != nullptr);
+  CHECK(!method->IsDirect());
+  CHECK(method->GetDeclaringClass() == javaLangObject);
+  DCHECK(!Runtime::Current()->IsActiveTransaction());
+  soa.Env()->SetObjectArrayElement(
+      proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
+          mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
+  method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize);
+  CHECK(method != nullptr);
+  CHECK(!method->IsDirect());
+  CHECK(method->GetDeclaringClass() == javaLangObject);
+  soa.Env()->SetObjectArrayElement(
+      proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
+          mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
+  method = javaLangObject->FindClassMethod(
+      "toString", "()Ljava/lang/String;", kRuntimePointerSize);
+  CHECK(method != nullptr);
+  CHECK(!method->IsDirect());
+  CHECK(method->GetDeclaringClass() == javaLangObject);
+  soa.Env()->SetObjectArrayElement(
+      proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
+          mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
+  // Now adds all interfaces virtual methods.
+  for (mirror::Class* interface : interfaces) {
+    for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) {
+      soa.Env()->SetObjectArrayElement(
+          proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
+              mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), &m)));
+    }
+  }
+  CHECK_EQ(array_index, methods_count);
+
+  // Builds an empty exception array.
+  jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr);
+  soa.Self()->AssertNoPendingException();
+
+  mirror::Class* proxyClass = class_linker->CreateProxyClass(
+      soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader,
+      proxyClassMethods, proxyClassThrows);
+  soa.Self()->AssertNoPendingException();
+  return proxyClass;
+}
+
+}  // namespace proxy_test
+}  // namespace art
+
+#endif  // ART_RUNTIME_PROXY_TEST_H_
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 077aa33..c555fca 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -224,30 +224,29 @@
 
   CodeItemDataAccessor accessor(handler_method_->DexInstructionData());
   const size_t number_of_vregs = accessor.RegistersSize();
-  CodeInfo code_info = handler_method_header_->GetOptimizedCodeInfo();
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
+  CodeInfo code_info(handler_method_header_);
 
   // Find stack map of the catch block.
-  StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc(), encoding);
+  StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc());
   DCHECK(catch_stack_map.IsValid());
   DexRegisterMap catch_vreg_map =
-      code_info.GetDexRegisterMapOf(catch_stack_map, encoding, number_of_vregs);
+      code_info.GetDexRegisterMapOf(catch_stack_map, number_of_vregs);
   if (!catch_vreg_map.IsValid()) {
     return;
   }
 
   // Find stack map of the throwing instruction.
   StackMap throw_stack_map =
-      code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset(), encoding);
+      code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset());
   DCHECK(throw_stack_map.IsValid());
   DexRegisterMap throw_vreg_map =
-      code_info.GetDexRegisterMapOf(throw_stack_map, encoding, number_of_vregs);
+      code_info.GetDexRegisterMapOf(throw_stack_map, number_of_vregs);
   DCHECK(throw_vreg_map.IsValid());
 
   // Copy values between them.
   for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
     DexRegisterLocation::Kind catch_location =
-        catch_vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding);
+        catch_vreg_map.GetLocationKind(vreg, number_of_vregs, code_info);
     if (catch_location == DexRegisterLocation::Kind::kNone) {
       continue;
     }
@@ -257,8 +256,7 @@
     uint32_t vreg_value;
     VRegKind vreg_kind = ToVRegKind(throw_vreg_map.GetLocationKind(vreg,
                                                                    number_of_vregs,
-                                                                   code_info,
-                                                                   encoding));
+                                                                   code_info));
     bool get_vreg_success = stack_visitor->GetVReg(stack_visitor->GetMethod(),
                                                    vreg,
                                                    vreg_kind,
@@ -271,8 +269,7 @@
     // Copy value to the catch phi's stack slot.
     int32_t slot_offset = catch_vreg_map.GetStackOffsetInBytes(vreg,
                                                                number_of_vregs,
-                                                               code_info,
-                                                               encoding);
+                                                               code_info);
     ArtMethod** frame_top = stack_visitor->GetCurrentQuickFrame();
     uint8_t* slot_address = reinterpret_cast<uint8_t*>(frame_top) + slot_offset;
     uint32_t* slot_ptr = reinterpret_cast<uint32_t*>(slot_address);
@@ -404,20 +401,18 @@
                                       const bool* updated_vregs)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
-    CodeInfo code_info = method_header->GetOptimizedCodeInfo();
+    CodeInfo code_info(method_header);
     uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc());
-    CodeInfoEncoding encoding = code_info.ExtractEncoding();
-    StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+    StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
     CodeItemDataAccessor accessor(m->DexInstructionData());
     const size_t number_of_vregs = accessor.RegistersSize();
-    uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map);
-    BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map);
+    uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map);
+    BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map);
     DexRegisterMap vreg_map = IsInInlinedFrame()
         ? code_info.GetDexRegisterMapAtDepth(GetCurrentInliningDepth() - 1,
-                                             code_info.GetInlineInfoOf(stack_map, encoding),
-                                             encoding,
+                                             code_info.GetInlineInfoOf(stack_map),
                                              number_of_vregs)
-        : code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs);
+        : code_info.GetDexRegisterMapOf(stack_map, number_of_vregs);
 
     if (!vreg_map.IsValid()) {
       return;
@@ -430,7 +425,7 @@
       }
 
       DexRegisterLocation::Kind location =
-          vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding);
+          vreg_map.GetLocationKind(vreg, number_of_vregs, code_info);
       static constexpr uint32_t kDeadValue = 0xEBADDE09;
       uint32_t value = kDeadValue;
       bool is_reference = false;
@@ -439,12 +434,11 @@
         case DexRegisterLocation::Kind::kInStack: {
           const int32_t offset = vreg_map.GetStackOffsetInBytes(vreg,
                                                                 number_of_vregs,
-                                                                code_info,
-                                                                encoding);
+                                                                code_info);
           const uint8_t* addr = reinterpret_cast<const uint8_t*>(GetCurrentQuickFrame()) + offset;
           value = *reinterpret_cast<const uint32_t*>(addr);
           uint32_t bit = (offset >> 2);
-          if (bit < encoding.stack_mask.encoding.BitSize() && stack_mask.LoadBit(bit)) {
+          if (bit < code_info.GetNumberOfStackMaskBits() && stack_mask.LoadBit(bit)) {
             is_reference = true;
           }
           break;
@@ -453,7 +447,7 @@
         case DexRegisterLocation::Kind::kInRegisterHigh:
         case DexRegisterLocation::Kind::kInFpuRegister:
         case DexRegisterLocation::Kind::kInFpuRegisterHigh: {
-          uint32_t reg = vreg_map.GetMachineRegister(vreg, number_of_vregs, code_info, encoding);
+          uint32_t reg = vreg_map.GetMachineRegister(vreg, number_of_vregs, code_info);
           bool result = GetRegisterIfAccessible(reg, ToVRegKind(location), &value);
           CHECK(result);
           if (location == DexRegisterLocation::Kind::kInRegister) {
@@ -464,7 +458,7 @@
           break;
         }
         case DexRegisterLocation::Kind::kConstant: {
-          value = vreg_map.GetConstant(vreg, number_of_vregs, code_info, encoding);
+          value = vreg_map.GetConstant(vreg, number_of_vregs, code_info);
           if (value == 0) {
             // Make it a reference for extra safety.
             is_reference = true;
@@ -479,8 +473,7 @@
               << "Unexpected location kind "
               << vreg_map.GetLocationInternalKind(vreg,
                                                   number_of_vregs,
-                                                  code_info,
-                                                  encoding);
+                                                  code_info);
           UNREACHABLE();
         }
       }
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 068bc28..66eba1e 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -23,8 +23,9 @@
 #include "common_throws.h"
 #include "dex/dex_file-inl.h"
 #include "indirect_reference_table-inl.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
+#include "jvalue-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/executable.h"
 #include "mirror/object_array-inl.h"
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index 7b36c73..d2d720f 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -23,8 +23,8 @@
 #include "base/enums.h"
 #include "common_compiler_test.h"
 #include "dex/descriptors_names.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "scoped_thread_state_change-inl.h"
 
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index 4584351..374591e 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -19,8 +19,10 @@
 
 #include "runtime.h"
 
+#include "arch/instruction_set.h"
 #include "art_method.h"
 #include "base/callee_save_type.h"
+#include "entrypoints/quick/callee_save_frame.h"
 #include "gc_root-inl.h"
 #include "obj_ptr-inl.h"
 
@@ -38,21 +40,22 @@
 
 inline QuickMethodFrameInfo Runtime::GetRuntimeMethodFrameInfo(ArtMethod* method) {
   DCHECK(method != nullptr);
+  DCHECK_EQ(instruction_set_, kRuntimeISA);
   // Cannot be imt-conflict-method or resolution-method.
   DCHECK_NE(method, GetImtConflictMethod());
   DCHECK_NE(method, GetResolutionMethod());
   // Don't use GetCalleeSaveMethod(), some tests don't set all callee save methods.
   if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveRefsAndArgs)) {
-    return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
+    return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
   } else if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveAllCalleeSaves)) {
-    return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveAllCalleeSaves);
+    return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveAllCalleeSaves);
   } else if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveRefsOnly)) {
-    return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsOnly);
+    return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsOnly);
   } else {
     DCHECK(method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverything) ||
            method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForClinit) ||
            method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForSuspendCheck));
-    return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveEverything);
+    return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveEverything);
   }
 }
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8f5295c..9196eb2 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -39,18 +39,12 @@
 #include "android-base/strings.h"
 
 #include "aot_class_linker.h"
-#include "arch/arm/quick_method_frame_info_arm.h"
 #include "arch/arm/registers_arm.h"
-#include "arch/arm64/quick_method_frame_info_arm64.h"
 #include "arch/arm64/registers_arm64.h"
 #include "arch/instruction_set_features.h"
-#include "arch/mips/quick_method_frame_info_mips.h"
 #include "arch/mips/registers_mips.h"
-#include "arch/mips64/quick_method_frame_info_mips64.h"
 #include "arch/mips64/registers_mips64.h"
-#include "arch/x86/quick_method_frame_info_x86.h"
 #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 "art_field-inl.h"
 #include "art_method-inl.h"
@@ -93,11 +87,11 @@
 #include "instrumentation.h"
 #include "intern_table.h"
 #include "interpreter/interpreter.h"
-#include "java_vm_ext.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
 #include "jit/profile_saver.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "linear_alloc.h"
 #include "memory_representation.h"
 #include "mirror/array.h"
@@ -232,7 +226,6 @@
       intern_table_(nullptr),
       class_linker_(nullptr),
       signal_catcher_(nullptr),
-      use_tombstoned_traces_(false),
       java_vm_(nullptr),
       fault_message_lock_("Fault message lock"),
       fault_message_(""),
@@ -246,7 +239,7 @@
       exit_(nullptr),
       abort_(nullptr),
       stats_enabled_(false),
-      is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL),
+      is_running_on_memory_tool_(kRunningOnMemoryTool),
       instrumentation_(),
       main_thread_group_(nullptr),
       system_thread_group_(nullptr),
@@ -274,6 +267,7 @@
       pending_hidden_api_warning_(false),
       dedupe_hidden_api_warnings_(true),
       always_set_hidden_api_warning_flag_(false),
+      hidden_api_access_event_log_rate_(0),
       dump_native_stack_on_sig_quit_(true),
       pruned_dalvik_cache_(false),
       // Initially assume we perceive jank in case the process state is never updated.
@@ -861,7 +855,11 @@
 }
 
 void Runtime::InitNonZygoteOrPostFork(
-    JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa) {
+    JNIEnv* env,
+    bool is_system_server,
+    NativeBridgeAction action,
+    const char* isa,
+    bool profile_system_server) {
   is_zygote_ = false;
 
   if (is_native_bridge_loaded_) {
@@ -884,8 +882,15 @@
   heap_->ResetGcPerformanceInfo();
 
   // We may want to collect profiling samples for system server, but we never want to JIT there.
-  if ((!is_system_server || !jit_options_->UseJitCompilation()) &&
-      !safe_mode_ &&
+  if (is_system_server) {
+    jit_options_->SetUseJitCompilation(false);
+    jit_options_->SetSaveProfilingInfo(profile_system_server);
+    if (profile_system_server) {
+      jit_options_->SetWaitForJitNotificationsToSaveProfile(false);
+      VLOG(profiler) << "Enabling system server profiles";
+    }
+  }
+  if (!safe_mode_ &&
       (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) &&
       jit_ == nullptr) {
     // Note that when running ART standalone (not zygote, nor zygote fork),
@@ -904,7 +909,7 @@
 
 void Runtime::StartSignalCatcher() {
   if (!is_zygote_) {
-    signal_catcher_ = new SignalCatcher(stack_trace_file_, use_tombstoned_traces_);
+    signal_catcher_ = new SignalCatcher();
   }
 }
 
@@ -1002,7 +1007,8 @@
       return false;
     }
     std::unique_ptr<const OatFile> oat_file(
-        OatFile::OpenWithElfFile(elf_file.release(),
+        OatFile::OpenWithElfFile(/* zip_fd */ -1,
+                                 elf_file.release(),
                                  vdex_file.release(),
                                  oat_location,
                                  nullptr,
@@ -1151,12 +1157,6 @@
   abort_ = runtime_options.GetOrDefault(Opt::HookAbort);
 
   default_stack_size_ = runtime_options.GetOrDefault(Opt::StackSize);
-  use_tombstoned_traces_ = runtime_options.GetOrDefault(Opt::UseTombstonedTraces);
-#if !defined(ART_TARGET_ANDROID)
-  CHECK(!use_tombstoned_traces_)
-      << "-Xusetombstonedtraces is only supported in an Android environment";
-#endif
-  stack_trace_file_ = runtime_options.ReleaseOrDefault(Opt::StackTraceFile);
 
   compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler);
   compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);
@@ -1192,7 +1192,7 @@
   // As is, we're encoding some logic here about which specific policy to use, which would be better
   // controlled by the framework.
   hidden_api_policy_ = do_hidden_api_checks
-      ? hiddenapi::EnforcementPolicy::kBlacklistOnly
+      ? hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList
       : hiddenapi::EnforcementPolicy::kNoChecks;
 
   no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
@@ -1349,8 +1349,10 @@
     case InstructionSet::kMips:
     case InstructionSet::kMips64:
       implicit_null_checks_ = true;
-      // Installing stack protection does not play well with valgrind.
-      implicit_so_checks_ = !(RUNNING_ON_MEMORY_TOOL && kMemoryToolIsValgrind);
+      // Installing stack protection does not play well with Valgrind.
+      // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+      // check whether setting `implicit_so_checks_` to `true` works with ASan.
+      implicit_so_checks_ = !kRunningOnMemoryTool;
       break;
     default:
       // Keep the defaults.
@@ -1365,8 +1367,8 @@
       // 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().
+      // 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_) {
         new SuspensionHandler(&fault_manager);
       }
@@ -2197,38 +2199,21 @@
 
 void Runtime::SetInstructionSet(InstructionSet instruction_set) {
   instruction_set_ = instruction_set;
-  if ((instruction_set_ == InstructionSet::kThumb2) || (instruction_set_ == InstructionSet::kArm)) {
-    for (int i = 0; i != kCalleeSaveSize; ++i) {
-      CalleeSaveType type = static_cast<CalleeSaveType>(i);
-      callee_save_method_frame_infos_[i] = arm::ArmCalleeSaveMethodFrameInfo(type);
-    }
-  } else if (instruction_set_ == InstructionSet::kMips) {
-    for (int i = 0; i != kCalleeSaveSize; ++i) {
-      CalleeSaveType type = static_cast<CalleeSaveType>(i);
-      callee_save_method_frame_infos_[i] = mips::MipsCalleeSaveMethodFrameInfo(type);
-    }
-  } else if (instruction_set_ == InstructionSet::kMips64) {
-    for (int i = 0; i != kCalleeSaveSize; ++i) {
-      CalleeSaveType type = static_cast<CalleeSaveType>(i);
-      callee_save_method_frame_infos_[i] = mips64::Mips64CalleeSaveMethodFrameInfo(type);
-    }
-  } else if (instruction_set_ == InstructionSet::kX86) {
-    for (int i = 0; i != kCalleeSaveSize; ++i) {
-      CalleeSaveType type = static_cast<CalleeSaveType>(i);
-      callee_save_method_frame_infos_[i] = x86::X86CalleeSaveMethodFrameInfo(type);
-    }
-  } else if (instruction_set_ == InstructionSet::kX86_64) {
-    for (int i = 0; i != kCalleeSaveSize; ++i) {
-      CalleeSaveType type = static_cast<CalleeSaveType>(i);
-      callee_save_method_frame_infos_[i] = x86_64::X86_64CalleeSaveMethodFrameInfo(type);
-    }
-  } else if (instruction_set_ == InstructionSet::kArm64) {
-    for (int i = 0; i != kCalleeSaveSize; ++i) {
-      CalleeSaveType type = static_cast<CalleeSaveType>(i);
-      callee_save_method_frame_infos_[i] = arm64::Arm64CalleeSaveMethodFrameInfo(type);
-    }
-  } else {
-    UNIMPLEMENTED(FATAL) << instruction_set_;
+  switch (instruction_set) {
+    case InstructionSet::kThumb2:
+      // kThumb2 is the same as kArm, use the canonical value.
+      instruction_set_ = InstructionSet::kArm;
+      break;
+    case InstructionSet::kArm:
+    case InstructionSet::kArm64:
+    case InstructionSet::kMips:
+    case InstructionSet::kMips64:
+    case InstructionSet::kX86:
+    case InstructionSet::kX86_64:
+      break;
+    default:
+      UNIMPLEMENTED(FATAL) << instruction_set_;
+      UNREACHABLE();
   }
 }
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 1b7663c..10f72e7 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -399,10 +399,6 @@
   ArtMethod* GetCalleeSaveMethodUnchecked(CalleeSaveType type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  QuickMethodFrameInfo GetCalleeSaveMethodFrameInfo(CalleeSaveType type) const {
-    return callee_save_method_frame_infos_[static_cast<size_t>(type)];
-  }
-
   QuickMethodFrameInfo GetRuntimeMethodFrameInfo(ArtMethod* method)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -451,7 +447,11 @@
 
   void PreZygoteFork();
   void InitNonZygoteOrPostFork(
-      JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa);
+      JNIEnv* env,
+      bool is_system_server,
+      NativeBridgeAction action,
+      const char* isa,
+      bool profile_system_server = false);
 
   const instrumentation::Instrumentation* GetInstrumentation() const {
     return &instrumentation_;
@@ -569,6 +569,14 @@
     return always_set_hidden_api_warning_flag_;
   }
 
+  void SetHiddenApiEventLogSampleRate(uint32_t rate) {
+    hidden_api_access_event_log_rate_ = rate;
+  }
+
+  uint32_t GetHiddenApiEventLogSampleRate() const {
+    return hidden_api_access_event_log_rate_;
+  }
+
   bool IsDexFileFallbackEnabled() const {
     return allow_dex_file_fallback_;
   }
@@ -819,7 +827,6 @@
   GcRoot<mirror::Object> sentinel_;
 
   InstructionSet instruction_set_;
-  QuickMethodFrameInfo callee_save_method_frame_infos_[kCalleeSaveSize];
 
   CompilerCallbacks* compiler_callbacks_;
   bool is_zygote_;
@@ -871,14 +878,6 @@
 
   SignalCatcher* signal_catcher_;
 
-  // If true, the runtime will connect to tombstoned via a socket to
-  // request an open file descriptor to write its traces to.
-  bool use_tombstoned_traces_;
-
-  // Location to which traces must be written on SIGQUIT. Only used if
-  // tombstoned_traces_ == false.
-  std::string stack_trace_file_;
-
   std::unique_ptr<JavaVMExt> java_vm_;
 
   std::unique_ptr<jit::Jit> jit_;
@@ -1014,7 +1013,8 @@
   // Whether access checks on hidden API should be performed.
   hiddenapi::EnforcementPolicy hidden_api_policy_;
 
-  // List of signature prefixes of methods that have been removed from the blacklist
+  // List of signature prefixes of methods that have been removed from the blacklist, and treated
+  // as if whitelisted.
   std::vector<std::string> hidden_api_exemptions_;
 
   // Whether the application has used an API which is not restricted but we
@@ -1030,6 +1030,10 @@
   // when there is a warning. This is only used for testing.
   bool always_set_hidden_api_warning_flag_;
 
+  // How often to log hidden API access to the event log. An integer between 0 (never)
+  // and 0x10000 (always).
+  uint32_t hidden_api_access_event_log_rate_;
+
   // Whether threads should dump their native stack on SIGQUIT.
   bool dump_native_stack_on_sig_quit_;
 
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 72d9919..54769f9 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -339,8 +339,8 @@
 };
 
 TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) {
-  // SigQuit induces a dump. ASAN isn't happy with libunwind reading memory.
-  TEST_DISABLED_FOR_MEMORY_TOOL_ASAN();
+  // SigQuit induces a dump. ASan isn't happy with libunwind reading memory.
+  TEST_DISABLED_FOR_MEMORY_TOOL();
 
   // The runtime needs to be started for the signal handler.
   Thread* self = Thread::Current();
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 4121ad6..e647423 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -77,6 +77,7 @@
 RUNTIME_OPTIONS_KEY (unsigned int,        JITOsrThreshold)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITPriorityThreadWeight)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITInvokeTransitionWeight)
+RUNTIME_OPTIONS_KEY (int,                 JITPoolThreadPthreadPriority,   jit::kJitPoolThreadPthreadDefaultPriority)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           JITCodeCacheInitialCapacity,    jit::JitCodeCache::kInitialCapacity)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           JITCodeCacheMaxCapacity,        jit::JitCodeCache::kMaxCapacity)
 RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
@@ -103,8 +104,6 @@
 RUNTIME_OPTIONS_KEY (LogVerbosity,        Verbose)
 RUNTIME_OPTIONS_KEY (unsigned int,        LockProfThreshold)
 RUNTIME_OPTIONS_KEY (unsigned int,        StackDumpLockProfThreshold)
-RUNTIME_OPTIONS_KEY (bool,                UseTombstonedTraces, false)
-RUNTIME_OPTIONS_KEY (std::string,         StackTraceFile)
 RUNTIME_OPTIONS_KEY (Unit,                MethodTrace)
 RUNTIME_OPTIONS_KEY (std::string,         MethodTraceFile,                "/data/misc/trace/method-trace-file.bin")
 RUNTIME_OPTIONS_KEY (unsigned int,        MethodTraceFileSize,            10 * MB)
diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h
index f955932..3089c24 100644
--- a/runtime/scoped_thread_state_change-inl.h
+++ b/runtime/scoped_thread_state_change-inl.h
@@ -22,7 +22,7 @@
 #include <android-base/logging.h>
 
 #include "base/casts.h"
-#include "jni_env_ext-inl.h"
+#include "jni/jni_env_ext-inl.h"
 #include "obj_ptr-inl.h"
 #include "runtime.h"
 #include "thread-inl.h"
diff --git a/runtime/scoped_thread_state_change.cc b/runtime/scoped_thread_state_change.cc
index 6a86cc6..edbce05 100644
--- a/runtime/scoped_thread_state_change.cc
+++ b/runtime/scoped_thread_state_change.cc
@@ -19,7 +19,7 @@
 #include <type_traits>
 
 #include "base/casts.h"
-#include "java_vm_ext.h"
+#include "jni/java_vm_ext.h"
 #include "obj_ptr-inl.h"
 #include "runtime-inl.h"
 
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index d590ad5..f4a27b8 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -73,19 +73,10 @@
 #endif
 }
 
-SignalCatcher::SignalCatcher(const std::string& stack_trace_file,
-                             bool use_tombstoned_stack_trace_fd)
-    : stack_trace_file_(stack_trace_file),
-      use_tombstoned_stack_trace_fd_(use_tombstoned_stack_trace_fd),
-      lock_("SignalCatcher lock"),
+SignalCatcher::SignalCatcher()
+    : lock_("SignalCatcher lock"),
       cond_("SignalCatcher::cond_", lock_),
       thread_(nullptr) {
-#if !defined(ART_TARGET_ANDROID)
-  // We're not running on Android, so we can't communicate with tombstoned
-  // to ask for an open file.
-  CHECK(!use_tombstoned_stack_trace_fd_);
-#endif
-
   SetHaltFlag(false);
 
   // Create a raw pthread; its start routine will attach to the runtime.
@@ -116,37 +107,11 @@
   return halt_;
 }
 
-bool SignalCatcher::OpenStackTraceFile(android::base::unique_fd* tombstone_fd,
-                                       android::base::unique_fd* output_fd) {
-  if (use_tombstoned_stack_trace_fd_) {
-#if defined(ART_TARGET_ANDROID)
-    return tombstoned_connect(getpid(), tombstone_fd, output_fd, kDebuggerdJavaBacktrace);
-#else
-    UNUSED(tombstone_fd);
-    UNUSED(output_fd);
-#endif
-  }
-
-  // The runtime is not configured to dump traces to a file, will LOG(INFO)
-  // instead.
-  if (stack_trace_file_.empty()) {
-    return false;
-  }
-
-  int fd = open(stack_trace_file_.c_str(), O_APPEND | O_CREAT | O_WRONLY, 0666);
-  if (fd == -1) {
-      PLOG(ERROR) << "Unable to open stack trace file '" << stack_trace_file_ << "'";
-      return false;
-  }
-
-  output_fd->reset(fd);
-  return true;
-}
-
 void SignalCatcher::Output(const std::string& s) {
+#if defined(ART_TARGET_ANDROID)
   android::base::unique_fd tombstone_fd;
   android::base::unique_fd output_fd;
-  if (!OpenStackTraceFile(&tombstone_fd, &output_fd)) {
+  if (!tombstoned_connect(getpid(), &tombstone_fd, &output_fd, kDebuggerdJavaBacktrace)) {
     LOG(INFO) << s;
     return;
   }
@@ -161,19 +126,16 @@
     file->Erase();
   }
 
-  const std::string output_path_msg = (use_tombstoned_stack_trace_fd_) ?
-      "[tombstoned]" : stack_trace_file_;
-
   if (success) {
-    LOG(INFO) << "Wrote stack traces to '" << output_path_msg << "'";
+    LOG(INFO) << "Wrote stack traces to tombstoned";
   } else {
-    PLOG(ERROR) << "Failed to write stack traces to '" << output_path_msg << "'";
+    PLOG(ERROR) << "Failed to write stack traces to tombstoned";
   }
-
-#if defined(ART_TARGET_ANDROID)
-  if (use_tombstoned_stack_trace_fd_ && !tombstoned_notify_completion(tombstone_fd)) {
+  if (!tombstoned_notify_completion(tombstone_fd)) {
     PLOG(WARNING) << "Unable to notify tombstoned of dump completion";
   }
+#else
+  LOG(INFO) << s;
 #endif
 }
 
diff --git a/runtime/signal_catcher.h b/runtime/signal_catcher.h
index 8a2a728..46eae7e 100644
--- a/runtime/signal_catcher.h
+++ b/runtime/signal_catcher.h
@@ -33,17 +33,7 @@
  */
 class SignalCatcher {
  public:
-  // If |use_tombstoned_stack_trace_fd| is |true|, traces will be
-  // written to a file descriptor provided by tombstoned. The process
-  // will communicate with tombstoned via a unix domain socket. This
-  // mode of stack trace dumping is only supported in an Android
-  // environment.
-  //
-  // If false, all traces will be dumped to |stack_trace_file| if it's
-  // non-empty. If |stack_trace_file| is empty, all traces will be written
-  // to the log buffer.
-  SignalCatcher(const std::string& stack_trace_file,
-                const bool use_tombstoned_stack_trace_fd);
+  SignalCatcher();
   ~SignalCatcher();
 
   void HandleSigQuit() REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_,
@@ -54,19 +44,12 @@
   // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
   static void* Run(void* arg) NO_THREAD_SAFETY_ANALYSIS;
 
-  // NOTE: We're using android::base::unique_fd here for easier
-  // interoperability with tombstoned client APIs.
-  bool OpenStackTraceFile(android::base::unique_fd* tombstone_fd,
-                          android::base::unique_fd* output_fd);
   void HandleSigUsr1();
   void Output(const std::string& s);
   void SetHaltFlag(bool new_value) REQUIRES(!lock_);
   bool ShouldHalt() REQUIRES(!lock_);
   int WaitForSignal(Thread* self, SignalSet& signals) REQUIRES(!lock_);
 
-  std::string stack_trace_file_;
-  const bool use_tombstoned_stack_trace_fd_;
-
   mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   ConditionVariable cond_ GUARDED_BY(lock_);
   bool halt_ GUARDED_BY(lock_);
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 229238e..7d1cb5c 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -25,6 +25,7 @@
 #include "base/hex_dump.h"
 #include "dex/dex_file_types.h"
 #include "entrypoints/entrypoint_utils-inl.h"
+#include "entrypoints/quick/callee_save_frame.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc/space/image_space.h"
 #include "gc/space/space-inl.h"
@@ -75,15 +76,14 @@
   }
 }
 
-static InlineInfo GetCurrentInlineInfo(const OatQuickMethodHeader* method_header,
+static InlineInfo GetCurrentInlineInfo(CodeInfo& code_info,
+                                       const OatQuickMethodHeader* method_header,
                                        uintptr_t cur_quick_frame_pc)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   uint32_t native_pc_offset = method_header->NativeQuickPcOffset(cur_quick_frame_pc);
-  CodeInfo code_info = method_header->GetOptimizedCodeInfo();
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+  StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
   DCHECK(stack_map.IsValid());
-  return code_info.GetInlineInfoOf(stack_map, encoding);
+  return code_info.GetInlineInfoOf(stack_map);
 }
 
 ArtMethod* StackVisitor::GetMethod() const {
@@ -92,16 +92,16 @@
   } else if (cur_quick_frame_ != nullptr) {
     if (IsInInlinedFrame()) {
       size_t depth_in_stack_map = current_inlining_depth_ - 1;
-      InlineInfo inline_info = GetCurrentInlineInfo(GetCurrentOatQuickMethodHeader(),
-                                                    cur_quick_frame_pc_);
       const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
-      CodeInfoEncoding encoding = method_header->GetOptimizedCodeInfo().ExtractEncoding();
+      CodeInfo code_info(method_header);
+      InlineInfo inline_info = GetCurrentInlineInfo(code_info,
+                                                    method_header,
+                                                    cur_quick_frame_pc_);
       MethodInfo method_info = method_header->GetOptimizedMethodInfo();
       DCHECK(walk_kind_ != StackWalkKind::kSkipInlinedFrames);
       return GetResolvedMethod(*GetCurrentQuickFrame(),
                                method_info,
                                inline_info,
-                               encoding.inline_info.encoding,
                                depth_in_stack_map);
     } else {
       return *cur_quick_frame_;
@@ -115,11 +115,11 @@
     return cur_shadow_frame_->GetDexPC();
   } else if (cur_quick_frame_ != nullptr) {
     if (IsInInlinedFrame()) {
-      size_t depth_in_stack_map = current_inlining_depth_ - 1;
       const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
-      CodeInfoEncoding encoding = method_header->GetOptimizedCodeInfo().ExtractEncoding();
-      return GetCurrentInlineInfo(GetCurrentOatQuickMethodHeader(), cur_quick_frame_pc_).
-          GetDexPcAtDepth(encoding.inline_info.encoding, depth_in_stack_map);
+      CodeInfo code_info(method_header);
+      size_t depth_in_stack_map = current_inlining_depth_ - 1;
+      return GetCurrentInlineInfo(code_info, method_header, cur_quick_frame_pc_).
+          GetDexPcAtDepth(depth_in_stack_map);
     } else if (cur_oat_quick_method_header_ == nullptr) {
       return dex::kDexNoIndex;
     } else {
@@ -229,32 +229,29 @@
   uint16_t number_of_dex_registers = accessor.RegistersSize();
   DCHECK_LT(vreg, number_of_dex_registers);
   const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
-  CodeInfo code_info = method_header->GetOptimizedCodeInfo();
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
+  CodeInfo code_info(method_header);
 
   uint32_t native_pc_offset = method_header->NativeQuickPcOffset(cur_quick_frame_pc_);
-  StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+  StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
   DCHECK(stack_map.IsValid());
   size_t depth_in_stack_map = current_inlining_depth_ - 1;
 
   DexRegisterMap dex_register_map = IsInInlinedFrame()
       ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map,
-                                           code_info.GetInlineInfoOf(stack_map, encoding),
-                                           encoding,
+                                           code_info.GetInlineInfoOf(stack_map),
                                            number_of_dex_registers)
-      : code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
+      : code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
 
   if (!dex_register_map.IsValid()) {
     return false;
   }
   DexRegisterLocation::Kind location_kind =
-      dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info, encoding);
+      dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info);
   switch (location_kind) {
     case DexRegisterLocation::Kind::kInStack: {
       const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg,
                                                                     number_of_dex_registers,
-                                                                    code_info,
-                                                                    encoding);
+                                                                    code_info);
       const uint8_t* addr = reinterpret_cast<const uint8_t*>(cur_quick_frame_) + offset;
       *val = *reinterpret_cast<const uint32_t*>(addr);
       return true;
@@ -264,11 +261,11 @@
     case DexRegisterLocation::Kind::kInFpuRegister:
     case DexRegisterLocation::Kind::kInFpuRegisterHigh: {
       uint32_t reg =
-          dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info, encoding);
+          dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info);
       return GetRegisterIfAccessible(reg, kind, val);
     }
     case DexRegisterLocation::Kind::kConstant:
-      *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info, encoding);
+      *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info);
       return true;
     case DexRegisterLocation::Kind::kNone:
       return false;
@@ -277,8 +274,7 @@
           << "Unexpected location kind "
           << dex_register_map.GetLocationInternalKind(vreg,
                                                       number_of_dex_registers,
-                                                      code_info,
-                                                      encoding);
+                                                      code_info);
       UNREACHABLE();
   }
 }
@@ -718,7 +714,7 @@
   Runtime* runtime = Runtime::Current();
 
   if (method->IsAbstract()) {
-    return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
+    return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
   }
 
   // This goes before IsProxyMethod since runtime methods have a null declaring class.
@@ -732,7 +728,7 @@
     // compiled method without any stubs. Therefore the method must have a OatQuickMethodHeader.
     DCHECK(!method->IsDirect() && !method->IsConstructor())
         << "Constructors of proxy classes must have a OatQuickMethodHeader";
-    return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
+    return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
   }
 
   // The only remaining case is if the method is native and uses the generic JNI stub,
@@ -751,8 +747,8 @@
   // Generic JNI frame.
   uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(method) + 1;
   size_t scope_size = HandleScope::SizeOf(handle_refs);
-  QuickMethodFrameInfo callee_info =
-      runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
+  constexpr QuickMethodFrameInfo callee_info =
+      RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
 
   // Callee saves + handle scope + method ref + alignment
   // Note: -sizeof(void*) since callee-save frame stores a whole method pointer.
@@ -830,15 +826,14 @@
         if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames)
             && (cur_oat_quick_method_header_ != nullptr)
             && cur_oat_quick_method_header_->IsOptimized()) {
-          CodeInfo code_info = cur_oat_quick_method_header_->GetOptimizedCodeInfo();
-          CodeInfoEncoding encoding = code_info.ExtractEncoding();
+          CodeInfo code_info(cur_oat_quick_method_header_);
           uint32_t native_pc_offset =
               cur_oat_quick_method_header_->NativeQuickPcOffset(cur_quick_frame_pc_);
-          StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
-          if (stack_map.IsValid() && stack_map.HasInlineInfo(encoding.stack_map.encoding)) {
-            InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+          StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+          if (stack_map.IsValid() && stack_map.HasInlineInfo()) {
+            InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
             DCHECK_EQ(current_inlining_depth_, 0u);
-            for (current_inlining_depth_ = inline_info.GetDepth(encoding.inline_info.encoding);
+            for (current_inlining_depth_ = inline_info.GetDepth();
                  current_inlining_depth_ != 0;
                  --current_inlining_depth_) {
               bool should_continue = VisitFrame();
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 9c7b687..2b7e8dd 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -25,8 +25,6 @@
 namespace art {
 
 constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex;
-constexpr uint32_t StackMap::kNoDexRegisterMap;
-constexpr uint32_t StackMap::kNoInlineInfo;
 
 std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind) {
   using Kind = DexRegisterLocation::Kind;
@@ -56,27 +54,25 @@
 DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind(
     uint16_t dex_register_number,
     uint16_t number_of_dex_registers,
-    const CodeInfo& code_info,
-    const CodeInfoEncoding& enc) const {
+    const CodeInfo& code_info) const {
   DexRegisterLocationCatalog dex_register_location_catalog =
-      code_info.GetDexRegisterLocationCatalog(enc);
+      code_info.GetDexRegisterLocationCatalog();
   size_t location_catalog_entry_index = GetLocationCatalogEntryIndex(
       dex_register_number,
       number_of_dex_registers,
-      code_info.GetNumberOfLocationCatalogEntries(enc));
+      code_info.GetNumberOfLocationCatalogEntries());
   return dex_register_location_catalog.GetLocationInternalKind(location_catalog_entry_index);
 }
 
 DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number,
                                                            uint16_t number_of_dex_registers,
-                                                           const CodeInfo& code_info,
-                                                           const CodeInfoEncoding& enc) const {
+                                                           const CodeInfo& code_info) const {
   DexRegisterLocationCatalog dex_register_location_catalog =
-      code_info.GetDexRegisterLocationCatalog(enc);
+      code_info.GetDexRegisterLocationCatalog();
   size_t location_catalog_entry_index = GetLocationCatalogEntryIndex(
       dex_register_number,
       number_of_dex_registers,
-      code_info.GetNumberOfLocationCatalogEntries(enc));
+      code_info.GetNumberOfLocationCatalogEntries());
   return dex_register_location_catalog.GetDexRegisterLocation(location_catalog_entry_index);
 }
 
@@ -90,27 +86,28 @@
      << " (" << location.GetValue() << ")" << suffix << '\n';
 }
 
-void StackMapEncoding::Dump(VariableIndentationOutputStream* vios) const {
+void StackMap::DumpEncoding(const BitTable<6>& table,
+                            VariableIndentationOutputStream* vios) {
   vios->Stream()
       << "StackMapEncoding"
-      << " (native_pc_bit_offset=" << static_cast<uint32_t>(kNativePcBitOffset)
-      << ", dex_pc_bit_offset=" << static_cast<uint32_t>(dex_pc_bit_offset_)
-      << ", dex_register_map_bit_offset=" << static_cast<uint32_t>(dex_register_map_bit_offset_)
-      << ", inline_info_bit_offset=" << static_cast<uint32_t>(inline_info_bit_offset_)
-      << ", register_mask_bit_offset=" << static_cast<uint32_t>(register_mask_index_bit_offset_)
-      << ", stack_mask_index_bit_offset=" << static_cast<uint32_t>(stack_mask_index_bit_offset_)
-      << ", total_bit_size=" << static_cast<uint32_t>(total_bit_size_)
+      << " (NativePcOffsetBits=" << table.NumColumnBits(kNativePcOffset)
+      << ", DexPcBits=" << table.NumColumnBits(kDexPc)
+      << ", DexRegisterMapOffsetBits=" << table.NumColumnBits(kDexRegisterMapOffset)
+      << ", InlineInfoIndexBits=" << table.NumColumnBits(kInlineInfoIndex)
+      << ", RegisterMaskIndexBits=" << table.NumColumnBits(kRegisterMaskIndex)
+      << ", StackMaskIndexBits=" << table.NumColumnBits(kStackMaskIndex)
       << ")\n";
 }
 
-void InlineInfoEncoding::Dump(VariableIndentationOutputStream* vios) const {
+void InlineInfo::DumpEncoding(const BitTable<5>& table,
+                              VariableIndentationOutputStream* vios) {
   vios->Stream()
       << "InlineInfoEncoding"
-      << " (method_index_bit_offset=" << static_cast<uint32_t>(kMethodIndexBitOffset)
-      << ", dex_pc_bit_offset=" << static_cast<uint32_t>(dex_pc_bit_offset_)
-      << ", extra_data_bit_offset=" << static_cast<uint32_t>(extra_data_bit_offset_)
-      << ", dex_register_map_bit_offset=" << static_cast<uint32_t>(dex_register_map_bit_offset_)
-      << ", total_bit_size=" << static_cast<uint32_t>(total_bit_size_)
+      << " (IsLastBits=" << table.NumColumnBits(kIsLast)
+      << ", MethodIndexIdxBits=" << table.NumColumnBits(kMethodIndexIdx)
+      << ", DexPcBits=" << table.NumColumnBits(kDexPc)
+      << ", ExtraDataBits=" << table.NumColumnBits(kExtraData)
+      << ", DexRegisterMapOffsetBits=" << table.NumColumnBits(kDexRegisterMapOffset)
       << ")\n";
 }
 
@@ -120,26 +117,24 @@
                     bool dump_stack_maps,
                     InstructionSet instruction_set,
                     const MethodInfo& method_info) const {
-  CodeInfoEncoding encoding = ExtractEncoding();
-  size_t number_of_stack_maps = GetNumberOfStackMaps(encoding);
+  size_t number_of_stack_maps = GetNumberOfStackMaps();
   vios->Stream()
       << "Optimized CodeInfo (number_of_dex_registers=" << number_of_dex_registers
       << ", number_of_stack_maps=" << number_of_stack_maps
       << ")\n";
   ScopedIndentation indent1(vios);
-  encoding.stack_map.encoding.Dump(vios);
-  if (HasInlineInfo(encoding)) {
-    encoding.inline_info.encoding.Dump(vios);
+  StackMap::DumpEncoding(stack_maps_, vios);
+  if (HasInlineInfo()) {
+    InlineInfo::DumpEncoding(inline_infos_, vios);
   }
   // Display the Dex register location catalog.
-  GetDexRegisterLocationCatalog(encoding).Dump(vios, *this);
+  GetDexRegisterLocationCatalog().Dump(vios, *this);
   // Display stack maps along with (live) Dex register maps.
   if (dump_stack_maps) {
     for (size_t i = 0; i < number_of_stack_maps; ++i) {
-      StackMap stack_map = GetStackMapAt(i, encoding);
+      StackMap stack_map = GetStackMapAt(i);
       stack_map.Dump(vios,
                      *this,
-                     encoding,
                      method_info,
                      code_offset,
                      number_of_dex_registers,
@@ -153,9 +148,8 @@
 
 void DexRegisterLocationCatalog::Dump(VariableIndentationOutputStream* vios,
                                       const CodeInfo& code_info) {
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
-  size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize(encoding);
+  size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries();
+  size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize();
   vios->Stream()
       << "DexRegisterLocationCatalog (number_of_entries=" << number_of_location_catalog_entries
       << ", size_in_bytes=" << location_catalog_size_in_bytes << ")\n";
@@ -169,8 +163,7 @@
 void DexRegisterMap::Dump(VariableIndentationOutputStream* vios,
                           const CodeInfo& code_info,
                           uint16_t number_of_dex_registers) const {
-  CodeInfoEncoding encoding = code_info.ExtractEncoding();
-  size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
+  size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries();
   // TODO: Display the bit mask of live Dex registers.
   for (size_t j = 0; j < number_of_dex_registers; ++j) {
     if (IsDexRegisterLive(j)) {
@@ -178,8 +171,7 @@
           j, number_of_dex_registers, number_of_location_catalog_entries);
       DexRegisterLocation location = GetDexRegisterLocation(j,
                                                             number_of_dex_registers,
-                                                            code_info,
-                                                            encoding);
+                                                            code_info);
       ScopedIndentation indent1(vios);
       DumpRegisterMapping(
           vios->Stream(), j, location, "v",
@@ -190,38 +182,35 @@
 
 void StackMap::Dump(VariableIndentationOutputStream* vios,
                     const CodeInfo& code_info,
-                    const CodeInfoEncoding& encoding,
                     const MethodInfo& method_info,
                     uint32_t code_offset,
                     uint16_t number_of_dex_registers,
                     InstructionSet instruction_set,
                     const std::string& header_suffix) const {
-  StackMapEncoding stack_map_encoding = encoding.stack_map.encoding;
-  const uint32_t pc_offset = GetNativePcOffset(stack_map_encoding, instruction_set);
+  const uint32_t pc_offset = GetNativePcOffset(instruction_set);
   vios->Stream()
       << "StackMap" << header_suffix
       << std::hex
       << " [native_pc=0x" << code_offset + pc_offset << "]"
-      << " [entry_size=0x" << encoding.stack_map.encoding.BitSize() << " bits]"
-      << " (dex_pc=0x" << GetDexPc(stack_map_encoding)
+      << " (dex_pc=0x" << GetDexPc()
       << ", native_pc_offset=0x" << pc_offset
-      << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding)
-      << ", inline_info_offset=0x" << GetInlineInfoIndex(stack_map_encoding)
-      << ", register_mask=0x" << code_info.GetRegisterMaskOf(encoding, *this)
+      << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset()
+      << ", inline_info_offset=0x" << GetInlineInfoIndex()
+      << ", register_mask=0x" << code_info.GetRegisterMaskOf(*this)
       << std::dec
       << ", stack_mask=0b";
-  BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, *this);
-  for (size_t i = 0, e = encoding.stack_mask.encoding.BitSize(); i < e; ++i) {
+  BitMemoryRegion stack_mask = code_info.GetStackMaskOf(*this);
+  for (size_t i = 0, e = code_info.GetNumberOfStackMaskBits(); i < e; ++i) {
     vios->Stream() << stack_mask.LoadBit(e - i - 1);
   }
   vios->Stream() << ")\n";
-  if (HasDexRegisterMap(stack_map_encoding)) {
+  if (HasDexRegisterMap()) {
     DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
-        *this, encoding, number_of_dex_registers);
+        *this, number_of_dex_registers);
     dex_register_map.Dump(vios, code_info, number_of_dex_registers);
   }
-  if (HasInlineInfo(stack_map_encoding)) {
-    InlineInfo inline_info = code_info.GetInlineInfoOf(*this, encoding);
+  if (HasInlineInfo()) {
+    InlineInfo inline_info = code_info.GetInlineInfoOf(*this);
     // We do not know the length of the dex register maps of inlined frames
     // at this level, so we just pass null to `InlineInfo::Dump` to tell
     // it not to look at these maps.
@@ -233,29 +222,27 @@
                       const CodeInfo& code_info,
                       const MethodInfo& method_info,
                       uint16_t number_of_dex_registers[]) const {
-  InlineInfoEncoding inline_info_encoding = code_info.ExtractEncoding().inline_info.encoding;
   vios->Stream() << "InlineInfo with depth "
-                 << static_cast<uint32_t>(GetDepth(inline_info_encoding))
+                 << static_cast<uint32_t>(GetDepth())
                  << "\n";
 
-  for (size_t i = 0; i < GetDepth(inline_info_encoding); ++i) {
+  for (size_t i = 0; i < GetDepth(); ++i) {
     vios->Stream()
         << " At depth " << i
         << std::hex
-        << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i);
-    if (EncodesArtMethodAtDepth(inline_info_encoding, i)) {
+        << " (dex_pc=0x" << GetDexPcAtDepth(i);
+    if (EncodesArtMethodAtDepth(i)) {
       ScopedObjectAccess soa(Thread::Current());
-      vios->Stream() << ", method=" << GetArtMethodAtDepth(inline_info_encoding, i)->PrettyMethod();
+      vios->Stream() << ", method=" << GetArtMethodAtDepth(i)->PrettyMethod();
     } else {
       vios->Stream()
           << std::dec
-          << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, method_info, i);
+          << ", method_index=" << GetMethodIndexAtDepth(method_info, i);
     }
     vios->Stream() << ")\n";
-    if (HasDexRegisterMapAtDepth(inline_info_encoding, i) && (number_of_dex_registers != nullptr)) {
-      CodeInfoEncoding encoding = code_info.ExtractEncoding();
+    if (HasDexRegisterMapAtDepth(i) && (number_of_dex_registers != nullptr)) {
       DexRegisterMap dex_register_map =
-          code_info.GetDexRegisterMapAtDepth(i, *this, encoding, number_of_dex_registers[i]);
+          code_info.GetDexRegisterMapAtDepth(i, *this, number_of_dex_registers[i]);
       ScopedIndentation indent1(vios);
       dex_register_map.Dump(vios, code_info, number_of_dex_registers[i]);
     }
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 3839764..91cecf0 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -21,12 +21,14 @@
 
 #include "arch/code_offset.h"
 #include "base/bit_memory_region.h"
+#include "base/bit_table.h"
 #include "base/bit_utils.h"
 #include "base/bit_vector.h"
 #include "base/leb128.h"
 #include "base/memory_region.h"
 #include "dex/dex_file_types.h"
 #include "method_info.h"
+#include "oat_quick_method_header.h"
 
 namespace art {
 
@@ -37,13 +39,8 @@
 // (signed) values.
 static constexpr ssize_t kFrameSlotSize = 4;
 
-// Size of Dex virtual registers.
-static constexpr size_t kVRegSize = 4;
-
 class ArtMethod;
 class CodeInfo;
-class StackMapEncoding;
-struct CodeInfoEncoding;
 
 /**
  * Classes in the following file are wrapper on stack map information backed
@@ -452,35 +449,31 @@
   explicit DexRegisterMap(MemoryRegion region) : region_(region) {}
   DexRegisterMap() {}
 
-  bool IsValid() const { return region_.pointer() != nullptr; }
+  bool IsValid() const { return region_.IsValid(); }
 
   // Get the surface kind of Dex register `dex_register_number`.
   DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number,
                                             uint16_t number_of_dex_registers,
-                                            const CodeInfo& code_info,
-                                            const CodeInfoEncoding& enc) const {
+                                            const CodeInfo& code_info) const {
     return DexRegisterLocation::ConvertToSurfaceKind(
-        GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info, enc));
+        GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info));
   }
 
   // Get the internal kind of Dex register `dex_register_number`.
   DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number,
                                                     uint16_t number_of_dex_registers,
-                                                    const CodeInfo& code_info,
-                                                    const CodeInfoEncoding& enc) const;
+                                                    const CodeInfo& code_info) const;
 
   // Get the Dex register location `dex_register_number`.
   DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number,
                                              uint16_t number_of_dex_registers,
-                                             const CodeInfo& code_info,
-                                             const CodeInfoEncoding& enc) const;
+                                             const CodeInfo& code_info) const;
 
   int32_t GetStackOffsetInBytes(uint16_t dex_register_number,
                                 uint16_t number_of_dex_registers,
-                                const CodeInfo& code_info,
-                                const CodeInfoEncoding& enc) const {
+                                const CodeInfo& code_info) const {
     DexRegisterLocation location =
-        GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc);
+        GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
     DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack);
     // GetDexRegisterLocation returns the offset in bytes.
     return location.GetValue();
@@ -488,20 +481,18 @@
 
   int32_t GetConstant(uint16_t dex_register_number,
                       uint16_t number_of_dex_registers,
-                      const CodeInfo& code_info,
-                      const CodeInfoEncoding& enc) const {
+                      const CodeInfo& code_info) const {
     DexRegisterLocation location =
-        GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc);
+        GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
     DCHECK_EQ(location.GetKind(), DexRegisterLocation::Kind::kConstant);
     return location.GetValue();
   }
 
   int32_t GetMachineRegister(uint16_t dex_register_number,
                              uint16_t number_of_dex_registers,
-                             const CodeInfo& code_info,
-                             const CodeInfoEncoding& enc) const {
+                             const CodeInfo& code_info) const {
     DexRegisterLocation location =
-        GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc);
+        GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
     DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister ||
            location.GetInternalKind() == DexRegisterLocation::Kind::kInRegisterHigh ||
            location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister ||
@@ -627,7 +618,7 @@
 
   // Return the size of the DexRegisterMap object, in bytes.
   size_t Size() const {
-    return region_.size();
+    return BitsToBytesRoundUp(region_.size_in_bits());
   }
 
   void Dump(VariableIndentationOutputStream* vios,
@@ -650,143 +641,12 @@
 
   static constexpr int kFixedSize = 0;
 
-  MemoryRegion region_;
+  BitMemoryRegion region_;
 
   friend class CodeInfo;
   friend class StackMapStream;
 };
 
-// Represents bit range of bit-packed integer field.
-// We reuse the idea from ULEB128p1 to support encoding of -1 (aka 0xFFFFFFFF).
-// If min_value is set to -1, we implicitly subtract one from any loaded value,
-// and add one to any stored value. This is generalized to any negative values.
-// In other words, min_value acts as a base and the stored value is added to it.
-struct FieldEncoding {
-  FieldEncoding(size_t start_offset, size_t end_offset, int32_t min_value = 0)
-      : start_offset_(start_offset), end_offset_(end_offset), min_value_(min_value) {
-    DCHECK_LE(start_offset_, end_offset_);
-    DCHECK_LE(BitSize(), 32u);
-  }
-
-  ALWAYS_INLINE size_t BitSize() const { return end_offset_ - start_offset_; }
-
-  template <typename Region>
-  ALWAYS_INLINE int32_t Load(const Region& region) const {
-    DCHECK_LE(end_offset_, region.size_in_bits());
-    return static_cast<int32_t>(region.LoadBits(start_offset_, BitSize())) + min_value_;
-  }
-
-  template <typename Region>
-  ALWAYS_INLINE void Store(Region region, int32_t value) const {
-    region.StoreBits(start_offset_, value - min_value_, BitSize());
-    DCHECK_EQ(Load(region), value);
-  }
-
- private:
-  size_t start_offset_;
-  size_t end_offset_;
-  int32_t min_value_;
-};
-
-class StackMapEncoding {
- public:
-  StackMapEncoding()
-      : dex_pc_bit_offset_(0),
-        dex_register_map_bit_offset_(0),
-        inline_info_bit_offset_(0),
-        register_mask_index_bit_offset_(0),
-        stack_mask_index_bit_offset_(0),
-        total_bit_size_(0) {}
-
-  // Set stack map bit layout based on given sizes.
-  // Returns the size of stack map in bits.
-  size_t SetFromSizes(size_t native_pc_max,
-                      size_t dex_pc_max,
-                      size_t dex_register_map_size,
-                      size_t number_of_inline_info,
-                      size_t number_of_register_masks,
-                      size_t number_of_stack_masks) {
-    total_bit_size_ = 0;
-    DCHECK_EQ(kNativePcBitOffset, total_bit_size_);
-    total_bit_size_ += MinimumBitsToStore(native_pc_max);
-
-    dex_pc_bit_offset_ = total_bit_size_;
-    // Note: We're not encoding the dex pc if there is none. That's the case
-    // for an intrinsified native method, such as String.charAt().
-    if (dex_pc_max != dex::kDexNoIndex) {
-      total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max);
-    }
-
-    // We also need +1 for kNoDexRegisterMap, but since the size is strictly
-    // greater than any offset we might try to encode, we already implicitly have it.
-    dex_register_map_bit_offset_ = total_bit_size_;
-    total_bit_size_ += MinimumBitsToStore(dex_register_map_size);
-
-    // We also need +1 for kNoInlineInfo, but since the inline_info_size is strictly
-    // greater than the offset we might try to encode, we already implicitly have it.
-    // If inline_info_size is zero, we can encode only kNoInlineInfo (in zero bits).
-    inline_info_bit_offset_ = total_bit_size_;
-    total_bit_size_ += MinimumBitsToStore(number_of_inline_info);
-
-    register_mask_index_bit_offset_ = total_bit_size_;
-    total_bit_size_ += MinimumBitsToStore(number_of_register_masks);
-
-    stack_mask_index_bit_offset_ = total_bit_size_;
-    total_bit_size_ += MinimumBitsToStore(number_of_stack_masks);
-
-    return total_bit_size_;
-  }
-
-  ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const {
-    return FieldEncoding(kNativePcBitOffset, dex_pc_bit_offset_);
-  }
-  ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const {
-    return FieldEncoding(dex_pc_bit_offset_, dex_register_map_bit_offset_, -1 /* min_value */);
-  }
-  ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const {
-    return FieldEncoding(dex_register_map_bit_offset_, inline_info_bit_offset_, -1 /* min_value */);
-  }
-  ALWAYS_INLINE FieldEncoding GetInlineInfoEncoding() const {
-    return FieldEncoding(inline_info_bit_offset_,
-                         register_mask_index_bit_offset_,
-                         -1 /* min_value */);
-  }
-  ALWAYS_INLINE FieldEncoding GetRegisterMaskIndexEncoding() const {
-    return FieldEncoding(register_mask_index_bit_offset_, stack_mask_index_bit_offset_);
-  }
-  ALWAYS_INLINE FieldEncoding GetStackMaskIndexEncoding() const {
-    return FieldEncoding(stack_mask_index_bit_offset_, total_bit_size_);
-  }
-  ALWAYS_INLINE size_t BitSize() const {
-    return total_bit_size_;
-  }
-
-  // Encode the encoding into the vector.
-  template<typename Vector>
-  void Encode(Vector* dest) const {
-    static_assert(alignof(StackMapEncoding) == 1, "Should not require alignment");
-    const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this);
-    dest->insert(dest->end(), ptr, ptr + sizeof(*this));
-  }
-
-  // Decode the encoding from a pointer, updates the pointer.
-  void Decode(const uint8_t** ptr) {
-    *this = *reinterpret_cast<const StackMapEncoding*>(*ptr);
-    *ptr += sizeof(*this);
-  }
-
-  void Dump(VariableIndentationOutputStream* vios) const;
-
- private:
-  static constexpr size_t kNativePcBitOffset = 0;
-  uint8_t dex_pc_bit_offset_;
-  uint8_t dex_register_map_bit_offset_;
-  uint8_t inline_info_bit_offset_;
-  uint8_t register_mask_index_bit_offset_;
-  uint8_t stack_mask_index_bit_offset_;
-  uint8_t total_bit_size_;
-};
-
 /**
  * A Stack Map holds compilation information for a specific PC necessary for:
  * - Mapping it to a dex PC,
@@ -794,248 +654,101 @@
  * - Knowing which registers hold objects,
  * - Knowing the inlining information,
  * - Knowing the values of dex registers.
- *
- * The information is of the form:
- *
- *   [native_pc_offset, dex_pc, dex_register_map_offset, inlining_info_index, register_mask_index,
- *   stack_mask_index].
  */
-class StackMap {
+class StackMap : public BitTable<6>::Accessor {
  public:
-  StackMap() {}
-  explicit StackMap(BitMemoryRegion region) : region_(region) {}
+  enum Field {
+    kNativePcOffset,
+    kDexPc,
+    kDexRegisterMapOffset,
+    kInlineInfoIndex,
+    kRegisterMaskIndex,
+    kStackMaskIndex,
+    kCount,
+  };
 
-  ALWAYS_INLINE bool IsValid() const { return region_.pointer() != nullptr; }
+  StackMap() : BitTable<kCount>::Accessor(nullptr, -1) {}
+  StackMap(const BitTable<kCount>* table, uint32_t row)
+    : BitTable<kCount>::Accessor(table, row) {}
 
-  ALWAYS_INLINE uint32_t GetDexPc(const StackMapEncoding& encoding) const {
-    return encoding.GetDexPcEncoding().Load(region_);
-  }
-
-  ALWAYS_INLINE void SetDexPc(const StackMapEncoding& encoding, uint32_t dex_pc) {
-    encoding.GetDexPcEncoding().Store(region_, dex_pc);
-  }
-
-  ALWAYS_INLINE uint32_t GetNativePcOffset(const StackMapEncoding& encoding,
-                                           InstructionSet instruction_set) const {
-    CodeOffset offset(
-        CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_)));
+  ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const {
+    CodeOffset offset(CodeOffset::FromCompressedOffset(Get<kNativePcOffset>()));
     return offset.Uint32Value(instruction_set);
   }
 
-  ALWAYS_INLINE void SetNativePcCodeOffset(const StackMapEncoding& encoding,
-                                           CodeOffset native_pc_offset) {
-    encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue());
-  }
+  uint32_t GetDexPc() const { return Get<kDexPc>(); }
 
-  ALWAYS_INLINE uint32_t GetDexRegisterMapOffset(const StackMapEncoding& encoding) const {
-    return encoding.GetDexRegisterMapEncoding().Load(region_);
-  }
+  uint32_t GetDexRegisterMapOffset() const { return Get<kDexRegisterMapOffset>(); }
+  bool HasDexRegisterMap() const { return GetDexRegisterMapOffset() != kNoValue; }
 
-  ALWAYS_INLINE void SetDexRegisterMapOffset(const StackMapEncoding& encoding, uint32_t offset) {
-    encoding.GetDexRegisterMapEncoding().Store(region_, offset);
-  }
+  uint32_t GetInlineInfoIndex() const { return Get<kInlineInfoIndex>(); }
+  bool HasInlineInfo() const { return GetInlineInfoIndex() != kNoValue; }
 
-  ALWAYS_INLINE uint32_t GetInlineInfoIndex(const StackMapEncoding& encoding) const {
-    return encoding.GetInlineInfoEncoding().Load(region_);
-  }
+  uint32_t GetRegisterMaskIndex() const { return Get<kRegisterMaskIndex>(); }
 
-  ALWAYS_INLINE void SetInlineInfoIndex(const StackMapEncoding& encoding, uint32_t index) {
-    encoding.GetInlineInfoEncoding().Store(region_, index);
-  }
+  uint32_t GetStackMaskIndex() const { return Get<kStackMaskIndex>(); }
 
-  ALWAYS_INLINE uint32_t GetRegisterMaskIndex(const StackMapEncoding& encoding) const {
-    return encoding.GetRegisterMaskIndexEncoding().Load(region_);
-  }
-
-  ALWAYS_INLINE void SetRegisterMaskIndex(const StackMapEncoding& encoding, uint32_t mask) {
-    encoding.GetRegisterMaskIndexEncoding().Store(region_, mask);
-  }
-
-  ALWAYS_INLINE uint32_t GetStackMaskIndex(const StackMapEncoding& encoding) const {
-    return encoding.GetStackMaskIndexEncoding().Load(region_);
-  }
-
-  ALWAYS_INLINE void SetStackMaskIndex(const StackMapEncoding& encoding, uint32_t mask) {
-    encoding.GetStackMaskIndexEncoding().Store(region_, mask);
-  }
-
-  ALWAYS_INLINE bool HasDexRegisterMap(const StackMapEncoding& encoding) const {
-    return GetDexRegisterMapOffset(encoding) != kNoDexRegisterMap;
-  }
-
-  ALWAYS_INLINE bool HasInlineInfo(const StackMapEncoding& encoding) const {
-    return GetInlineInfoIndex(encoding) != kNoInlineInfo;
-  }
-
-  ALWAYS_INLINE bool Equals(const StackMap& other) const {
-    return region_.pointer() == other.region_.pointer() &&
-           region_.size() == other.region_.size() &&
-           region_.BitOffset() == other.region_.BitOffset();
-  }
-
+  static void DumpEncoding(const BitTable<6>& table, VariableIndentationOutputStream* vios);
   void Dump(VariableIndentationOutputStream* vios,
             const CodeInfo& code_info,
-            const CodeInfoEncoding& encoding,
             const MethodInfo& method_info,
             uint32_t code_offset,
             uint16_t number_of_dex_registers,
             InstructionSet instruction_set,
             const std::string& header_suffix = "") const;
-
-  // Special (invalid) offset for the DexRegisterMapOffset field meaning
-  // that there is no Dex register map for this stack map.
-  static constexpr uint32_t kNoDexRegisterMap = -1;
-
-  // Special (invalid) offset for the InlineDescriptorOffset field meaning
-  // that there is no inline info for this stack map.
-  static constexpr uint32_t kNoInlineInfo = -1;
-
- private:
-  static constexpr int kFixedSize = 0;
-
-  BitMemoryRegion region_;
-
-  friend class StackMapStream;
-};
-
-class InlineInfoEncoding {
- public:
-  void SetFromSizes(size_t method_index_idx_max,
-                    size_t dex_pc_max,
-                    size_t extra_data_max,
-                    size_t dex_register_map_size) {
-    total_bit_size_ = kMethodIndexBitOffset;
-    total_bit_size_ += MinimumBitsToStore(method_index_idx_max);
-
-    dex_pc_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
-    // Note: We're not encoding the dex pc if there is none. That's the case
-    // for an intrinsified native method, such as String.charAt().
-    if (dex_pc_max != dex::kDexNoIndex) {
-      total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max);
-    }
-
-    extra_data_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
-    total_bit_size_ += MinimumBitsToStore(extra_data_max);
-
-    // We also need +1 for kNoDexRegisterMap, but since the size is strictly
-    // greater than any offset we might try to encode, we already implicitly have it.
-    dex_register_map_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
-    total_bit_size_ += MinimumBitsToStore(dex_register_map_size);
-  }
-
-  ALWAYS_INLINE FieldEncoding GetMethodIndexIdxEncoding() const {
-    return FieldEncoding(kMethodIndexBitOffset, dex_pc_bit_offset_);
-  }
-  ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const {
-    return FieldEncoding(dex_pc_bit_offset_, extra_data_bit_offset_, -1 /* min_value */);
-  }
-  ALWAYS_INLINE FieldEncoding GetExtraDataEncoding() const {
-    return FieldEncoding(extra_data_bit_offset_, dex_register_map_bit_offset_);
-  }
-  ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const {
-    return FieldEncoding(dex_register_map_bit_offset_, total_bit_size_, -1 /* min_value */);
-  }
-  ALWAYS_INLINE size_t BitSize() const {
-    return total_bit_size_;
-  }
-
-  void Dump(VariableIndentationOutputStream* vios) const;
-
-  // Encode the encoding into the vector.
-  template<typename Vector>
-  void Encode(Vector* dest) const {
-    static_assert(alignof(InlineInfoEncoding) == 1, "Should not require alignment");
-    const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this);
-    dest->insert(dest->end(), ptr, ptr + sizeof(*this));
-  }
-
-  // Decode the encoding from a pointer, updates the pointer.
-  void Decode(const uint8_t** ptr) {
-    *this = *reinterpret_cast<const InlineInfoEncoding*>(*ptr);
-    *ptr += sizeof(*this);
-  }
-
- private:
-  static constexpr uint8_t kIsLastBitOffset = 0;
-  static constexpr uint8_t kMethodIndexBitOffset = 1;
-  uint8_t dex_pc_bit_offset_;
-  uint8_t extra_data_bit_offset_;
-  uint8_t dex_register_map_bit_offset_;
-  uint8_t total_bit_size_;
 };
 
 /**
- * Inline information for a specific PC. The information is of the form:
- *
- *   [is_last,
- *    method_index (or ArtMethod high bits),
- *    dex_pc,
- *    extra_data (ArtMethod low bits or 1),
- *    dex_register_map_offset]+.
+ * Inline information for a specific PC.
+ * The row referenced from the StackMap holds information at depth 0.
+ * Following rows hold information for further depths.
  */
-class InlineInfo {
+class InlineInfo : public BitTable<5>::Accessor {
  public:
-  explicit InlineInfo(BitMemoryRegion region) : region_(region) {}
+  enum Field {
+    kIsLast,  // Determines if there are further rows for further depths.
+    kMethodIndexIdx,  // Method index or ArtMethod high bits.
+    kDexPc,
+    kExtraData,  // ArtMethod low bits or 1.
+    kDexRegisterMapOffset,
+    kCount,
+  };
+  static constexpr uint32_t kLast = -1;
+  static constexpr uint32_t kMore = 0;
 
-  ALWAYS_INLINE uint32_t GetDepth(const InlineInfoEncoding& encoding) const {
+  InlineInfo(const BitTable<kCount>* table, uint32_t row)
+    : BitTable<kCount>::Accessor(table, row) {}
+
+  ALWAYS_INLINE InlineInfo AtDepth(uint32_t depth) const {
+    return InlineInfo(table_, this->row_ + depth);
+  }
+
+  uint32_t GetDepth() const {
     size_t depth = 0;
-    while (!GetRegionAtDepth(encoding, depth++).LoadBit(0)) { }  // Check is_last bit.
+    while (AtDepth(depth++).Get<kIsLast>() == kMore) { }
     return depth;
   }
 
-  ALWAYS_INLINE void SetDepth(const InlineInfoEncoding& encoding, uint32_t depth) {
-    DCHECK_GT(depth, 0u);
-    for (size_t d = 0; d < depth; ++d) {
-      GetRegionAtDepth(encoding, d).StoreBit(0, d == depth - 1);  // Set is_last bit.
-    }
+  uint32_t GetMethodIndexIdxAtDepth(uint32_t depth) const {
+    DCHECK(!EncodesArtMethodAtDepth(depth));
+    return AtDepth(depth).Get<kMethodIndexIdx>();
   }
 
-  ALWAYS_INLINE uint32_t GetMethodIndexIdxAtDepth(const InlineInfoEncoding& encoding,
-                                                  uint32_t depth) const {
-    DCHECK(!EncodesArtMethodAtDepth(encoding, depth));
-    return encoding.GetMethodIndexIdxEncoding().Load(GetRegionAtDepth(encoding, depth));
+  uint32_t GetMethodIndexAtDepth(const MethodInfo& method_info, uint32_t depth) const {
+    return method_info.GetMethodIndex(GetMethodIndexIdxAtDepth(depth));
   }
 
-  ALWAYS_INLINE void SetMethodIndexIdxAtDepth(const InlineInfoEncoding& encoding,
-                                              uint32_t depth,
-                                              uint32_t index) {
-    encoding.GetMethodIndexIdxEncoding().Store(GetRegionAtDepth(encoding, depth), index);
+  uint32_t GetDexPcAtDepth(uint32_t depth) const {
+    return AtDepth(depth).Get<kDexPc>();
   }
 
-
-  ALWAYS_INLINE uint32_t GetMethodIndexAtDepth(const InlineInfoEncoding& encoding,
-                                               const MethodInfo& method_info,
-                                               uint32_t depth) const {
-    return method_info.GetMethodIndex(GetMethodIndexIdxAtDepth(encoding, depth));
+  bool EncodesArtMethodAtDepth(uint32_t depth) const {
+    return (AtDepth(depth).Get<kExtraData>() & 1) == 0;
   }
 
-  ALWAYS_INLINE uint32_t GetDexPcAtDepth(const InlineInfoEncoding& encoding,
-                                         uint32_t depth) const {
-    return encoding.GetDexPcEncoding().Load(GetRegionAtDepth(encoding, depth));
-  }
-
-  ALWAYS_INLINE void SetDexPcAtDepth(const InlineInfoEncoding& encoding,
-                                     uint32_t depth,
-                                     uint32_t dex_pc) {
-    encoding.GetDexPcEncoding().Store(GetRegionAtDepth(encoding, depth), dex_pc);
-  }
-
-  ALWAYS_INLINE bool EncodesArtMethodAtDepth(const InlineInfoEncoding& encoding,
-                                             uint32_t depth) const {
-    return (encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)) & 1) == 0;
-  }
-
-  ALWAYS_INLINE void SetExtraDataAtDepth(const InlineInfoEncoding& encoding,
-                                         uint32_t depth,
-                                         uint32_t extra_data) {
-    encoding.GetExtraDataEncoding().Store(GetRegionAtDepth(encoding, depth), extra_data);
-  }
-
-  ALWAYS_INLINE ArtMethod* GetArtMethodAtDepth(const InlineInfoEncoding& encoding,
-                                               uint32_t depth) const {
-    uint32_t low_bits = encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth));
-    uint32_t high_bits = encoding.GetMethodIndexIdxEncoding().Load(
-        GetRegionAtDepth(encoding, depth));
+  ArtMethod* GetArtMethodAtDepth(uint32_t depth) const {
+    uint32_t low_bits = AtDepth(depth).Get<kExtraData>();
+    uint32_t high_bits = AtDepth(depth).Get<kMethodIndexIdx>();
     if (high_bits == 0) {
       return reinterpret_cast<ArtMethod*>(low_bits);
     } else {
@@ -1045,411 +758,132 @@
     }
   }
 
-  ALWAYS_INLINE uint32_t GetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding,
-                                                        uint32_t depth) const {
-    return encoding.GetDexRegisterMapEncoding().Load(GetRegionAtDepth(encoding, depth));
+  uint32_t GetDexRegisterMapOffsetAtDepth(uint32_t depth) const {
+    return AtDepth(depth).Get<kDexRegisterMapOffset>();
   }
 
-  ALWAYS_INLINE void SetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding,
-                                                    uint32_t depth,
-                                                    uint32_t offset) {
-    encoding.GetDexRegisterMapEncoding().Store(GetRegionAtDepth(encoding, depth), offset);
+  bool HasDexRegisterMapAtDepth(uint32_t depth) const {
+    return GetDexRegisterMapOffsetAtDepth(depth) != StackMap::kNoValue;
   }
 
-  ALWAYS_INLINE bool HasDexRegisterMapAtDepth(const InlineInfoEncoding& encoding,
-                                              uint32_t depth) const {
-    return GetDexRegisterMapOffsetAtDepth(encoding, depth) != StackMap::kNoDexRegisterMap;
-  }
-
+  static void DumpEncoding(const BitTable<5>& table, VariableIndentationOutputStream* vios);
   void Dump(VariableIndentationOutputStream* vios,
             const CodeInfo& info,
             const MethodInfo& method_info,
             uint16_t* number_of_dex_registers) const;
-
- private:
-  ALWAYS_INLINE BitMemoryRegion GetRegionAtDepth(const InlineInfoEncoding& encoding,
-                                                 uint32_t depth) const {
-    size_t entry_size = encoding.BitSize();
-    DCHECK_GT(entry_size, 0u);
-    return region_.Subregion(depth * entry_size, entry_size);
-  }
-
-  BitMemoryRegion region_;
 };
 
-// Bit sized region encoding, may be more than 255 bits.
-class BitRegionEncoding {
+class InvokeInfo : public BitTable<3>::Accessor {
  public:
-  uint32_t num_bits = 0;
+  enum Field {
+    kNativePcOffset,
+    kInvokeType,
+    kMethodIndexIdx,
+    kCount,
+  };
 
-  ALWAYS_INLINE size_t BitSize() const {
-    return num_bits;
-  }
+  InvokeInfo(const BitTable<kCount>* table, uint32_t row)
+    : BitTable<kCount>::Accessor(table, row) {}
 
-  template<typename Vector>
-  void Encode(Vector* dest) const {
-    EncodeUnsignedLeb128(dest, num_bits);  // Use leb in case num_bits is greater than 255.
-  }
-
-  void Decode(const uint8_t** ptr) {
-    num_bits = DecodeUnsignedLeb128(ptr);
-  }
-};
-
-// A table of bit sized encodings.
-template <typename Encoding>
-struct BitEncodingTable {
-  static constexpr size_t kInvalidOffset = static_cast<size_t>(-1);
-  // How the encoding is laid out (serialized).
-  Encoding encoding;
-
-  // Number of entries in the table (serialized).
-  size_t num_entries;
-
-  // Bit offset for the base of the table (computed).
-  size_t bit_offset = kInvalidOffset;
-
-  template<typename Vector>
-  void Encode(Vector* dest) const {
-    EncodeUnsignedLeb128(dest, num_entries);
-    encoding.Encode(dest);
-  }
-
-  ALWAYS_INLINE void Decode(const uint8_t** ptr) {
-    num_entries = DecodeUnsignedLeb128(ptr);
-    encoding.Decode(ptr);
-  }
-
-  // Set the bit offset in the table and adds the space used by the table to offset.
-  void UpdateBitOffset(size_t* offset) {
-    DCHECK(offset != nullptr);
-    bit_offset = *offset;
-    *offset += encoding.BitSize() * num_entries;
-  }
-
-  // Return the bit region for the map at index i.
-  ALWAYS_INLINE BitMemoryRegion BitRegion(MemoryRegion region, size_t index) const {
-    DCHECK_NE(bit_offset, kInvalidOffset) << "Invalid table offset";
-    DCHECK_LT(index, num_entries);
-    const size_t map_size = encoding.BitSize();
-    return BitMemoryRegion(region, bit_offset + index * map_size, map_size);
-  }
-};
-
-// A byte sized table of possible variable sized encodings.
-struct ByteSizedTable {
-  static constexpr size_t kInvalidOffset = static_cast<size_t>(-1);
-
-  // Number of entries in the table (serialized).
-  size_t num_entries = 0;
-
-  // Number of bytes of the table (serialized).
-  size_t num_bytes;
-
-  // Bit offset for the base of the table (computed).
-  size_t byte_offset = kInvalidOffset;
-
-  template<typename Vector>
-  void Encode(Vector* dest) const {
-    EncodeUnsignedLeb128(dest, num_entries);
-    EncodeUnsignedLeb128(dest, num_bytes);
-  }
-
-  ALWAYS_INLINE void Decode(const uint8_t** ptr) {
-    num_entries = DecodeUnsignedLeb128(ptr);
-    num_bytes = DecodeUnsignedLeb128(ptr);
-  }
-
-  // Set the bit offset of the table. Adds the total bit size of the table to offset.
-  void UpdateBitOffset(size_t* offset) {
-    DCHECK(offset != nullptr);
-    DCHECK_ALIGNED(*offset, kBitsPerByte);
-    byte_offset = *offset / kBitsPerByte;
-    *offset += num_bytes * kBitsPerByte;
-  }
-};
-
-// Format is [native pc, invoke type, method index].
-class InvokeInfoEncoding {
- public:
-  void SetFromSizes(size_t native_pc_max,
-                    size_t invoke_type_max,
-                    size_t method_index_max) {
-    total_bit_size_ = 0;
-    DCHECK_EQ(kNativePcBitOffset, total_bit_size_);
-    total_bit_size_ += MinimumBitsToStore(native_pc_max);
-    invoke_type_bit_offset_ = total_bit_size_;
-    total_bit_size_ += MinimumBitsToStore(invoke_type_max);
-    method_index_bit_offset_ = total_bit_size_;
-    total_bit_size_ += MinimumBitsToStore(method_index_max);
-  }
-
-  ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const {
-    return FieldEncoding(kNativePcBitOffset, invoke_type_bit_offset_);
-  }
-
-  ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const {
-    return FieldEncoding(invoke_type_bit_offset_, method_index_bit_offset_);
-  }
-
-  ALWAYS_INLINE FieldEncoding GetMethodIndexEncoding() const {
-    return FieldEncoding(method_index_bit_offset_, total_bit_size_);
-  }
-
-  ALWAYS_INLINE size_t BitSize() const {
-    return total_bit_size_;
-  }
-
-  template<typename Vector>
-  void Encode(Vector* dest) const {
-    static_assert(alignof(InvokeInfoEncoding) == 1, "Should not require alignment");
-    const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this);
-    dest->insert(dest->end(), ptr, ptr + sizeof(*this));
-  }
-
-  void Decode(const uint8_t** ptr) {
-    *this = *reinterpret_cast<const InvokeInfoEncoding*>(*ptr);
-    *ptr += sizeof(*this);
-  }
-
- private:
-  static constexpr uint8_t kNativePcBitOffset = 0;
-  uint8_t invoke_type_bit_offset_;
-  uint8_t method_index_bit_offset_;
-  uint8_t total_bit_size_;
-};
-
-class InvokeInfo {
- public:
-  explicit InvokeInfo(BitMemoryRegion region) : region_(region) {}
-
-  ALWAYS_INLINE uint32_t GetNativePcOffset(const InvokeInfoEncoding& encoding,
-                                           InstructionSet instruction_set) const {
-    CodeOffset offset(
-        CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_)));
+  ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const {
+    CodeOffset offset(CodeOffset::FromCompressedOffset(Get<kNativePcOffset>()));
     return offset.Uint32Value(instruction_set);
   }
 
-  ALWAYS_INLINE void SetNativePcCodeOffset(const InvokeInfoEncoding& encoding,
-                                           CodeOffset native_pc_offset) {
-    encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue());
+  uint32_t GetInvokeType() const { return Get<kInvokeType>(); }
+
+  uint32_t GetMethodIndexIdx() const { return Get<kMethodIndexIdx>(); }
+
+  uint32_t GetMethodIndex(MethodInfo method_info) const {
+    return method_info.GetMethodIndex(GetMethodIndexIdx());
   }
-
-  ALWAYS_INLINE uint32_t GetInvokeType(const InvokeInfoEncoding& encoding) const {
-    return encoding.GetInvokeTypeEncoding().Load(region_);
-  }
-
-  ALWAYS_INLINE void SetInvokeType(const InvokeInfoEncoding& encoding, uint32_t invoke_type) {
-    encoding.GetInvokeTypeEncoding().Store(region_, invoke_type);
-  }
-
-  ALWAYS_INLINE uint32_t GetMethodIndexIdx(const InvokeInfoEncoding& encoding) const {
-    return encoding.GetMethodIndexEncoding().Load(region_);
-  }
-
-  ALWAYS_INLINE void SetMethodIndexIdx(const InvokeInfoEncoding& encoding,
-                                       uint32_t method_index_idx) {
-    encoding.GetMethodIndexEncoding().Store(region_, method_index_idx);
-  }
-
-  ALWAYS_INLINE uint32_t GetMethodIndex(const InvokeInfoEncoding& encoding,
-                                        MethodInfo method_info) const {
-    return method_info.GetMethodIndex(GetMethodIndexIdx(encoding));
-  }
-
-  bool IsValid() const { return region_.pointer() != nullptr; }
-
- private:
-  BitMemoryRegion region_;
-};
-
-// Most of the fields are encoded as ULEB128 to save space.
-struct CodeInfoEncoding {
-  using SizeType = uint32_t;
-
-  static constexpr SizeType kInvalidSize = std::numeric_limits<SizeType>::max();
-
-  // Byte sized tables go first to avoid unnecessary alignment bits.
-  ByteSizedTable dex_register_map;
-  ByteSizedTable location_catalog;
-  BitEncodingTable<StackMapEncoding> stack_map;
-  BitEncodingTable<BitRegionEncoding> register_mask;
-  BitEncodingTable<BitRegionEncoding> stack_mask;
-  BitEncodingTable<InvokeInfoEncoding> invoke_info;
-  BitEncodingTable<InlineInfoEncoding> inline_info;
-
-  CodeInfoEncoding() {}
-
-  explicit CodeInfoEncoding(const void* data) {
-    const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
-    dex_register_map.Decode(&ptr);
-    location_catalog.Decode(&ptr);
-    stack_map.Decode(&ptr);
-    register_mask.Decode(&ptr);
-    stack_mask.Decode(&ptr);
-    invoke_info.Decode(&ptr);
-    if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
-      inline_info.Decode(&ptr);
-    } else {
-      inline_info = BitEncodingTable<InlineInfoEncoding>();
-    }
-    cache_header_size =
-        dchecked_integral_cast<SizeType>(ptr - reinterpret_cast<const uint8_t*>(data));
-    ComputeTableOffsets();
-  }
-
-  // Compress is not const since it calculates cache_header_size. This is used by PrepareForFillIn.
-  template<typename Vector>
-  void Compress(Vector* dest) {
-    dex_register_map.Encode(dest);
-    location_catalog.Encode(dest);
-    stack_map.Encode(dest);
-    register_mask.Encode(dest);
-    stack_mask.Encode(dest);
-    invoke_info.Encode(dest);
-    if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
-      inline_info.Encode(dest);
-    }
-    cache_header_size = dest->size();
-  }
-
-  ALWAYS_INLINE void ComputeTableOffsets() {
-    // Skip the header.
-    size_t bit_offset = HeaderSize() * kBitsPerByte;
-    // The byte tables must be aligned so they must go first.
-    dex_register_map.UpdateBitOffset(&bit_offset);
-    location_catalog.UpdateBitOffset(&bit_offset);
-    // Other tables don't require alignment.
-    stack_map.UpdateBitOffset(&bit_offset);
-    register_mask.UpdateBitOffset(&bit_offset);
-    stack_mask.UpdateBitOffset(&bit_offset);
-    invoke_info.UpdateBitOffset(&bit_offset);
-    inline_info.UpdateBitOffset(&bit_offset);
-    cache_non_header_size = RoundUp(bit_offset, kBitsPerByte) / kBitsPerByte - HeaderSize();
-  }
-
-  ALWAYS_INLINE size_t HeaderSize() const {
-    DCHECK_NE(cache_header_size, kInvalidSize) << "Uninitialized";
-    return cache_header_size;
-  }
-
-  ALWAYS_INLINE size_t NonHeaderSize() const {
-    DCHECK_NE(cache_non_header_size, kInvalidSize) << "Uninitialized";
-    return cache_non_header_size;
-  }
-
- private:
-  // Computed fields (not serialized).
-  // Header size in bytes, cached to avoid needing to re-decoding the encoding in HeaderSize.
-  SizeType cache_header_size = kInvalidSize;
-  // Non header size in bytes, cached to avoid needing to re-decoding the encoding in NonHeaderSize.
-  SizeType cache_non_header_size = kInvalidSize;
 };
 
 /**
  * Wrapper around all compiler information collected for a method.
  * The information is of the form:
  *
- *   [CodeInfoEncoding, DexRegisterMap+, DexLocationCatalog+, StackMap+, RegisterMask+, StackMask+,
- *    InlineInfo*]
+ *   [BitTable<Header>, BitTable<StackMap>, BitTable<RegisterMask>, BitTable<InlineInfo>,
+ *    BitTable<InvokeInfo>, BitTable<StackMask>, DexRegisterMap, DexLocationCatalog]
  *
- * where CodeInfoEncoding is of the form:
- *
- *   [ByteSizedTable(dex_register_map), ByteSizedTable(location_catalog),
- *    BitEncodingTable<StackMapEncoding>, BitEncodingTable<BitRegionEncoding>,
- *    BitEncodingTable<BitRegionEncoding>, BitEncodingTable<InlineInfoEncoding>]
  */
 class CodeInfo {
  public:
-  explicit CodeInfo(MemoryRegion region) : region_(region) {
-  }
-
   explicit CodeInfo(const void* data) {
-    CodeInfoEncoding encoding = CodeInfoEncoding(data);
-    region_ = MemoryRegion(const_cast<void*>(data),
-                           encoding.HeaderSize() + encoding.NonHeaderSize());
+    Decode(reinterpret_cast<const uint8_t*>(data));
   }
 
-  CodeInfoEncoding ExtractEncoding() const {
-    CodeInfoEncoding encoding(region_.begin());
-    AssertValidStackMap(encoding);
-    return encoding;
+  explicit CodeInfo(MemoryRegion region) : CodeInfo(region.begin()) {
+    DCHECK_EQ(size_, region.size());
   }
 
-  bool HasInlineInfo(const CodeInfoEncoding& encoding) const {
-    return encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0;
+  explicit CodeInfo(const OatQuickMethodHeader* header)
+    : CodeInfo(header->GetOptimizedCodeInfoPtr()) {
   }
 
-  DexRegisterLocationCatalog GetDexRegisterLocationCatalog(const CodeInfoEncoding& encoding) const {
-    return DexRegisterLocationCatalog(region_.Subregion(encoding.location_catalog.byte_offset,
-                                                        encoding.location_catalog.num_bytes));
+  size_t Size() const {
+    return size_;
   }
 
-  ALWAYS_INLINE size_t GetNumberOfStackMaskBits(const CodeInfoEncoding& encoding) const {
-    return encoding.stack_mask.encoding.BitSize();
+  bool HasInlineInfo() const {
+    return stack_maps_.NumColumnBits(StackMap::kInlineInfoIndex) != 0;
   }
 
-  ALWAYS_INLINE StackMap GetStackMapAt(size_t index, const CodeInfoEncoding& encoding) const {
-    return StackMap(encoding.stack_map.BitRegion(region_, index));
+  DexRegisterLocationCatalog GetDexRegisterLocationCatalog() const {
+    return DexRegisterLocationCatalog(location_catalog_);
   }
 
-  BitMemoryRegion GetStackMask(size_t index, const CodeInfoEncoding& encoding) const {
-    return encoding.stack_mask.BitRegion(region_, index);
+  ALWAYS_INLINE size_t GetNumberOfStackMaskBits() const {
+    return stack_mask_bits_;
   }
 
-  BitMemoryRegion GetStackMaskOf(const CodeInfoEncoding& encoding,
-                                 const StackMap& stack_map) const {
-    return GetStackMask(stack_map.GetStackMaskIndex(encoding.stack_map.encoding), encoding);
+  ALWAYS_INLINE StackMap GetStackMapAt(size_t index) const {
+    return StackMap(&stack_maps_, index);
   }
 
-  BitMemoryRegion GetRegisterMask(size_t index, const CodeInfoEncoding& encoding) const {
-    return encoding.register_mask.BitRegion(region_, index);
+  BitMemoryRegion GetStackMask(size_t index) const {
+    return stack_masks_.Subregion(index * stack_mask_bits_, stack_mask_bits_);
   }
 
-  uint32_t GetRegisterMaskOf(const CodeInfoEncoding& encoding, const StackMap& stack_map) const {
-    size_t index = stack_map.GetRegisterMaskIndex(encoding.stack_map.encoding);
-    return GetRegisterMask(index, encoding).LoadBits(0u, encoding.register_mask.encoding.BitSize());
+  BitMemoryRegion GetStackMaskOf(const StackMap& stack_map) const {
+    return GetStackMask(stack_map.GetStackMaskIndex());
   }
 
-  uint32_t GetNumberOfLocationCatalogEntries(const CodeInfoEncoding& encoding) const {
-    return encoding.location_catalog.num_entries;
+  uint32_t GetRegisterMaskOf(const StackMap& stack_map) const {
+    return register_masks_.Get(stack_map.GetRegisterMaskIndex());
   }
 
-  uint32_t GetDexRegisterLocationCatalogSize(const CodeInfoEncoding& encoding) const {
-    return encoding.location_catalog.num_bytes;
+  uint32_t GetNumberOfLocationCatalogEntries() const {
+    return location_catalog_entries_;
   }
 
-  uint32_t GetNumberOfStackMaps(const CodeInfoEncoding& encoding) const {
-    return encoding.stack_map.num_entries;
+  uint32_t GetDexRegisterLocationCatalogSize() const {
+    return location_catalog_.size();
   }
 
-  // Get the size of all the stack maps of this CodeInfo object, in bits. Not byte aligned.
-  ALWAYS_INLINE size_t GetStackMapsSizeInBits(const CodeInfoEncoding& encoding) const {
-    return encoding.stack_map.encoding.BitSize() * GetNumberOfStackMaps(encoding);
+  uint32_t GetNumberOfStackMaps() const {
+    return stack_maps_.NumRows();
   }
 
-  InvokeInfo GetInvokeInfo(const CodeInfoEncoding& encoding, size_t index) const {
-    return InvokeInfo(encoding.invoke_info.BitRegion(region_, index));
+  InvokeInfo GetInvokeInfo(size_t index) const {
+    return InvokeInfo(&invoke_infos_, index);
   }
 
   DexRegisterMap GetDexRegisterMapOf(StackMap stack_map,
-                                     const CodeInfoEncoding& encoding,
                                      size_t number_of_dex_registers) const {
-    if (!stack_map.HasDexRegisterMap(encoding.stack_map.encoding)) {
+    if (!stack_map.HasDexRegisterMap()) {
       return DexRegisterMap();
     }
-    const uint32_t offset = encoding.dex_register_map.byte_offset +
-        stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding);
-    size_t size = ComputeDexRegisterMapSizeOf(encoding, offset, number_of_dex_registers);
-    return DexRegisterMap(region_.Subregion(offset, size));
+    const uint32_t offset = stack_map.GetDexRegisterMapOffset();
+    size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers);
+    return DexRegisterMap(dex_register_maps_.Subregion(offset, size));
   }
 
-  size_t GetDexRegisterMapsSize(const CodeInfoEncoding& encoding,
-                                uint32_t number_of_dex_registers) const {
+  size_t GetDexRegisterMapsSize(uint32_t number_of_dex_registers) const {
     size_t total = 0;
-    for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) {
-      StackMap stack_map = GetStackMapAt(i, encoding);
-      DexRegisterMap map(GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers));
+    for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
+      StackMap stack_map = GetStackMapAt(i);
+      DexRegisterMap map(GetDexRegisterMapOf(stack_map, number_of_dex_registers));
       total += map.Size();
     }
     return total;
@@ -1458,38 +892,30 @@
   // Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`.
   DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth,
                                           InlineInfo inline_info,
-                                          const CodeInfoEncoding& encoding,
                                           uint32_t number_of_dex_registers) const {
-    if (!inline_info.HasDexRegisterMapAtDepth(encoding.inline_info.encoding, depth)) {
+    if (!inline_info.HasDexRegisterMapAtDepth(depth)) {
       return DexRegisterMap();
     } else {
-      uint32_t offset = encoding.dex_register_map.byte_offset +
-          inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, depth);
-      size_t size = ComputeDexRegisterMapSizeOf(encoding, offset, number_of_dex_registers);
-      return DexRegisterMap(region_.Subregion(offset, size));
+      uint32_t offset = inline_info.GetDexRegisterMapOffsetAtDepth(depth);
+      size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers);
+      return DexRegisterMap(dex_register_maps_.Subregion(offset, size));
     }
   }
 
-  InlineInfo GetInlineInfo(size_t index, const CodeInfoEncoding& encoding) const {
-    // Since we do not know the depth, we just return the whole remaining map. The caller may
-    // access the inline info for arbitrary depths. To return the precise inline info we would need
-    // to count the depth before returning.
-    // TODO: Clean this up.
-    const size_t bit_offset = encoding.inline_info.bit_offset +
-        index * encoding.inline_info.encoding.BitSize();
-    return InlineInfo(BitMemoryRegion(region_, bit_offset, region_.size_in_bits() - bit_offset));
+  InlineInfo GetInlineInfo(size_t index) const {
+    return InlineInfo(&inline_infos_, index);
   }
 
-  InlineInfo GetInlineInfoOf(StackMap stack_map, const CodeInfoEncoding& encoding) const {
-    DCHECK(stack_map.HasInlineInfo(encoding.stack_map.encoding));
-    uint32_t index = stack_map.GetInlineInfoIndex(encoding.stack_map.encoding);
-    return GetInlineInfo(index, encoding);
+  InlineInfo GetInlineInfoOf(StackMap stack_map) const {
+    DCHECK(stack_map.HasInlineInfo());
+    uint32_t index = stack_map.GetInlineInfoIndex();
+    return GetInlineInfo(index);
   }
 
-  StackMap GetStackMapForDexPc(uint32_t dex_pc, const CodeInfoEncoding& encoding) const {
-    for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) {
-      StackMap stack_map = GetStackMapAt(i, encoding);
-      if (stack_map.GetDexPc(encoding.stack_map.encoding) == dex_pc) {
+  StackMap GetStackMapForDexPc(uint32_t dex_pc) const {
+    for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
+      StackMap stack_map = GetStackMapAt(i);
+      if (stack_map.GetDexPc() == dex_pc) {
         return stack_map;
       }
     }
@@ -1498,40 +924,39 @@
 
   // Searches the stack map list backwards because catch stack maps are stored
   // at the end.
-  StackMap GetCatchStackMapForDexPc(uint32_t dex_pc, const CodeInfoEncoding& encoding) const {
-    for (size_t i = GetNumberOfStackMaps(encoding); i > 0; --i) {
-      StackMap stack_map = GetStackMapAt(i - 1, encoding);
-      if (stack_map.GetDexPc(encoding.stack_map.encoding) == dex_pc) {
+  StackMap GetCatchStackMapForDexPc(uint32_t dex_pc) const {
+    for (size_t i = GetNumberOfStackMaps(); i > 0; --i) {
+      StackMap stack_map = GetStackMapAt(i - 1);
+      if (stack_map.GetDexPc() == dex_pc) {
         return stack_map;
       }
     }
     return StackMap();
   }
 
-  StackMap GetOsrStackMapForDexPc(uint32_t dex_pc, const CodeInfoEncoding& encoding) const {
-    size_t e = GetNumberOfStackMaps(encoding);
+  StackMap GetOsrStackMapForDexPc(uint32_t dex_pc) const {
+    size_t e = GetNumberOfStackMaps();
     if (e == 0) {
       // There cannot be OSR stack map if there is no stack map.
       return StackMap();
     }
     // Walk over all stack maps. If two consecutive stack maps are identical, then we
     // have found a stack map suitable for OSR.
-    const StackMapEncoding& stack_map_encoding = encoding.stack_map.encoding;
     for (size_t i = 0; i < e - 1; ++i) {
-      StackMap stack_map = GetStackMapAt(i, encoding);
-      if (stack_map.GetDexPc(stack_map_encoding) == dex_pc) {
-        StackMap other = GetStackMapAt(i + 1, encoding);
-        if (other.GetDexPc(stack_map_encoding) == dex_pc &&
-            other.GetNativePcOffset(stack_map_encoding, kRuntimeISA) ==
-                stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA)) {
-          DCHECK_EQ(other.GetDexRegisterMapOffset(stack_map_encoding),
-                    stack_map.GetDexRegisterMapOffset(stack_map_encoding));
-          DCHECK(!stack_map.HasInlineInfo(stack_map_encoding));
+      StackMap stack_map = GetStackMapAt(i);
+      if (stack_map.GetDexPc() == dex_pc) {
+        StackMap other = GetStackMapAt(i + 1);
+        if (other.GetDexPc() == dex_pc &&
+            other.GetNativePcOffset(kRuntimeISA) ==
+                stack_map.GetNativePcOffset(kRuntimeISA)) {
+          DCHECK_EQ(other.GetDexRegisterMapOffset(),
+                    stack_map.GetDexRegisterMapOffset());
+          DCHECK(!stack_map.HasInlineInfo());
           if (i < e - 2) {
             // Make sure there are not three identical stack maps following each other.
             DCHECK_NE(
-                stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA),
-                GetStackMapAt(i + 2, encoding).GetNativePcOffset(stack_map_encoding, kRuntimeISA));
+                stack_map.GetNativePcOffset(kRuntimeISA),
+                GetStackMapAt(i + 2).GetNativePcOffset(kRuntimeISA));
           }
           return stack_map;
         }
@@ -1540,30 +965,27 @@
     return StackMap();
   }
 
-  StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset,
-                                        const CodeInfoEncoding& encoding) const {
+  StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) const {
     // TODO: Safepoint stack maps are sorted by native_pc_offset but catch stack
     //       maps are not. If we knew that the method does not have try/catch,
     //       we could do binary search.
-    for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) {
-      StackMap stack_map = GetStackMapAt(i, encoding);
-      if (stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA) ==
-          native_pc_offset) {
+    for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
+      StackMap stack_map = GetStackMapAt(i);
+      if (stack_map.GetNativePcOffset(kRuntimeISA) == native_pc_offset) {
         return stack_map;
       }
     }
     return StackMap();
   }
 
-  InvokeInfo GetInvokeInfoForNativePcOffset(uint32_t native_pc_offset,
-                                            const CodeInfoEncoding& encoding) {
-    for (size_t index = 0; index < encoding.invoke_info.num_entries; index++) {
-      InvokeInfo item = GetInvokeInfo(encoding, index);
-      if (item.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA) == native_pc_offset) {
+  InvokeInfo GetInvokeInfoForNativePcOffset(uint32_t native_pc_offset) {
+    for (size_t index = 0; index < invoke_infos_.NumRows(); index++) {
+      InvokeInfo item = GetInvokeInfo(index);
+      if (item.GetNativePcOffset(kRuntimeISA) == native_pc_offset) {
         return item;
       }
     }
-    return InvokeInfo(BitMemoryRegion());
+    return InvokeInfo(&invoke_infos_, -1);
   }
 
   // Dump this CodeInfo object on `os`.  `code_offset` is the (absolute)
@@ -1578,23 +1000,10 @@
             InstructionSet instruction_set,
             const MethodInfo& method_info) const;
 
-  // Check that the code info has valid stack map and abort if it does not.
-  void AssertValidStackMap(const CodeInfoEncoding& encoding) const {
-    if (region_.size() != 0 && region_.size_in_bits() < GetStackMapsSizeInBits(encoding)) {
-      LOG(FATAL) << region_.size() << "\n"
-                 << encoding.HeaderSize() << "\n"
-                 << encoding.NonHeaderSize() << "\n"
-                 << encoding.location_catalog.num_entries << "\n"
-                 << encoding.stack_map.num_entries << "\n"
-                 << encoding.stack_map.encoding.BitSize();
-    }
-  }
-
  private:
   // Compute the size of the Dex register map associated to the stack map at
   // `dex_register_map_offset_in_code_info`.
-  size_t ComputeDexRegisterMapSizeOf(const CodeInfoEncoding& encoding,
-                                     uint32_t dex_register_map_offset_in_code_info,
+  size_t ComputeDexRegisterMapSizeOf(uint32_t dex_register_map_offset,
                                      uint16_t number_of_dex_registers) const {
     // Offset where the actual mapping data starts within art::DexRegisterMap.
     size_t location_mapping_data_offset_in_dex_register_map =
@@ -1602,12 +1011,12 @@
     // Create a temporary art::DexRegisterMap to be able to call
     // art::DexRegisterMap::GetNumberOfLiveDexRegisters and
     DexRegisterMap dex_register_map_without_locations(
-        MemoryRegion(region_.Subregion(dex_register_map_offset_in_code_info,
-                                       location_mapping_data_offset_in_dex_register_map)));
+        MemoryRegion(dex_register_maps_.Subregion(dex_register_map_offset,
+                                        location_mapping_data_offset_in_dex_register_map)));
     size_t number_of_live_dex_registers =
         dex_register_map_without_locations.GetNumberOfLiveDexRegisters(number_of_dex_registers);
     size_t location_mapping_data_size_in_bits =
-        DexRegisterMap::SingleEntrySizeInBits(GetNumberOfLocationCatalogEntries(encoding))
+        DexRegisterMap::SingleEntrySizeInBits(GetNumberOfLocationCatalogEntries())
         * number_of_live_dex_registers;
     size_t location_mapping_data_size_in_bytes =
         RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte;
@@ -1616,37 +1025,42 @@
     return dex_register_map_size;
   }
 
-  // Compute the size of a Dex register location catalog starting at offset `origin`
-  // in `region_` and containing `number_of_dex_locations` entries.
-  size_t ComputeDexRegisterLocationCatalogSize(uint32_t origin,
-                                               uint32_t number_of_dex_locations) const {
-    // TODO: Ideally, we would like to use art::DexRegisterLocationCatalog::Size or
-    // art::DexRegisterLocationCatalog::FindLocationOffset, but the
-    // DexRegisterLocationCatalog is not yet built.  Try to factor common code.
-    size_t offset = origin + DexRegisterLocationCatalog::kFixedSize;
-
-    // Skip the first `number_of_dex_locations - 1` entries.
-    for (uint16_t i = 0; i < number_of_dex_locations; ++i) {
-      // Read the first next byte and inspect its first 3 bits to decide
-      // whether it is a short or a large location.
-      DexRegisterLocationCatalog::ShortLocation first_byte =
-          region_.LoadUnaligned<DexRegisterLocationCatalog::ShortLocation>(offset);
-      DexRegisterLocation::Kind kind =
-          DexRegisterLocationCatalog::ExtractKindFromShortLocation(first_byte);
-      if (DexRegisterLocation::IsShortLocationKind(kind)) {
-        // Short location.  Skip the current byte.
-        offset += DexRegisterLocationCatalog::SingleShortEntrySize();
-      } else {
-        // Large location.  Skip the 5 next bytes.
-        offset += DexRegisterLocationCatalog::SingleLargeEntrySize();
-      }
-    }
-    size_t size = offset - origin;
-    return size;
+  MemoryRegion DecodeMemoryRegion(MemoryRegion& region, size_t* bit_offset) {
+    size_t length = DecodeVarintBits(BitMemoryRegion(region), bit_offset);
+    size_t offset = BitsToBytesRoundUp(*bit_offset);;
+    *bit_offset = (offset + length) * kBitsPerByte;
+    return region.Subregion(offset, length);
   }
 
-  MemoryRegion region_;
-  friend class StackMapStream;
+  void Decode(const uint8_t* data) {
+    size_t non_header_size = DecodeUnsignedLeb128(&data);
+    MemoryRegion region(const_cast<uint8_t*>(data), non_header_size);
+    BitMemoryRegion bit_region(region);
+    size_t bit_offset = 0;
+    size_ = UnsignedLeb128Size(non_header_size) + non_header_size;
+    dex_register_maps_ = DecodeMemoryRegion(region, &bit_offset);
+    location_catalog_entries_ = DecodeVarintBits(bit_region, &bit_offset);
+    location_catalog_ = DecodeMemoryRegion(region, &bit_offset);
+    stack_maps_.Decode(bit_region, &bit_offset);
+    invoke_infos_.Decode(bit_region, &bit_offset);
+    inline_infos_.Decode(bit_region, &bit_offset);
+    register_masks_.Decode(bit_region, &bit_offset);
+    stack_mask_bits_ = DecodeVarintBits(bit_region, &bit_offset);
+    stack_masks_ = bit_region.Subregion(bit_offset, non_header_size * kBitsPerByte - bit_offset);
+  }
+
+  size_t size_;
+  MemoryRegion dex_register_maps_;
+  uint32_t location_catalog_entries_;
+  MemoryRegion location_catalog_;
+  BitTable<StackMap::Field::kCount> stack_maps_;
+  BitTable<InvokeInfo::Field::kCount> invoke_infos_;
+  BitTable<InlineInfo::Field::kCount> inline_infos_;
+  BitTable<1> register_masks_;
+  uint32_t stack_mask_bits_ = 0;
+  BitMemoryRegion stack_masks_;
+
+  friend class OatDumper;
 };
 
 #undef ELEMENT_BYTE_OFFSET_AFTER
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index e34f32e..91c27af 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -23,7 +23,7 @@
 #include "base/casts.h"
 #include "base/mutex-inl.h"
 #include "base/time_utils.h"
-#include "jni_env_ext.h"
+#include "jni/jni_env_ext.h"
 #include "managed_stack-inl.h"
 #include "obj_ptr.h"
 #include "thread-current-inl.h"
diff --git a/runtime/thread.cc b/runtime/thread.cc
index f6ac64f..2275dae 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -65,8 +65,8 @@
 #include "interpreter/interpreter.h"
 #include "interpreter/shadow_frame.h"
 #include "java_frame_root_info.h"
-#include "java_vm_ext.h"
-#include "jni_internal.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/object_array-inl.h"
@@ -1115,21 +1115,10 @@
   Runtime* runtime = Runtime::Current();
   bool implicit_stack_check = !runtime->ExplicitStackOverflowChecks() && !runtime->IsAotCompiler();
 
-  // Valgrind on arm doesn't give the right values here. Do not install the guard page, and
-  // effectively disable stack overflow checks (we'll get segfaults, potentially) by setting
-  // stack_begin to 0.
-  const bool valgrind_on_arm =
-      (kRuntimeISA == InstructionSet::kArm || kRuntimeISA == InstructionSet::kArm64) &&
-      kMemoryToolIsValgrind &&
-      RUNNING_ON_MEMORY_TOOL != 0;
-  if (valgrind_on_arm) {
-    tlsPtr_.stack_begin = nullptr;
-  }
-
   ResetDefaultStackEnd();
 
   // Install the protected region if we are doing implicit overflow checks.
-  if (implicit_stack_check && !valgrind_on_arm) {
+  if (implicit_stack_check) {
     // The thread might have protected region at the bottom.  We need
     // to install our own region so we need to move the limits
     // of the stack to make room for it.
@@ -3559,16 +3548,15 @@
       StackReference<mirror::Object>* vreg_base = reinterpret_cast<StackReference<mirror::Object>*>(
           reinterpret_cast<uintptr_t>(cur_quick_frame));
       uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc());
-      CodeInfo code_info = method_header->GetOptimizedCodeInfo();
-      CodeInfoEncoding encoding = code_info.ExtractEncoding();
-      StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+      CodeInfo code_info(method_header);
+      StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
       DCHECK(map.IsValid());
 
-      T vreg_info(m, code_info, encoding, map, visitor_);
+      T vreg_info(m, code_info, map, visitor_);
 
       // Visit stack entries that hold pointers.
-      const size_t number_of_bits = code_info.GetNumberOfStackMaskBits(encoding);
-      BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, map);
+      const size_t number_of_bits = code_info.GetNumberOfStackMaskBits();
+      BitMemoryRegion stack_mask = code_info.GetStackMaskOf(map);
       for (size_t i = 0; i < number_of_bits; ++i) {
         if (stack_mask.LoadBit(i)) {
           StackReference<mirror::Object>* ref_addr = vreg_base + i;
@@ -3583,7 +3571,7 @@
         }
       }
       // Visit callee-save registers that hold pointers.
-      uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, map);
+      uint32_t register_mask = code_info.GetRegisterMaskOf(map);
       for (size_t i = 0; i < BitSizeOf<uint32_t>(); ++i) {
         if (register_mask & (1 << i)) {
           mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(i));
@@ -3631,7 +3619,6 @@
     struct UndefinedVRegInfo {
       UndefinedVRegInfo(ArtMethod* method ATTRIBUTE_UNUSED,
                         const CodeInfo& code_info ATTRIBUTE_UNUSED,
-                        const CodeInfoEncoding& encoding ATTRIBUTE_UNUSED,
                         const StackMap& map ATTRIBUTE_UNUSED,
                         RootVisitor& _visitor)
           : visitor(_visitor) {
@@ -3662,14 +3649,11 @@
     struct StackMapVRegInfo {
       StackMapVRegInfo(ArtMethod* method,
                        const CodeInfo& _code_info,
-                       const CodeInfoEncoding& _encoding,
                        const StackMap& map,
                        RootVisitor& _visitor)
           : number_of_dex_registers(method->DexInstructionData().RegistersSize()),
             code_info(_code_info),
-            encoding(_encoding),
             dex_register_map(code_info.GetDexRegisterMapOf(map,
-                                                           encoding,
                                                            number_of_dex_registers)),
             visitor(_visitor) {
       }
@@ -3684,7 +3668,7 @@
         bool found = false;
         for (size_t dex_reg = 0; dex_reg != number_of_dex_registers; ++dex_reg) {
           DexRegisterLocation location = dex_register_map.GetDexRegisterLocation(
-              dex_reg, number_of_dex_registers, code_info, encoding);
+              dex_reg, number_of_dex_registers, code_info);
           if (location.GetKind() == kind && static_cast<size_t>(location.GetValue()) == index) {
             visitor(ref, dex_reg, stack_visitor);
             found = true;
@@ -3718,7 +3702,6 @@
 
       size_t number_of_dex_registers;
       const CodeInfo& code_info;
-      const CodeInfoEncoding& encoding;
       DexRegisterMap dex_register_map;
       RootVisitor& visitor;
     };
diff --git a/runtime/thread.h b/runtime/thread.h
index 22b77ee..3ec050a 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -30,11 +30,11 @@
 #include "arch/instruction_set.h"
 #include "base/atomic.h"
 #include "base/enums.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "globals.h"
 #include "handle_scope.h"
 #include "instrumentation.h"
 #include "jvalue.h"
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 44af867..b2be549 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -40,7 +40,7 @@
 #include "gc/heap.h"
 #include "gc/reference_processor.h"
 #include "gc_root.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "lock_word.h"
 #include "monitor.h"
 #include "native_stack_dump.h"
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index 15c514e..608f0ee 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -21,7 +21,7 @@
 #include "nativeloader/native_loader.h"
 
 #include "base/strlcpy.h"
-#include "java_vm_ext.h"
+#include "jni/java_vm_ext.h"
 #include "runtime.h"
 #include "thread-current-inl.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/runtime/trace.h b/runtime/trace.h
index b242d15..1fae250 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -27,10 +27,10 @@
 #include <vector>
 
 #include "base/atomic.h"
+#include "base/globals.h"
 #include "base/macros.h"
 #include "base/os.h"
 #include "base/safe_map.h"
-#include "globals.h"
 #include "instrumentation.h"
 
 namespace unix_file {
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index 68a5760..c0ea6be 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -22,9 +22,9 @@
 #include <android-base/logging.h>
 
 #include "base/bit_utils.h"
+#include "base/globals.h"
 #include "dex/primitive.h"
 #include "gc_root.h"
-#include "globals.h"
 #include "mirror/dex_cache.h"
 
 namespace art {
diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc
new file mode 100644
index 0000000..f08742f
--- /dev/null
+++ b/runtime/var_handles.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 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 "var_handles.h"
+
+#include "common_throws.h"
+#include "dex/dex_instruction.h"
+#include "handle.h"
+#include "method_handles-inl.h"
+#include "mirror/method_type.h"
+#include "mirror/var_handle.h"
+
+namespace art {
+
+namespace {
+
+bool VarHandleInvokeAccessorWithConversions(Thread* self,
+                                            ShadowFrame& shadow_frame,
+                                            Handle<mirror::VarHandle> var_handle,
+                                            Handle<mirror::MethodType> callsite_type,
+                                            const mirror::VarHandle::AccessMode access_mode,
+                                            const InstructionOperands* const operands,
+                                            JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::MethodType> accessor_type(hs.NewHandle(
+      var_handle->GetMethodTypeForAccessMode(self, access_mode)));
+  const size_t num_vregs = accessor_type->NumberOfVRegs();
+  const int num_params = accessor_type->GetPTypes()->GetLength();
+  ShadowFrameAllocaUniquePtr accessor_frame =
+      CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
+  ShadowFrameGetter getter(shadow_frame, operands);
+  static const uint32_t kFirstDestinationReg = 0;
+  ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
+  if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) {
+    return false;
+  }
+  RangeInstructionOperands accessor_operands(kFirstDestinationReg,
+                                             kFirstDestinationReg + num_vregs);
+  if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) {
+    return false;
+  }
+  return ConvertReturnValue(callsite_type, accessor_type, result);
+}
+
+}  // namespace
+
+bool VarHandleInvokeAccessor(Thread* self,
+                             ShadowFrame& shadow_frame,
+                             Handle<mirror::VarHandle> var_handle,
+                             Handle<mirror::MethodType> callsite_type,
+                             const mirror::VarHandle::AccessMode access_mode,
+                             const InstructionOperands* const operands,
+                             JValue* result) {
+  if (var_handle.IsNull()) {
+    ThrowNullPointerExceptionFromDexPC();
+    return false;
+  }
+
+  if (!var_handle->IsAccessModeSupported(access_mode)) {
+    ThrowUnsupportedOperationException();
+    return false;
+  }
+
+  mirror::VarHandle::MatchKind match_kind =
+      var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type.Get());
+  if (LIKELY(match_kind == mirror::VarHandle::MatchKind::kExact)) {
+    return var_handle->Access(access_mode, &shadow_frame, operands, result);
+  } else if (match_kind == mirror::VarHandle::MatchKind::kWithConversions) {
+    return VarHandleInvokeAccessorWithConversions(self,
+                                                  shadow_frame,
+                                                  var_handle,
+                                                  callsite_type,
+                                                  access_mode,
+                                                  operands,
+                                                  result);
+  } else {
+    DCHECK_EQ(match_kind, mirror::VarHandle::MatchKind::kNone);
+    ThrowWrongMethodTypeException(var_handle->PrettyDescriptorForAccessMode(access_mode),
+                                  callsite_type->PrettyDescriptor());
+    return false;
+  }
+}
+
+}  // namespace art
diff --git a/runtime/var_handles.h b/runtime/var_handles.h
new file mode 100644
index 0000000..2ff8405
--- /dev/null
+++ b/runtime/var_handles.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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_VAR_HANDLES_H_
+#define ART_RUNTIME_VAR_HANDLES_H_
+
+#include "mirror/var_handle.h"
+
+namespace art {
+
+bool VarHandleInvokeAccessor(Thread* self,
+                             ShadowFrame& shadow_frame,
+                             Handle<mirror::VarHandle> var_handle,
+                             Handle<mirror::MethodType> callsite_type,
+                             const mirror::VarHandle::AccessMode access_mode,
+                             const InstructionOperands* const operands,
+                             JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_VAR_HANDLES_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 72292c3..91cec23 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2287,14 +2287,10 @@
     case Instruction::CONST_METHOD_HANDLE:
       work_line_->SetRegisterType<LockOp::kClear>(
           this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodHandle());
-      // TODO: add compiler support for const-method-{handle,type} (b/66890674)
-      Fail(VERIFY_ERROR_FORCE_INTERPRETER);
       break;
     case Instruction::CONST_METHOD_TYPE:
       work_line_->SetRegisterType<LockOp::kClear>(
           this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodType());
-      // TODO: add compiler support for const-method-{handle,type} (b/66890674)
-      Fail(VERIFY_ERROR_FORCE_INTERPRETER);
       break;
     case Instruction::MONITOR_ENTER:
       work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_);
@@ -3090,7 +3086,8 @@
         DCHECK(HasFailures());
         break;
       }
-      const uint32_t proto_idx = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
+      const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
+      const dex::ProtoIndex proto_idx(vRegH);
       const char* return_descriptor =
           dex_file_->GetReturnTypeDescriptor(dex_file_->GetProtoId(proto_idx));
       const RegType& return_type =
@@ -3121,7 +3118,7 @@
       CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
       it.Next();  // Skip to name.
       it.Next();  // Skip to method type of the method handle
-      const uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+      const dex::ProtoIndex proto_idx(it.GetJavaValue().c);
       const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx);
       DexFileParameterIterator param_it(*dex_file_, proto_id);
       // Treat method as static as it has yet to be determined.
@@ -3801,16 +3798,8 @@
     must_fail = true;
     // Try to find the method also with the other type for better error reporting below
     // but do not store such bogus lookup result in the DexCache or VerifierDeps.
-    if (klass->IsInterface()) {
-      // NB This is normally not really allowed but we want to get any static or private object
-      // methods for error message purposes. This will never be returned.
-      // TODO We might want to change the verifier to not require this.
-      res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
-    } else {
-      // If there was an interface method with the same signature,
-      // we would have found it also in the "copied" methods.
-      DCHECK(klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size) == nullptr);
-    }
+    res_method = class_linker->FindIncompatibleMethod(
+        klass, dex_cache_.Get(), class_loader_.Get(), dex_method_idx);
   }
 
   if (res_method == nullptr) {
@@ -4202,7 +4191,8 @@
 
   if (UNLIKELY(method_type == METHOD_POLYMORPHIC)) {
     // Process the signature of the calling site that is invoking the method handle.
-    DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(inst->VRegH()));
+    dex::ProtoIndex proto_idx(inst->VRegH());
+    DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(proto_idx));
     return VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, res_method);
   } else {
     // Process the target method's signature.
@@ -4220,8 +4210,6 @@
     expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name);
   } else if (klass == mirror::VarHandle::StaticClass()) {
     expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name);
-    // TODO: add compiler support for VarHandle accessor methods (b/71781600)
-    Fail(VERIFY_ERROR_FORCE_INTERPRETER);
   } else {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD)
         << "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor();
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index ea30e05..1bbf5a6 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -148,7 +148,9 @@
     result += StringPrintf("{%d},", monitor);
   }
   for (auto& pairs : reg_to_lock_depths_) {
-    result += StringPrintf("<%d -> %x>", pairs.first, pairs.second);
+    result += StringPrintf("<%d -> %" PRIx64 ">",
+                           pairs.first,
+                           static_cast<uint64_t>(pairs.second));
   }
   return result;
 }
@@ -337,7 +339,7 @@
   if (!reg_type.IsReferenceTypes()) {
     verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter on non-object ("
         << reg_type << ")";
-  } else if (monitors_.size() >= 32) {
+  } else if (monitors_.size() >= kMaxMonitorStackDepth) {
     verifier->Fail(VERIFY_ERROR_LOCKING);
     if (kDumpLockFailures) {
       VLOG(verifier) << "monitor-enter stack overflow while verifying "
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 168eb7b..9bb60bb 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_VERIFIER_REGISTER_LINE_H_
 #define ART_RUNTIME_VERIFIER_REGISTER_LINE_H_
 
+#include <limits>
 #include <memory>
 #include <vector>
 
@@ -62,8 +63,14 @@
 // stack of entered monitors (identified by code unit offset).
 class RegisterLine {
  public:
+  using RegisterStackMask = uint32_t;
   // A map from register to a bit vector of indices into the monitors_ stack.
-  using RegToLockDepthsMap = ScopedArenaSafeMap<uint32_t, uint32_t>;
+  using RegToLockDepthsMap = ScopedArenaSafeMap<uint32_t, RegisterStackMask>;
+
+  // Maximum number of nested monitors to track before giving up and
+  // taking the slow path.
+  static constexpr size_t kMaxMonitorStackDepth =
+      std::numeric_limits<RegisterStackMask>::digits;
 
   // Create a register line of num_regs registers.
   static RegisterLine* Create(size_t num_regs, MethodVerifier* verifier);
@@ -391,7 +398,7 @@
   }
 
   bool SetRegToLockDepth(size_t reg, size_t depth) {
-    CHECK_LT(depth, 32u);
+    CHECK_LT(depth, kMaxMonitorStackDepth);
     if (IsSetLockDepth(reg, depth)) {
       return false;  // Register already holds lock so locking twice is erroneous.
     }
diff --git a/runtime/verify_object.cc b/runtime/verify_object.cc
index a031a07..70ca13f 100644
--- a/runtime/verify_object.cc
+++ b/runtime/verify_object.cc
@@ -17,8 +17,8 @@
 #include "verify_object-inl.h"
 
 #include "base/bit_utils.h"
+#include "base/globals.h"
 #include "gc/heap.h"
-#include "globals.h"
 #include "mirror/object-inl.h"
 #include "obj_ptr-inl.h"
 #include "runtime.h"
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index f5d112c..f7cdf39 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -23,13 +23,16 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
+#include "base/enums.h"
+#include "class_linker.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "hidden_api.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class.h"
 #include "mirror/throwable.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "obj_ptr-inl.h"
+#include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
 
@@ -231,19 +234,28 @@
   V(java_lang_String_init_StringBuilder, "(Ljava/lang/StringBuilder;)V", newStringFromStringBuilder, "newStringFromStringBuilder", "(Ljava/lang/StringBuilder;)Ljava/lang/String;", NewStringFromStringBuilder) \
 
 #define STATIC_STRING_INIT(init_runtime_name, init_signature, new_runtime_name, ...) \
-    static ArtMethod* init_runtime_name; \
-    static ArtMethod* new_runtime_name;
+    static ArtMethod* init_runtime_name = nullptr; \
+    static ArtMethod* new_runtime_name = nullptr;
     STRING_INIT_LIST(STATIC_STRING_INIT)
 #undef STATIC_STRING_INIT
 
-void WellKnownClasses::InitStringInit(JNIEnv* env) {
-  ScopedObjectAccess soa(Thread::Current());
-  #define LOAD_STRING_INIT(init_runtime_name, init_signature, new_runtime_name,             \
-                           new_java_name, new_signature, ...)                               \
-      init_runtime_name = jni::DecodeArtMethod(                                             \
-          CacheMethod(env, java_lang_String, false, "<init>", init_signature));             \
-      new_runtime_name = jni::DecodeArtMethod(                                              \
-          CacheMethod(env, java_lang_StringFactory, true, new_java_name, new_signature));
+void WellKnownClasses::InitStringInit(ObjPtr<mirror::Class> string_class,
+                                      ObjPtr<mirror::Class> string_builder_class) {
+  PointerSize p_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+  auto find_method = [p_size](ObjPtr<mirror::Class> klass,
+                              const char* name,
+                              const char* sig,
+                              bool expext_static) REQUIRES_SHARED(Locks::mutator_lock_) {
+    ArtMethod* ret = klass->FindClassMethod(name, sig, p_size);
+    CHECK(ret != nullptr);
+    CHECK_EQ(expext_static, ret->IsStatic());
+    return ret;
+  };
+
+  #define LOAD_STRING_INIT(init_runtime_name, init_signature, new_runtime_name,                  \
+                           new_java_name, new_signature, ...)                                    \
+      init_runtime_name = find_method(string_class, "<init>", init_signature, false);            \
+      new_runtime_name = find_method(string_builder_class, new_java_name, new_signature, true);
       STRING_INIT_LIST(LOAD_STRING_INIT)
   #undef LOAD_STRING_INIT
 }
@@ -252,6 +264,7 @@
   QuickEntryPoints* qpoints = &tlsPtr_.quick_entrypoints;
   #define SET_ENTRY_POINT(init_runtime_name, init_signature, new_runtime_name,              \
                           new_java_name, new_signature, entry_point_name)                   \
+      DCHECK(!Runtime::Current()->IsStarted() || (new_runtime_name) != nullptr);            \
       qpoints->p ## entry_point_name = reinterpret_cast<void(*)()>(new_runtime_name);
       STRING_INIT_LIST(SET_ENTRY_POINT)
   #undef SET_ENTRY_POINT
@@ -260,7 +273,9 @@
 ArtMethod* WellKnownClasses::StringInitToStringFactory(ArtMethod* string_init) {
   #define TO_STRING_FACTORY(init_runtime_name, init_signature, new_runtime_name,            \
                             new_java_name, new_signature, entry_point_name)                 \
+      DCHECK((init_runtime_name) != nullptr);                                               \
       if (string_init == (init_runtime_name)) {                                             \
+        DCHECK((new_runtime_name) != nullptr);                                              \
         return (new_runtime_name);                                                          \
       }
       STRING_INIT_LIST(TO_STRING_FACTORY)
@@ -282,26 +297,9 @@
 }
 #undef STRING_INIT_LIST
 
-class ScopedHiddenApiExemption {
- public:
-  explicit ScopedHiddenApiExemption(Runtime* runtime)
-      : runtime_(runtime),
-        initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) {
-    runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
-  }
-
-  ~ScopedHiddenApiExemption() {
-    runtime_->SetHiddenApiEnforcementPolicy(initial_policy_);
-  }
-
- private:
-  Runtime* runtime_;
-  const hiddenapi::EnforcementPolicy initial_policy_;
-  DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption);
-};
-
 void WellKnownClasses::Init(JNIEnv* env) {
-  ScopedHiddenApiExemption hiddenapi_exemption(Runtime::Current());
+  hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
+      hiddenapi::EnforcementPolicy::kNoChecks);
 
   dalvik_annotation_optimization_CriticalNative =
       CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
@@ -427,9 +425,6 @@
   java_lang_Integer_valueOf = CachePrimitiveBoxingMethod(env, 'I', "java/lang/Integer");
   java_lang_Long_valueOf = CachePrimitiveBoxingMethod(env, 'J', "java/lang/Long");
   java_lang_Short_valueOf = CachePrimitiveBoxingMethod(env, 'S', "java/lang/Short");
-
-  InitStringInit(env);
-  Thread::Current()->InitStringEntryPoints();
 }
 
 void WellKnownClasses::LateInit(JNIEnv* env) {
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 25c07b2..c06e4a7 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -40,6 +40,9 @@
 
   static void Clear();
 
+  static void InitStringInit(ObjPtr<mirror::Class> string_class,
+                             ObjPtr<mirror::Class> string_builder_class)
+      REQUIRES_SHARED(Locks::mutator_lock_);
   static ArtMethod* StringInitToStringFactory(ArtMethod* method);
   static uint32_t StringInitToEntryPoint(ArtMethod* method);
 
@@ -168,9 +171,6 @@
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_type;
-
- private:
-  static void InitStringInit(JNIEnv* env);
 };
 
 }  // namespace art
diff --git a/simulator/Android.bp b/simulator/Android.bp
index 74b5a90..8690426 100644
--- a/simulator/Android.bp
+++ b/simulator/Android.bp
@@ -44,6 +44,7 @@
     defaults: ["libart_simulator_defaults"],
     shared_libs: [
         "libart",
+        "libartbase",
         "libvixl-arm64",
     ],
 }
@@ -56,6 +57,7 @@
     ],
     shared_libs: [
         "libartd",
+        "libartbased",
         "libvixld-arm64",
     ],
 }
@@ -80,6 +82,7 @@
     name: "libart-simulator-container",
     defaults: ["libart_simulator_container_defaults"],
     shared_libs: [
+        "libartbase",
         "libart",
     ],
 }
@@ -91,6 +94,7 @@
         "libart_simulator_container_defaults",
     ],
     shared_libs: [
+        "libartbased",
         "libartd",
     ],
 }
diff --git a/simulator/code_simulator_container.cc b/simulator/code_simulator_container.cc
index 9f52b32..3206bc7 100644
--- a/simulator/code_simulator_container.cc
+++ b/simulator/code_simulator_container.cc
@@ -18,9 +18,9 @@
 
 #include "code_simulator_container.h"
 
+#include "base/globals.h"
 #include "base/logging.h"  // For VLOG.
 #include "code_simulator.h"
-#include "globals.h"
 
 namespace art {
 
diff --git a/test/001-HelloWorld/src/Main.java b/test/001-HelloWorld/src/Main.java
index 401e852..1ef6289 100644
--- a/test/001-HelloWorld/src/Main.java
+++ b/test/001-HelloWorld/src/Main.java
@@ -14,37 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.concurrent.*;
-
-interface I {
-}
-
-class A implements I {
-    static int x = (int)(10*Math.random());  // Suppress initialization.
-}
-
 public class Main {
-  public static void main(String[] args) throws Exception {
-
-      final CountDownLatch first = new CountDownLatch(1);
-      final CountDownLatch second = new CountDownLatch(1);
-
-      new Thread(new Runnable() {
-          public void run() {
-              try {
-                  synchronized(I.class) {
-                      first.countDown();
-                      second.await();
-                  }
-              } catch (Exception e) {
-                  e.printStackTrace();
-              }
-          }
-      }).start();
-
-      first.await();
-      new A();
-      second.countDown();
-      System.out.println("Hello, world!");
+  public static void main(String[] args) {
+    System.out.println("Hello, world!");
   }
 }
diff --git a/test/080-oom-throw/run b/test/080-oom-throw/run
index eb47378..08db73b 100644
--- a/test/080-oom-throw/run
+++ b/test/080-oom-throw/run
@@ -14,4 +14,18 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Ensure the minimum log severity is at least 'WARNING' to display the
+# stack trace shown before exception
+#
+#   "java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying
+#   to throw OutOfMemoryError; no stack trace available"
+#
+# is set, to try to understand a recurring crash in this test (b/77567088).
+case "$ANDROID_LOG_TAGS" in
+  # Lower the minimum log severity to WARNING if it was initialy set
+  # to a higher level ('ERROR', 'FATAL' or 'SILENT' -- see
+  # https://developer.android.com/studio/command-line/logcat#filteringOutput).
+  (\*:[efs]) export ANDROID_LOG_TAGS='*:w';;
+esac
+
 exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index 6a9bf61..2b57824 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -33,7 +33,7 @@
 14 (class java.lang.Short)
 [java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
 [private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
-[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)]
+[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), private static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(char[],int,int,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(char[],int,int,java.lang.String,int), void java.lang.String.getChars(char[],int)]
 []
 [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
 0
diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
index f01b825..d9ade93 100644
--- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
+++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
@@ -19,8 +19,8 @@
 
 #include "base/casts.h"
 #include "base/macros.h"
-#include "java_vm_ext.h"
-#include "jni_env_ext.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_env_ext.h"
 #include "thread-current-inl.h"
 
 namespace art {
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 49db0c8..a4d0d0c 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -111,8 +111,6 @@
     jint,
     jboolean) {
 #if __linux__
-  // TODO: What to do on Valgrind?
-
   std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
   if (!bt->Unwind(0, nullptr)) {
     printf("Cannot unwind in process.\n");
@@ -188,7 +186,6 @@
     jboolean,
     jint pid_int) {
 #if __linux__
-  // TODO: What to do on Valgrind?
   pid_t pid = static_cast<pid_t>(pid_int);
 
   // OK, this is painful. debuggerd uses ptrace to unwind other processes.
diff --git a/test/678-checker-simd-saturation/build b/test/172-app-image-twice/check
old mode 100644
new mode 100755
similarity index 69%
copy from test/678-checker-simd-saturation/build
copy to test/172-app-image-twice/check
index d85147f..26a97a4
--- a/test/678-checker-simd-saturation/build
+++ b/test/172-app-image-twice/check
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright 2018 The Android Open Source Project
+# Copyright (C) 2018 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
+#     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,
@@ -14,7 +14,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
+# Remove all lines not containing "passed".
+grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
diff --git a/test/172-app-image-twice/debug_print_class.cc b/test/172-app-image-twice/debug_print_class.cc
new file mode 100644
index 0000000..6c3de20
--- /dev/null
+++ b/test/172-app-image-twice/debug_print_class.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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 "debug_print.h"
+#include "dex/dex_file.h"
+#include "mirror/class-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_debugPrintClass(JNIEnv*, jclass, jclass cls) {
+  ScopedObjectAccess soa(Thread::Current());
+  ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
+  LOG(ERROR) << "klass: " << klass.Ptr() << " dex_file: " << klass->GetDexFile().GetLocation()
+      << "/" << static_cast<const void*>(&klass->GetDexFile())
+      << " " << DescribeSpace(klass);
+}
+
+}  // namespace art
diff --git a/test/678-checker-simd-saturation/expected.txt b/test/172-app-image-twice/expected.txt
similarity index 100%
rename from test/678-checker-simd-saturation/expected.txt
rename to test/172-app-image-twice/expected.txt
diff --git a/test/172-app-image-twice/info.txt b/test/172-app-image-twice/info.txt
new file mode 100644
index 0000000..028046e
--- /dev/null
+++ b/test/172-app-image-twice/info.txt
@@ -0,0 +1 @@
+Regression test for loading the same app image twice.
diff --git a/test/172-app-image-twice/profile b/test/172-app-image-twice/profile
new file mode 100644
index 0000000..70cb2ef
--- /dev/null
+++ b/test/172-app-image-twice/profile
@@ -0,0 +1 @@
+LTestClass;
diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run
new file mode 100644
index 0000000..aa28190
--- /dev/null
+++ b/test/172-app-image-twice/run
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+
+# Build an app image with TestClass (specified by profile) and class loader
+# context that skips the duplicate class checks.
+
+# Target and host use a different shell, and we need to special case the
+# passing of the class loader context marker.
+if [[ "$@" = *" --host "* ]]; then
+  ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
+      -Xcompiler-option --class-loader-context=\&
+else
+  ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
+      -Xcompiler-option '--class-loader-context=\&'
+fi
diff --git a/test/172-app-image-twice/src/Main.java b/test/172-app-image-twice/src/Main.java
new file mode 100644
index 0000000..a1c151a
--- /dev/null
+++ b/test/172-app-image-twice/src/Main.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 {
+    private static String TEST_NAME = "172-app-image-twice";
+
+    public static void main(String args[]) throws Exception {
+        System.loadLibrary(args[0]);
+
+        Class<?> tc1 = Class.forName("TestClass");
+
+        String dexPath = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar";
+        Class<?> bdcl = Class.forName("dalvik.system.BaseDexClassLoader");
+        Method addDexPathMethod = bdcl.getDeclaredMethod("addDexPath", String.class);
+        addDexPathMethod.invoke(Main.class.getClassLoader(), dexPath);
+
+        Class<?> tc2 = Class.forName("TestClass");
+
+        // Add extra logging to simulate libcore logging, this logging should not be compared
+        // against.
+        System.out.println("Extra logging");
+
+        if (tc1 != tc2) {
+            System.out.println("Class mismatch!");
+            debugPrintClass(tc1);
+            debugPrintClass(tc2);
+        } else {
+            System.out.println("passed");
+        }
+    }
+
+    public static native void debugPrintClass(Class<?> cls);
+}
diff --git a/test/651-checker-simd-minmax/src/Main.java b/test/172-app-image-twice/src/TestClass.java
similarity index 70%
copy from test/651-checker-simd-minmax/src/Main.java
copy to test/172-app-image-twice/src/TestClass.java
index 9134dd1..5381718 100644
--- a/test/651-checker-simd-minmax/src/Main.java
+++ b/test/172-app-image-twice/src/TestClass.java
@@ -14,14 +14,5 @@
  * limitations under the License.
  */
 
-public class Main {
-  public static void main(String[] args) {
-    ByteSimdMinMax.main();
-    CharSimdMinMax.main();
-    ShortSimdMinMax.main();
-    IntSimdMinMax.main();
-    LongSimdMinMax.main();
-    DoubleSimdMinMax.main();
-    FloatSimdMinMax.main();
-  }
+public class TestClass {
 }
diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java
index 70e94c4..97f0973 100644
--- a/test/1935-get-set-current-frame-jit/src/Main.java
+++ b/test/1935-get-set-current-frame-jit/src/Main.java
@@ -58,6 +58,10 @@
     }
     public void run() {
       int TARGET = 42;
+      if (hasJit() && expectOsr && !Main.isInterpreted()) {
+          System.out.println("Unexpectedly in jit code prior to restarting the JIT!");
+      }
+      startJit();
       // We will suspend the thread during this loop.
       while (continueBusyLoop) {
         inBusyLoop = true;
@@ -91,7 +95,9 @@
 
   public static void runGet() throws Exception {
     Method target = IntRunner.class.getDeclaredMethod("run");
-    // Get Int
+    // Stop jit temporarily. It will be restarted by the test itself.
+    stopJit();
+    // Get Int.
     IntRunner int_runner = new IntRunner(true);
     Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
     target_get.start();
@@ -121,7 +127,9 @@
 
   public static void runSet() throws Exception {
     Method target = IntRunner.class.getDeclaredMethod("run");
-    // Set Int
+    // Stop jit temporarily. It will be restarted by the test itself.
+    stopJit();
+    // Set Int. Even if we start out in JIT code somehow we should be pushed out of it.
     IntRunner int_runner = new IntRunner(false);
     Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
     target_set.start();
@@ -173,5 +181,7 @@
 
   public static native boolean isInterpreted();
   public static native boolean isInOsrCode(String methodName);
+  public static native boolean stopJit();
+  public static native boolean startJit();
   public static native boolean hasJit();
 }
diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt
index 1a457a0..5af1116 100644
--- a/test/1940-ddms-ext/expected.txt
+++ b/test/1940-ddms-ext/expected.txt
@@ -16,6 +16,10 @@
 Sending data [1] to chunk handler 305419896
 MyDdmHandler: Chunk received: Chunk(Type: 0x12345678, Len: 1, data: [1])
 Got error: JVMTI_ERROR_INTERNAL
+threadNotify started!
+Target thread started!
+Target thread finished!
+threadNotify Disabled!
 Saw expected thread events.
 Expected chunk type published: 1213221190
 Expected chunk type published: 1297109829
diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java
index 226fe35..2957f63 100644
--- a/test/1940-ddms-ext/src-art/art/Test1940.java
+++ b/test/1940-ddms-ext/src-art/art/Test1940.java
@@ -178,10 +178,14 @@
       }
     };
     DdmVmInternal.threadNotify(true);
+    System.out.println("threadNotify started!");
     final Thread thr = new Thread(() -> { return; }, "THREAD");
     thr.start();
+    System.out.println("Target thread started!");
     thr.join();
+    System.out.println("Target thread finished!");
     DdmVmInternal.threadNotify(false);
+    System.out.println("threadNotify Disabled!");
     // Make sure we saw at least one of Thread-create, Thread name, & thread death.
     if (!types_seen[0] || !types_seen[1] || !types_seen[2]) {
       System.out.println("Didn't see expected chunks for thread creation! got: " +
diff --git a/test/1947-breakpoint-redefine-deopt/check_deopt.cc b/test/1947-breakpoint-redefine-deopt/check_deopt.cc
index b40b201..667d8be 100644
--- a/test/1947-breakpoint-redefine-deopt/check_deopt.cc
+++ b/test/1947-breakpoint-redefine-deopt/check_deopt.cc
@@ -16,7 +16,7 @@
 
 #include "jni.h"
 #include "art_method-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "instrumentation.h"
 #include "scoped_thread_state_change-inl.h"
 
diff --git a/test/305-other-fault-handler/fault_handler.cc b/test/305-other-fault-handler/fault_handler.cc
index a0831ca..211d142 100644
--- a/test/305-other-fault-handler/fault_handler.cc
+++ b/test/305-other-fault-handler/fault_handler.cc
@@ -23,9 +23,9 @@
 #include <stdint.h>
 #include <sys/mman.h>
 
+#include "base/globals.h"
 #include "base/mem_map.h"
 #include "fault_handler.h"
-#include "globals.h"
 
 namespace art {
 
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index 95c19ea..fcc3c1a 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -991,15 +991,23 @@
   /// CHECK-START: int Main.StaticConditionNulls() constant_folding$after_inlining (before)
   /// CHECK-DAG:     <<Null:l\d+>>    NullConstant
   /// CHECK-DAG:     <<Cond:z\d+>>    NotEqual [<<Null>>,<<Null>>]
-  /// CHECK-DAG:                      Select [{{i\d+}},{{i\d+}},<<Cond>>]
+  /// CHECK-DAG:                      If [<<Cond>>]
 
   /// CHECK-START: int Main.StaticConditionNulls() constant_folding$after_inlining (after)
   /// CHECK-DAG:     <<Const0:i\d+>>  IntConstant 0
-  /// CHECK-DAG:                      Select [{{i\d+}},{{i\d+}},<<Const0>>]
+  /// CHECK-DAG:                      If [<<Const0>>]
 
   /// CHECK-START: int Main.StaticConditionNulls() constant_folding$after_inlining (after)
   /// CHECK-NOT:                      NotEqual
 
+  /// CHECK-START: int Main.StaticConditionNulls() dead_code_elimination$after_inlining (before)
+  /// CHECK-DAG:     <<Phi:i\d+>>     Phi
+  /// CHECK-DAG:                      Return [<<Phi>>]
+  //
+  /// CHECK-START: int Main.StaticConditionNulls() dead_code_elimination$after_inlining (after)
+  /// CHECK-DAG:     <<Const5:i\d+>>  IntConstant 5
+  /// CHECK-DAG:                      Return [<<Const5>>]
+
   private static Object getNull() {
     return null;
   }
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 444b455..b24cfcb 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -882,7 +882,7 @@
   /// CHECK-NOT:                       Neg
   /// CHECK-NOT:                       Add
 
-  /// CHECK-START: int Main.$noinline$NegNeg2(int) constant_folding$after_inlining (after)
+  /// CHECK-START: int Main.$noinline$NegNeg2(int) constant_folding$after_gvn (after)
   /// CHECK:         <<Const0:i\d+>>   IntConstant 0
   /// CHECK-NOT:                       Neg
   /// CHECK-NOT:                       Add
@@ -1128,17 +1128,19 @@
     return res;
   }
 
-  /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (before)
   /// CHECK-DAG:     <<Arg:z\d+>>      ParameterValue
   /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
   /// CHECK-DAG:     <<Const1:i\d+>>   IntConstant 1
   /// CHECK-DAG:     <<Const2:i\d+>>   IntConstant 2
-  /// CHECK-DAG:     <<NotArg:i\d+>>   Select [<<Const1>>,<<Const0>>,<<Arg>>]
-  /// CHECK-DAG:     <<Cond:z\d+>>     Equal [<<NotArg>>,<<Const2>>]
-  /// CHECK-DAG:     <<NotCond:i\d+>>  Select [<<Const1>>,<<Const0>>,<<Cond>>]
-  /// CHECK-DAG:                       Return [<<NotCond>>]
+  /// CHECK-DAG:                       If [<<Arg>>]
+  /// CHECK-DAG:     <<Phi1:i\d+>>     Phi [<<Const0>>,<<Const1>>]
+  /// CHECK-DAG:     <<Cond:z\d+>>     Equal [<<Phi1>>,<<Const2>>]
+  /// CHECK-DAG:                       If [<<Cond>>]
+  /// CHECK-DAG:     <<Phi2:i\d+>>     Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:                       Return [<<Phi2>>]
 
-  /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (after)
   /// CHECK-DAG:     <<True:i\d+>>     IntConstant 1
   /// CHECK-DAG:                       Return [<<True>>]
 
@@ -1157,12 +1159,14 @@
   /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
   /// CHECK-DAG:     <<Const1:i\d+>>   IntConstant 1
   /// CHECK-DAG:     <<Const2:i\d+>>   IntConstant 2
-  /// CHECK-DAG:     <<NotArg:i\d+>>   Select [<<Const1>>,<<Const0>>,<<Arg>>]
-  /// CHECK-DAG:     <<Cond:z\d+>>     NotEqual [<<NotArg>>,<<Const2>>]
-  /// CHECK-DAG:     <<NotCond:i\d+>>  Select [<<Const1>>,<<Const0>>,<<Cond>>]
-  /// CHECK-DAG:                       Return [<<NotCond>>]
+  /// CHECK-DAG:                       If [<<Arg>>]
+  /// CHECK-DAG:     <<Phi1:i\d+>>     Phi [<<Const0>>,<<Const1>>]
+  /// CHECK-DAG:     <<Cond:z\d+>>     NotEqual [<<Phi1>>,<<Const2>>]
+  /// CHECK-DAG:                       If [<<Cond>>]
+  /// CHECK-DAG:     <<Phi2:i\d+>>     Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:                       Return [<<Phi2>>]
 
-  /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (after)
   /// CHECK-DAG:     <<False:i\d+>>    IntConstant 0
   /// CHECK-DAG:                       Return [<<False>>]
 
@@ -1198,28 +1202,14 @@
 
   /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before)
   /// CHECK-DAG:     <<Arg:z\d+>>       ParameterValue
-  /// CHECK-NOT:                        BooleanNot [<<Arg>>]
-  /// CHECK-NOT:                        Phi
-
-  /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before)
-  /// CHECK-DAG:     <<Arg:z\d+>>       ParameterValue
   /// CHECK-DAG:     <<Const0:i\d+>>    IntConstant 0
   /// CHECK-DAG:     <<Const1:i\d+>>    IntConstant 1
-  /// CHECK-DAG:     <<Sel:i\d+>>       Select [<<Const1>>,<<Const0>>,<<Arg>>]
-  /// CHECK-DAG:     <<Sel2:i\d+>>      Select [<<Const1>>,<<Const0>>,<<Sel>>]
-  /// CHECK-DAG:                        Return [<<Sel2>>]
+  /// CHECK-DAG:                        If [<<Arg>>]
+  /// CHECK-DAG:     <<Phi:i\d+>>       Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:                        Return [<<Phi>>]
 
-  /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG:     <<Arg:z\d+>>       ParameterValue
-  /// CHECK:                            BooleanNot [<<Arg>>]
-  /// CHECK-NEXT:                       Goto
-
-  /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after)
-  /// CHECK-NOT:                        Select
-
-  /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) dead_code_elimination$final (after)
-  /// CHECK-DAG:     <<Arg:z\d+>>       ParameterValue
-  /// CHECK-NOT:                        BooleanNot [<<Arg>>]
   /// CHECK-DAG:                        Return [<<Arg>>]
 
   public static boolean NegateValue(boolean arg) {
@@ -1348,10 +1338,11 @@
   /// CHECK-DAG:      <<Const54:i\d+>>  IntConstant 54
   /// CHECK-DAG:      <<Field:z\d+>>    StaticFieldGet
   /// CHECK-DAG:      <<NE:z\d+>>       NotEqual [<<Field>>,<<Const1>>]
-  /// CHECK-DAG:      <<Select:i\d+>>   Select [<<Const13>>,<<Const54>>,<<NE>>]
-  /// CHECK-DAG:                        Return [<<Select>>]
+  /// CHECK-DAG:                        If [<<NE>>]
+  /// CHECK-DAG:      <<Phi:i\d+>>      Phi [<<Const13>>,<<Const54>>]
+  /// CHECK-DAG:                        Return [<<Phi>>]
 
-  /// CHECK-START: int Main.$noinline$booleanFieldNotEqualOne() instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.$noinline$booleanFieldNotEqualOne() select_generator (after)
   /// CHECK-DAG:      <<Field:z\d+>>    StaticFieldGet
   /// CHECK-DAG:      <<Const13:i\d+>>  IntConstant 13
   /// CHECK-DAG:      <<Const54:i\d+>>  IntConstant 54
@@ -1367,11 +1358,12 @@
   /// CHECK-DAG:      <<Const13:i\d+>>  IntConstant 13
   /// CHECK-DAG:      <<Const54:i\d+>>  IntConstant 54
   /// CHECK-DAG:      <<Field:z\d+>>    StaticFieldGet
-  /// CHECK-DAG:      <<NE:z\d+>>       Equal [<<Field>>,<<Const0>>]
-  /// CHECK-DAG:      <<Select:i\d+>>   Select [<<Const13>>,<<Const54>>,<<NE>>]
-  /// CHECK-DAG:                        Return [<<Select>>]
+  /// CHECK-DAG:      <<EQ:z\d+>>       Equal [<<Field>>,<<Const0>>]
+  /// CHECK-DAG:                        If [<<EQ>>]
+  /// CHECK-DAG:      <<Phi:i\d+>>      Phi [<<Const13>>,<<Const54>>]
+  /// CHECK-DAG:                        Return [<<Phi>>]
 
-  /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() select_generator (after)
   /// CHECK-DAG:      <<Field:z\d+>>    StaticFieldGet
   /// CHECK-DAG:      <<Const13:i\d+>>  IntConstant 13
   /// CHECK-DAG:      <<Const54:i\d+>>  IntConstant 54
@@ -1390,18 +1382,20 @@
   /// CHECK-DAG:      <<Const42:i\d+>>  IntConstant 42
   /// CHECK-DAG:      <<Const54:i\d+>>  IntConstant 54
   /// CHECK-DAG:      <<LE:z\d+>>       LessThanOrEqual [<<Arg>>,<<Const42>>]
-  /// CHECK-DAG:      <<GT:i\d+>>       Select [<<Const1>>,<<Const0>>,<<LE>>]
-  /// CHECK-DAG:      <<NE:z\d+>>       NotEqual [<<GT>>,<<Const1>>]
-  /// CHECK-DAG:      <<Result:i\d+>>   Select [<<Const13>>,<<Const54>>,<<NE>>]
-  /// CHECK-DAG:                        Return [<<Result>>]
+  /// CHECK-DAG:                        If [<<LE>>]
+  /// CHECK-DAG:      <<Phi1:i\d+>>     Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:      <<NE:z\d+>>       NotEqual [<<Phi1>>,<<Const1>>]
+  /// CHECK-DAG:                        If [<<NE>>]
+  /// CHECK-DAG:      <<Phi2:i\d+>>     Phi [<<Const13>>,<<Const54>>]
+  /// CHECK-DAG:                        Return [<<Phi2>>]
 
-  /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) select_generator (after)
   /// CHECK-DAG:      <<Arg:i\d+>>      ParameterValue
   /// CHECK-DAG:      <<Const13:i\d+>>  IntConstant 13
   /// CHECK-DAG:      <<Const42:i\d+>>  IntConstant 42
   /// CHECK-DAG:      <<Const54:i\d+>>  IntConstant 54
-  /// CHECK-DAG:      <<Result:i\d+>>   Select [<<Const13>>,<<Const54>>,<<LE:z\d+>>]
-  /// CHECK-DAG:      <<LE>>            LessThanOrEqual [<<Arg>>,<<Const42>>]
+  /// CHECK-DAG:      <<LE:z\d+>>       LessThanOrEqual [<<Arg>>,<<Const42>>]
+  /// CHECK-DAG:      <<Result:i\d+>>   Select [<<Const13>>,<<Const54>>,<<LE>>]
   /// CHECK-DAG:                        Return [<<Result>>]
   // Note that we match `LE` from Select because there are two identical
   // LessThanOrEqual instructions.
@@ -1418,18 +1412,20 @@
   /// CHECK-DAG:      <<Const42:i\d+>>  IntConstant 42
   /// CHECK-DAG:      <<Const54:i\d+>>  IntConstant 54
   /// CHECK-DAG:      <<LE:z\d+>>       LessThanOrEqual [<<Arg>>,<<Const42>>]
-  /// CHECK-DAG:      <<GT:i\d+>>       Select [<<Const1>>,<<Const0>>,<<LE>>]
-  /// CHECK-DAG:      <<NE:z\d+>>       Equal [<<GT>>,<<Const0>>]
-  /// CHECK-DAG:      <<Result:i\d+>>   Select [<<Const13>>,<<Const54>>,<<NE>>]
-  /// CHECK-DAG:                        Return [<<Result>>]
+  /// CHECK-DAG:                        If [<<LE>>]
+  /// CHECK-DAG:      <<Phi1:i\d+>>     Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:      <<EQ:z\d+>>       Equal [<<Phi1>>,<<Const0>>]
+  /// CHECK-DAG:                        If [<<EQ>>]
+  /// CHECK-DAG:      <<Phi2:i\d+>>     Phi [<<Const13>>,<<Const54>>]
+  /// CHECK-DAG:                        Return [<<Phi2>>]
 
-  /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) select_generator (after)
   /// CHECK-DAG:      <<Arg:i\d+>>      ParameterValue
   /// CHECK-DAG:      <<Const13:i\d+>>  IntConstant 13
   /// CHECK-DAG:      <<Const42:i\d+>>  IntConstant 42
   /// CHECK-DAG:      <<Const54:i\d+>>  IntConstant 54
-  /// CHECK-DAG:      <<Result:i\d+>>   Select [<<Const13>>,<<Const54>>,<<LE:z\d+>>]
-  /// CHECK-DAG:      <<LE>>            LessThanOrEqual [<<Arg>>,<<Const42>>]
+  /// CHECK-DAG:      <<LE:z\d+>>       LessThanOrEqual [<<Arg>>,<<Const42>>]
+  /// CHECK-DAG:      <<Result:i\d+>>   Select [<<Const13>>,<<Const54>>,<<LE>>]
   /// CHECK-DAG:                        Return [<<Result>>]
   // Note that we match `LE` from Select because there are two identical
   // LessThanOrEqual instructions.
@@ -2571,12 +2567,13 @@
   /// CHECK-DAG:      <<Const0:i\d+>>   IntConstant 0
   /// CHECK-DAG:      <<Const1:i\d+>>   IntConstant 1
   /// CHECK-DAG:      <<Const255:i\d+>> IntConstant 255
-  /// CHECK-DAG:      <<Select:i\d+>>   Select [<<Const0>>,<<Const1>>,<<Arg>>]
-  /// CHECK-DAG:      <<And:i\d+>>      And [<<Select>>,<<Const255>>]
+  /// CHECK-DAG:                        If [<<Arg>>]
+  /// CHECK-DAG:      <<Phi:i\d+>>      Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:      <<And:i\d+>>      And [<<Const255>>,<<Phi>>]
   /// CHECK-DAG:      <<Conv:b\d+>>     TypeConversion [<<And>>]
   /// CHECK-DAG:                        Return [<<Conv>>]
 
-  /// CHECK-START: int Main.$noinline$bug68142795Boolean(boolean) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.$noinline$bug68142795Boolean(boolean) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG:      <<Arg:z\d+>>      ParameterValue
   /// CHECK-DAG:                        Return [<<Arg>>]
   public static int $noinline$bug68142795Boolean(boolean b) {
diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali
index cda6f73..5290bad 100644
--- a/test/485-checker-dce-loop-update/smali/TestCase.smali
+++ b/test/485-checker-dce-loop-update/smali/TestCase.smali
@@ -140,11 +140,11 @@
 ## CHECK-DAG:     <<PhiX:i\d+>>  Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
 ## CHECK-DAG:                    If [<<ArgY>>]                              loop:<<HeaderY>>
 ## CHECK-DAG:     <<Mul9:i\d+>>  Mul [<<PhiX>>,<<Cst11>>]                   loop:<<HeaderY>>
-## CHECK-DAG:     <<SelX:i\d+>>  Select [<<PhiX>>,<<Mul9>>,<<ArgZ>>]        loop:<<HeaderY>>
+## CHECK-DAG:     <<PhiY:i\d+>>  Phi [<<PhiX>>,<<Mul9>>]                    loop:<<HeaderY>>
 ## CHECK-DAG:                    If [<<Cst1>>]                              loop:<<HeaderY>>
-## CHECK-DAG:     <<Add5>>       Add [<<SelX>>,<<Cst5>>]                    loop:<<HeaderY>>
+## CHECK-DAG:     <<Add5>>       Add [<<PhiY>>,<<Cst5>>]                    loop:<<HeaderY>>
 ## CHECK-DAG:     <<Add7>>       Add [<<PhiX>>,<<Cst7>>]                    loop:<<HeaderY>>
-## CHECK-DAG:                    Return [<<SelX>>]                          loop:none
+## CHECK-DAG:                    Return [<<PhiY>>]                          loop:none
 
 ## CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination$after_inlining (after)
 ## CHECK-DAG:     <<ArgX:i\d+>>  ParameterValue
@@ -156,6 +156,22 @@
 ## CHECK-DAG:                    If [<<ArgY>>]                              loop:<<HeaderY>>
 ## CHECK-DAG:     <<Add7>>       Add [<<PhiX>>,<<Cst7>>]                    loop:<<HeaderY>>
 ## CHECK-DAG:     <<Mul9:i\d+>>  Mul [<<PhiX>>,<<Cst11>>]                   loop:none
+## CHECK-DAG:     <<Phi:i\d+>>   Phi [<<PhiX>>,<<Mul9>>]                    loop:none
+## CHECK-DAG:                    Return [<<Phi>>]                           loop:none
+
+## CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination$after_inlining (after)
+## CHECK-NOT:                    IntConstant 5
+
+## CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) select_generator (after)
+## CHECK-DAG:     <<ArgX:i\d+>>  ParameterValue
+## CHECK-DAG:     <<ArgY:z\d+>>  ParameterValue
+## CHECK-DAG:     <<ArgZ:z\d+>>  ParameterValue
+## CHECK-DAG:     <<Cst7:i\d+>>  IntConstant 7
+## CHECK-DAG:     <<Cst11:i\d+>> IntConstant 11
+## CHECK-DAG:     <<PhiX:i\d+>>  Phi [<<ArgX>>,<<Add7:i\d+>>]               loop:<<HeaderY:B\d+>>
+## CHECK-DAG:                    If [<<ArgY>>]                              loop:<<HeaderY>>
+## CHECK-DAG:     <<Add7>>       Add [<<PhiX>>,<<Cst7>>]                    loop:<<HeaderY>>
+## CHECK-DAG:     <<Mul9:i\d+>>  Mul [<<PhiX>>,<<Cst11>>]                   loop:none
 ## CHECK-DAG:     <<SelX:i\d+>>  Select [<<PhiX>>,<<Mul9>>,<<ArgZ>>]        loop:none
 ## CHECK-DAG:                    Return [<<SelX>>]                          loop:none
 
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index ebde3bf..93c1538 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -1137,6 +1137,126 @@
 
   static Object[] sArray;
 
+  /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (before)
+  /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+  /// CHECK-DAG: <<A:l\d+>>      NewArray
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const0>>]
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const1>>]
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const1>>]
+  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [<<A>>,<<Const0>>]
+  /// CHECK-DAG:                 Return [<<Get>>]
+  //
+  /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (after)
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+  /// CHECK-DAG:                 Return [<<Const1>>]
+  //
+  /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (after)
+  /// CHECK-NOT:                 NewArray
+  /// CHECK-NOT:                 ArraySet
+  /// CHECK-NOT:                 ArrayGet
+  private static int testLocalArrayMerge1(boolean x) {
+    // The explicit store can be removed right away
+    // since it is equivalent to the default.
+    int[] a = { 0 };
+    // The diamond pattern stores/load can be replaced
+    // by the direct value.
+    if (x) {
+      a[0] = 1;
+    } else {
+      a[0] = 1;
+    }
+    return a[0];
+  }
+
+  /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (before)
+  /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+  /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+  /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+  /// CHECK-DAG: <<A:l\d+>>      NewArray
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const1>>]
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const2>>]
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const3>>]
+  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [<<A>>,<<Const0>>]
+  /// CHECK-DAG:                 Return [<<Get>>]
+  //
+  /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (after)
+  /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+  /// CHECK-DAG: <<A:l\d+>>      NewArray
+  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [<<A>>,<<Const0>>]
+  /// CHECK-DAG:                 Return [<<Get>>]
+  //
+  /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (after)
+  /// CHECK-DAG:                 ArraySet
+  /// CHECK-DAG:                 ArraySet
+  /// CHECK-NOT:                 ArraySet
+  private static int testLocalArrayMerge2(boolean x) {
+    // The explicit store can be removed eventually even
+    // though it is not equivalent to the default.
+    int[] a = { 1 };
+    // The diamond pattern stores/load remain.
+    if (x) {
+      a[0] = 2;
+    } else {
+      a[0] = 3;
+    }
+    return a[0];
+  }
+
+  /// CHECK-START: int Main.testLocalArrayMerge3(boolean) load_store_elimination (after)
+  /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+  /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+  /// CHECK-DAG: <<A:l\d+>>      NewArray
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const1>>]
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const2>>]
+  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [<<A>>,<<Const0>>]
+  /// CHECK-DAG:                 Return [<<Get>>]
+  private static int testLocalArrayMerge3(boolean x) {
+    // All stores/load remain.
+    int[] a = { 1 };
+    if (x) {
+      a[0] = 2;
+    }
+    return a[0];
+  }
+
+  /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (before)
+  /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+  /// CHECK-DAG: <<A:l\d+>>      NewArray
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const0>>]
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const1>>]
+  /// CHECK-DAG:                 ArraySet [<<A>>,<<Const0>>,<<Const1>>]
+  /// CHECK-DAG: <<Get1:b\d+>>   ArrayGet [<<A>>,<<Const0>>]
+  /// CHECK-DAG: <<Get2:a\d+>>   ArrayGet [<<A>>,<<Const0>>]
+  /// CHECK-DAG: <<Add:i\d+>>    Add [<<Get1>>,<<Get2>>]
+  /// CHECK-DAG:                 Return [<<Add>>]
+  //
+  /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (after)
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+  /// CHECK-DAG: <<Cnv1:b\d+>>   TypeConversion [<<Const1>>]
+  /// CHECK-DAG: <<Cnv2:a\d+>>   TypeConversion [<<Const1>>]
+  /// CHECK-DAG: <<Add:i\d+>>    Add [<<Cnv1>>,<<Cnv2>>]
+  /// CHECK-DAG:                 Return [<<Add>>]
+  //
+  /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (after)
+  /// CHECK-NOT:                 NewArray
+  /// CHECK-NOT:                 ArraySet
+  /// CHECK-NOT:                 ArrayGet
+  private static int testLocalArrayMerge4(boolean x) {
+    byte[] a = { 0 };
+    if (x) {
+      a[0] = 1;
+    } else {
+      a[0] = 1;
+    }
+    // Differently typed (signed vs unsigned),
+    // but same reference.
+    return a[0] + (a[0] & 0xff);
+  }
+
   static void assertIntEquals(int result, int expected) {
     if (expected != result) {
       throw new Error("Expected: " + expected + ", found: " + result);
@@ -1271,6 +1391,15 @@
     assertIntEquals(testclass2.i, 55);
 
     assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4);
+
+    assertIntEquals(testLocalArrayMerge1(true), 1);
+    assertIntEquals(testLocalArrayMerge1(false), 1);
+    assertIntEquals(testLocalArrayMerge2(true), 2);
+    assertIntEquals(testLocalArrayMerge2(false), 3);
+    assertIntEquals(testLocalArrayMerge3(true), 2);
+    assertIntEquals(testLocalArrayMerge3(false), 1);
+    assertIntEquals(testLocalArrayMerge4(true), 2);
+    assertIntEquals(testLocalArrayMerge4(false), 2);
   }
 
   static boolean sFlag;
diff --git a/test/651-checker-simd-minmax/build b/test/530-checker-lse2/build
similarity index 100%
rename from test/651-checker-simd-minmax/build
rename to test/530-checker-lse2/build
diff --git a/test/543-checker-dce-trycatch/smali/TestCase.smali b/test/543-checker-dce-trycatch/smali/TestCase.smali
index f50e01e..7ad9ba8 100644
--- a/test/543-checker-dce-trycatch/smali/TestCase.smali
+++ b/test/543-checker-dce-trycatch/smali/TestCase.smali
@@ -215,10 +215,10 @@
 ## CHECK-DAG:     <<Const0x10:i\d+>> IntConstant 16
 ## CHECK-DAG:     <<Const0x11:i\d+>> IntConstant 17
 ## CHECK-DAG:     <<Add:i\d+>>       Add [<<Arg0>>,<<Arg1>>]
-## CHECK-DAG:     <<Select:i\d+>>    Select [<<Const0xf>>,<<Add>>,{{z\d+}}]
+## CHECK-DAG:     <<Phi:i\d+>>       Phi [<<Add>>,<<Const0xf>>] reg:3 is_catch_phi:false
 ## CHECK-DAG:                        Phi [<<Const0xa>>,<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
 ## CHECK-DAG:                        Phi [<<Add>>,<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
-## CHECK-DAG:                        Phi [<<Select>>,<<Const0x10>>,<<Const0x11>>] reg:3 is_catch_phi:true
+## CHECK-DAG:                        Phi [<<Phi>>,<<Const0x10>>,<<Const0x11>>] reg:3 is_catch_phi:true
 
 ## CHECK-START: int TestCase.testCatchPhiInputs_DefinedInTryBlock(int, int, int, int) dead_code_elimination$after_inlining (after)
 ## CHECK-DAG:     <<Const0xb:i\d+>>  IntConstant 11
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index fb76904..b3e4a60 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -728,9 +728,41 @@
   /// CHECK:                            UShr
   /// CHECK-NOT:                        UShr
   //
-  // Note: running extra simplification before GVN would expose the common subexpressions between
-  // shifts with larger distance `b << 62`, `b << 63` etc. and the equivalent smaller distances.
-  // TODO: b/78171933
+  // Note: running extra simplification after inlining and before GVN exposes the common
+  // subexpressions between shifts with larger distance `b << 62`, `b << 63` etc.
+  // and the equivalent smaller distances.
+  //
+  /// CHECK-START: void Main.$opt$validateShiftInt(int, int) GVN (after)
+  /// CHECK:                            Shl
+  /// CHECK:                            Shl
+  /// CHECK:                            Shl
+  /// CHECK:                            Shl
+  /// CHECK:                            Shl
+  /// CHECK:                            Shl
+  /// CHECK:                            Shl
+  /// CHECK:                            Shl
+  /// CHECK:                            Shl
+  /// CHECK-NOT:                        Shl
+  /// CHECK:                            Shr
+  /// CHECK:                            Shr
+  /// CHECK:                            Shr
+  /// CHECK:                            Shr
+  /// CHECK:                            Shr
+  /// CHECK:                            Shr
+  /// CHECK:                            Shr
+  /// CHECK:                            Shr
+  /// CHECK:                            Shr
+  /// CHECK-NOT:                        Shl
+  /// CHECK:                            UShr
+  /// CHECK:                            UShr
+  /// CHECK:                            UShr
+  /// CHECK:                            UShr
+  /// CHECK:                            UShr
+  /// CHECK:                            UShr
+  /// CHECK:                            UShr
+  /// CHECK:                            UShr
+  /// CHECK:                            UShr
+  /// CHECK-NOT:                        UShr
   //
   /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after)
   /// CHECK:                            DataProcWithShifterOp
@@ -760,12 +792,6 @@
   /// CHECK:                            DataProcWithShifterOp
   /// CHECK:                            DataProcWithShifterOp
   /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
   /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after)
@@ -801,12 +827,6 @@
   /// CHECK:                            DataProcWithShifterOp
   /// CHECK:                            DataProcWithShifterOp
   /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
-  /// CHECK:                            DataProcWithShifterOp
   /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after)
diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali
index adafb78..8898c48 100644
--- a/test/563-checker-fakestring/smali/TestCase.smali
+++ b/test/563-checker-fakestring/smali/TestCase.smali
@@ -133,17 +133,8 @@
 
 .end method
 
-# Test that the compiler does not assume that the first argument of String.<init>
-# is a NewInstance by inserting an irreducible loop between them (b/26676472).
-
-# We verify the type of the input instruction (Phi) in debuggable mode, because
-# it is eliminated by later stages of SsaBuilder otherwise.
-
-## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance1(byte[], boolean) register (after)
-## CHECK-DAG:                   InvokeStaticOrDirect env:[[<<Phi:l\d+>>,{{.*]]}}
-## CHECK-DAG:     <<Phi>>       Phi
-
-.method public static thisNotNewInstance1([BZ)Ljava/lang/String;
+# Test #1 for irreducible loops and String.<init>.
+.method public static irreducibleLoopAndStringInit1([BZ)Ljava/lang/String;
    .registers 5
 
    new-instance v0, Ljava/lang/String;
@@ -164,11 +155,8 @@
 
 .end method
 
-## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance2(byte[], boolean) register (after)
-## CHECK-DAG:                   InvokeStaticOrDirect env:[[<<Phi:l\d+>>,{{.*]]}}
-## CHECK-DAG:     <<Phi>>       Phi
-
-.method public static thisNotNewInstance2([BZ)Ljava/lang/String;
+# Test #2 for irreducible loops and String.<init>.
+.method public static irreducibleLoopAndStringInit2([BZ)Ljava/lang/String;
    .registers 5
 
    new-instance v0, Ljava/lang/String;
@@ -188,3 +176,26 @@
    return-object v0
 
 .end method
+
+# Test #3 for irreducible loops and String.<init> alias.
+.method public static irreducibleLoopAndStringInit3([BZ)Ljava/lang/String;
+   .registers 5
+
+   new-instance v0, Ljava/lang/String;
+   move-object v2, v0
+
+   # Irreducible loop
+   if-eqz p1, :loop_entry
+   :loop_header
+   const v1, 0x1
+   xor-int p1, p1, v1
+   :loop_entry
+   if-eqz p1, :string_init
+   goto :loop_header
+
+   :string_init
+   const-string v1, "UTF8"
+   invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V
+   return-object v2
+
+.end method
diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java
index 78cb37a..d38b7f4 100644
--- a/test/563-checker-fakestring/src/Main.java
+++ b/test/563-checker-fakestring/src/Main.java
@@ -65,14 +65,21 @@
     }
 
     {
-      Method m = c.getMethod("thisNotNewInstance1", byte[].class, boolean.class);
+      Method m = c.getMethod("irreducibleLoopAndStringInit1", byte[].class, boolean.class);
       String result = (String) m.invoke(null, new Object[] { testData, true });
       assertEqual(testString, result);
       result = (String) m.invoke(null, new Object[] { testData, false });
       assertEqual(testString, result);
     }
     {
-      Method m = c.getMethod("thisNotNewInstance2", byte[].class, boolean.class);
+      Method m = c.getMethod("irreducibleLoopAndStringInit2", byte[].class, boolean.class);
+      String result = (String) m.invoke(null, new Object[] { testData, true });
+      assertEqual(testString, result);
+      result = (String) m.invoke(null, new Object[] { testData, false });
+      assertEqual(testString, result);
+    }
+    {
+      Method m = c.getMethod("irreducibleLoopAndStringInit3", byte[].class, boolean.class);
       String result = (String) m.invoke(null, new Object[] { testData, true });
       assertEqual(testString, result);
       result = (String) m.invoke(null, new Object[] { testData, false });
diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java
index 80358cd..e36a2ba 100644
--- a/test/565-checker-doublenegbitwise/src/Main.java
+++ b/test/565-checker-doublenegbitwise/src/Main.java
@@ -94,7 +94,7 @@
    * same pass.
    */
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG:       <<P1:z\d+>>          ParameterValue
   /// CHECK-DAG:       <<P2:z\d+>>          ParameterValue
   /// CHECK-DAG:       <<Const0:i\d+>>      IntConstant 0
@@ -104,7 +104,7 @@
   /// CHECK-DAG:       <<And:i\d+>>         And [<<Select1>>,<<Select2>>]
   /// CHECK-DAG:                            Return [<<And>>]
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG:       <<Cond1:z\d+>>       ParameterValue
   /// CHECK-DAG:       <<Cond2:z\d+>>       ParameterValue
   /// CHECK-DAG:       <<Or:i\d+>>          Or [<<Cond1>>,<<Cond2>>]
@@ -165,7 +165,7 @@
    * same pass.
    */
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG:       <<P1:z\d+>>          ParameterValue
   /// CHECK-DAG:       <<P2:z\d+>>          ParameterValue
   /// CHECK-DAG:       <<Const0:i\d+>>      IntConstant 0
@@ -175,7 +175,7 @@
   /// CHECK-DAG:       <<Or:i\d+>>          Or [<<Select1>>,<<Select2>>]
   /// CHECK-DAG:                            Return [<<Or>>]
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG:       <<Cond1:z\d+>>       ParameterValue
   /// CHECK-DAG:       <<Cond2:z\d+>>       ParameterValue
   /// CHECK-DAG:       <<And:i\d+>>         And [<<Cond1>>,<<Cond2>>]
@@ -275,7 +275,7 @@
    * same pass.
    */
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG:       <<P1:z\d+>>          ParameterValue
   /// CHECK-DAG:       <<P2:z\d+>>          ParameterValue
   /// CHECK-DAG:       <<Const0:i\d+>>      IntConstant 0
@@ -285,7 +285,7 @@
   /// CHECK-DAG:       <<Xor:i\d+>>         Xor [<<Select1>>,<<Select2>>]
   /// CHECK-DAG:                            Return [<<Xor>>]
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG:       <<Cond1:z\d+>>       ParameterValue
   /// CHECK-DAG:       <<Cond2:z\d+>>       ParameterValue
   /// CHECK-DAG:       <<Xor:i\d+>>         Xor [<<Cond1>>,<<Cond2>>]
diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc
index e2b8aa0..7c1507f 100644
--- a/test/566-polymorphic-inlining/polymorphic_inline.cc
+++ b/test/566-polymorphic-inlining/polymorphic_inline.cc
@@ -48,9 +48,8 @@
     }
   }
 
-  CodeInfo info = header->GetOptimizedCodeInfo();
-  CodeInfoEncoding encoding = info.ExtractEncoding();
-  CHECK(info.HasInlineInfo(encoding));
+  CodeInfo info(header);
+  CHECK(info.HasInlineInfo());
 }
 
 static void allocate_profiling_info(jclass cls, const char* method_name) {
diff --git a/test/651-checker-simd-minmax/build b/test/569-checker-pattern-replacement/build
similarity index 100%
copy from test/651-checker-simd-minmax/build
copy to test/569-checker-pattern-replacement/build
diff --git a/test/651-checker-simd-minmax/build b/test/583-checker-zero/build
similarity index 100%
copy from test/651-checker-simd-minmax/build
copy to test/583-checker-zero/build
diff --git a/test/586-checker-null-array-get/src/Main.java b/test/586-checker-null-array-get/src/Main.java
index de9429f..ebe91cf 100644
--- a/test/586-checker-null-array-get/src/Main.java
+++ b/test/586-checker-null-array-get/src/Main.java
@@ -107,9 +107,8 @@
   /// CHECK-DAG: <<GetJ3:j\d+>>      ArrayGet [<<CheckJ>>,{{i\d+}}]
   public static void bar() {
     // We create multiple accesses that will lead the bounds check
-    // elimination pass to add a HDeoptimize. Not having the bounds check helped
-    // the load store elimination think it could merge two ArrayGet with different
-    // types.
+    // elimination pass to add a HDeoptimize. Not having the bounds check
+    // makes the ArrayGets look almost the same if it were not for the type!
     String[] array = (String[])getNull();
     objectField = array[0];
     objectField = array[1];
diff --git a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
index 494ab95..f74e88f 100644
--- a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
+++ b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
@@ -233,9 +233,7 @@
 ## CHECK-DAG:     <<One:i\d+>>           IntConstant 1
 ## CHECK-DAG:     <<Sget:z\d+>>          StaticFieldGet
 ## CHECK-DAG:     <<Sel:i\d+>>           Select [<<Zero>>,<<One>>,<<Sget>>]
-## CHECK-DAG:     <<IToJ:j\d+>>          TypeConversion [<<Sel>>]
-## CHECK-DAG:     <<JToI:i\d+>>          TypeConversion [<<IToJ>>]
-## CHECK-DAG:                            Return [<<JToI>>]
+## CHECK-DAG:                            Return [<<Sel>>]
 
 ## CHECK-START: int SmaliTests.longToIntOfBoolean() instruction_simplifier$after_bce (after)
 ## CHECK-DAG:     <<Method:[ij]\d+>>     CurrentMethod
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index bb9ab84..b22d61e 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -18,7 +18,6 @@
 
 #include "art_method-inl.h"
 #include "dex/method_reference.h"
-#include "jit/profile_compilation_info.h"
 #include "jit/profile_saver.h"
 #include "jni.h"
 #include "mirror/class-inl.h"
@@ -26,6 +25,7 @@
 #include "nativehelper/ScopedUtfChars.h"
 #include "oat_file_assistant.h"
 #include "oat_file_manager.h"
+#include "profile/profile_compilation_info.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 
diff --git a/test/631-checker-get-class/src/Main.java b/test/631-checker-get-class/src/Main.java
index 61c0adf..b318168 100644
--- a/test/631-checker-get-class/src/Main.java
+++ b/test/631-checker-get-class/src/Main.java
@@ -34,11 +34,14 @@
   }
 
   /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
   /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
-  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
-  /// CHECK-DAG:                 Return [<<Select>>]
+  /// CHECK-DAG:                 If [<<Eq>>]
+  /// CHECK-DAG: <<Phi:i\d+>>    Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:                 Return [<<Phi>>]
 
-  /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.classEquality1() dead_code_elimination$after_inlining (after)
   /// CHECK-DAG: <<Constant:i\d+>> IntConstant 1
   /// CHECK-DAG:                   Return [<<Constant>>]
   public static boolean classEquality1() {
@@ -46,11 +49,14 @@
   }
 
   /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
   /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
-  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
-  /// CHECK-DAG:                 Return [<<Select>>]
+  /// CHECK-DAG:                 If [<<Eq>>]
+  /// CHECK-DAG: <<Phi:i\d+>>    Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:                 Return [<<Phi>>]
 
-  /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.classEquality2() dead_code_elimination$after_inlining (after)
   /// CHECK-DAG: <<Constant:i\d+>> IntConstant 0
   /// CHECK-DAG:                   Return [<<Constant>>]
   public static boolean classEquality2() {
@@ -59,11 +65,14 @@
   }
 
   /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
   /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
-  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
-  /// CHECK-DAG:                 Return [<<Select>>]
+  /// CHECK-DAG:                 If [<<Eq>>]
+  /// CHECK-DAG: <<Phi:i\d+>>    Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:                 Return [<<Phi>>]
 
-  /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.classEquality3() dead_code_elimination$after_inlining (after)
   /// CHECK-DAG: <<Constant:i\d+>> IntConstant 0
   /// CHECK-DAG:                   Return [<<Constant>>]
   public static boolean classEquality3() {
@@ -71,11 +80,14 @@
   }
 
   /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+  /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
   /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
-  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
-  /// CHECK-DAG:                 Return [<<Select>>]
+  /// CHECK-DAG:                 If [<<Eq>>]
+  /// CHECK-DAG: <<Phi:i\d+>>    Phi [<<Const1>>,<<Const0>>]
+  /// CHECK-DAG:                 Return [<<Phi>>]
 
-  /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (after)
+  /// CHECK-START: boolean Main.classEquality4() dead_code_elimination$after_inlining (after)
   /// CHECK-DAG: <<Constant:i\d+>> IntConstant 1
   /// CHECK-DAG:                   Return [<<Constant>>]
   public static boolean classEquality4() {
diff --git a/test/651-checker-simd-minmax/expected.txt b/test/651-checker-simd-minmax/expected.txt
deleted file mode 100644
index f362c45..0000000
--- a/test/651-checker-simd-minmax/expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-ByteSimdMinMax passed
-CharSimdMinMax passed
-ShortSimdMinMax passed
-IntSimdMinMax passed
-LongSimdMinMax passed
-DoubleSimdMinMax passed
-FloatSimdMinMax passed
diff --git a/test/651-checker-simd-minmax/info.txt b/test/651-checker-simd-minmax/info.txt
deleted file mode 100644
index 73af124..0000000
--- a/test/651-checker-simd-minmax/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on min/max SIMD vectorization.
diff --git a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
deleted file mode 100644
index fff15fa..0000000
--- a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Tests for MIN/MAX vectorization.
- */
-public class ByteSimdMinMax {
-
-  /// CHECK-START: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMin(byte[] x, byte[] y, byte[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (byte) Math.min(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) instruction_simplifier (before)
-  /// CHECK-DAG: <<I255:i\d+>> IntConstant 255                     loop:none
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<I255>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<I255>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<And1>>,<<And2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMinUnsigned(byte[] x, byte[] y, byte[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (byte) Math.min(y[i] & 0xff, z[i] & 0xff);
-    }
-  }
-
-  /// CHECK-START: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMax(byte[] x, byte[] y, byte[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (byte) Math.max(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) instruction_simplifier (before)
-  /// CHECK-DAG: <<I255:i\d+>> IntConstant 255                     loop:none
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<I255>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<I255>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  InvokeStaticOrDirect [<<And1>>,<<And2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMaxUnsigned(byte[] x, byte[] y, byte[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (byte) Math.max(y[i] & 0xff, z[i] & 0xff);
-    }
-  }
-
-  /// CHECK-START: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (before)
-  /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                     loop:none
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get:b\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get>>,<<I100>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none
-  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none
-  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Repl>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>       outer_loop:none
-  private static void doitMin100(byte[] x, byte[] y) {
-    int min = Math.min(x.length, y.length);
-    for (int i = 0; i < min; i++) {
-      x[i] = (byte) Math.min(y[i], 100);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMax(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<I11:i\d+>>  IntConstant -11                      loop:none
-  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 23                       loop:none
-  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
-  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
-  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
-  private static void doitMinMax(byte[] x, byte[] y) {
-    int n = Math.min(x.length, y.length);
-    for (int i = 0; i < n; i++) {
-      x[i] = (byte) Math.max(-11, Math.min(y[i], 23));
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMaxUnsigned(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<I11:i\d+>>  IntConstant 11                       loop:none
-  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 23                       loop:none
-  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
-  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
-  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
-  private static void doitMinMaxUnsigned(byte[] x, byte[] y) {
-    int n = Math.min(x.length, y.length);
-    for (int i = 0; i < n; i++) {
-      x[i] = (byte) Math.max(11, Math.min(y[i] & 0xff, 23));
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void ByteSimdMinMax.doitMinAlt(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMinAlt(byte[] x, byte[] y, byte[] z) {
-    int n = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < n; ++i) {
-      x[i] = y[i] < z[i] ? y[i] : z[i];
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMaxAlt(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMaxAlt(byte[] x, byte[] y, byte[] z) {
-    int n = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < n; ++i) {
-      x[i] = y[i] > z[i] ? y[i] : z[i];
-    }
-  }
-
-  public static void main() {
-    // Initialize cross-values for all possible values.
-    int total = 256 * 256;
-    byte[] x = new byte[total];
-    byte[] y = new byte[total];
-    byte[] z = new byte[total];
-    int k = 0;
-    for (int i = 0; i < 256; i++) {
-      for (int j = 0; j < 256; j++) {
-        x[k] = 0;
-        y[k] = (byte) i;
-        z[k] = (byte) j;
-        k++;
-      }
-    }
-
-    // And test.
-    doitMin(x, y, z);
-    for (int i = 0; i < total; i++) {
-      byte expected = (byte) Math.min(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMinUnsigned(x, y, z);
-    for (int i = 0; i < total; i++) {
-      byte expected = (byte) Math.min(y[i] & 0xff, z[i] & 0xff);
-      expectEquals(expected, x[i]);
-    }
-    doitMax(x, y, z);
-    for (int i = 0; i < total; i++) {
-      byte expected = (byte) Math.max(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMaxUnsigned(x, y, z);
-    for (int i = 0; i < total; i++) {
-      byte expected = (byte) Math.max(y[i] & 0xff, z[i] & 0xff);
-      expectEquals(expected, x[i]);
-    }
-    doitMin100(x, y);
-    for (int i = 0; i < total; i++) {
-      byte expected = (byte) Math.min(y[i], 100);
-      expectEquals(expected, x[i]);
-    }
-    doitMinMax(x, y);
-    for (int i = 0; i < total; i++) {
-      int s = y[i];
-      byte expected = (byte) (s < -11 ? -11 : (s > 23 ? 23 : s));
-      expectEquals(expected, x[i]);
-    }
-    doitMinMaxUnsigned(x, y);
-    for (int i = 0; i < total; i++) {
-      int u = y[i] & 0xff;
-      byte expected = (byte) (u < 11 ? 11 : (u > 23 ? 23 : u));
-      expectEquals(expected, x[i]);
-    }
-    doitMinAlt(x, y, z);
-    for (int i = 0; i < total; i++) {
-      byte expected = (byte) Math.min(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMaxAlt(x, y, z);
-    for (int i = 0; i < total; i++) {
-      byte expected = (byte) Math.max(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    System.out.println("ByteSimdMinMax passed");
-  }
-
-  private static void expectEquals(byte expected, byte result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
-}
diff --git a/test/651-checker-simd-minmax/src/CharSimdMinMax.java b/test/651-checker-simd-minmax/src/CharSimdMinMax.java
deleted file mode 100644
index 30169c4..0000000
--- a/test/651-checker-simd-minmax/src/CharSimdMinMax.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Tests for MIN/MAX vectorization.
- */
-public class CharSimdMinMax {
-
-  /// CHECK-START: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:c\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMin(char[] x, char[] y, char[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (char) Math.min(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:c\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMax(char[] x, char[] y, char[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (char) Math.max(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (before)
-  /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                     loop:none
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get:c\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get>>,<<I100>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:c\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM64,MIPS64}: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (after)
-  /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none
-  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none
-  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>    outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Repl>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>         outer_loop:none
-  private static void doitMin100(char[] x, char[] y) {
-    int min = Math.min(x.length, y.length);
-    for (int i = 0; i < min; i++) {
-      x[i] = (char) Math.min(y[i], 100);
-    }
-  }
-
-  public static void main() {
-    char[] interesting = {
-      0x0000, 0x0001, 0x007f, 0x0080, 0x0081, 0x00ff,
-      0x0100, 0x0101, 0x017f, 0x0180, 0x0181, 0x01ff,
-      0x7f00, 0x7f01, 0x7f7f, 0x7f80, 0x7f81, 0x7fff,
-      0x8000, 0x8001, 0x807f, 0x8080, 0x8081, 0x80ff,
-      0x8100, 0x8101, 0x817f, 0x8180, 0x8181, 0x81ff,
-      0xff00, 0xff01, 0xff7f, 0xff80, 0xff81, 0xffff
-    };
-    // Initialize cross-values for the interesting values.
-    int total = interesting.length * interesting.length;
-    char[] x = new char[total];
-    char[] y = new char[total];
-    char[] z = new char[total];
-    int k = 0;
-    for (int i = 0; i < interesting.length; i++) {
-      for (int j = 0; j < interesting.length; j++) {
-        x[k] = 0;
-        y[k] = interesting[i];
-        z[k] = interesting[j];
-        k++;
-      }
-    }
-
-    // And test.
-    doitMin(x, y, z);
-    for (int i = 0; i < total; i++) {
-      char expected = (char) Math.min(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMax(x, y, z);
-    for (int i = 0; i < total; i++) {
-      char expected = (char) Math.max(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMin100(x, y);
-    for (int i = 0; i < total; i++) {
-      char expected = (char) Math.min(y[i], 100);
-      expectEquals(expected, x[i]);
-    }
-
-    System.out.println("CharSimdMinMax passed");
-  }
-
-  private static void expectEquals(char expected, char result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
-}
diff --git a/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java b/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java
deleted file mode 100644
index da20594..0000000
--- a/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Tests for MIN/MAX vectorization.
- */
-public class DoubleSimdMinMax {
-
-  /// CHECK-START: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>>      outer_loop:none
-  //
-  // TODO x86: 0.0 vs -0.0?
-  // TODO MIPS64: min(x, NaN)?
-  //
-  /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMin(double[] x, double[] y, double[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = Math.min(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>>      outer_loop:none
-  //
-  // TODO x86: 0.0 vs -0.0?
-  // TODO MIPS64: max(x, NaN)?
-  //
-  /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMax(double[] x, double[] y, double[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = Math.max(y[i], z[i]);
-    }
-  }
-
-  public static void main() {
-    double[] interesting = {
-      -0.0f,
-      +0.0f,
-      -1.0f,
-      +1.0f,
-      -3.14f,
-      +3.14f,
-      -100.0f,
-      +100.0f,
-      -4444.44f,
-      +4444.44f,
-      Double.MIN_NORMAL,
-      Double.MIN_VALUE,
-      Double.MAX_VALUE,
-      Double.NEGATIVE_INFINITY,
-      Double.POSITIVE_INFINITY,
-      Double.NaN
-    };
-    // Initialize cross-values for the interesting values.
-    int total = interesting.length * interesting.length;
-    double[] x = new double[total];
-    double[] y = new double[total];
-    double[] z = new double[total];
-    int k = 0;
-    for (int i = 0; i < interesting.length; i++) {
-      for (int j = 0; j < interesting.length; j++) {
-        x[k] = 0;
-        y[k] = interesting[i];
-        z[k] = interesting[j];
-        k++;
-      }
-    }
-
-    // And test.
-    doitMin(x, y, z);
-    for (int i = 0; i < total; i++) {
-      double expected = Math.min(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMax(x, y, z);
-    for (int i = 0; i < total; i++) {
-      double expected = Math.max(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-
-    System.out.println("DoubleSimdMinMax passed");
-  }
-
-  private static void expectEquals(double expected, double result) {
-    // Tests the bits directly. This distinguishes correctly between +0.0
-    // and -0.0 and returns a canonical representation for all NaN.
-    long expected_bits = Double.doubleToLongBits(expected);
-    long result_bits = Double.doubleToLongBits(result);
-    if (expected_bits != result_bits) {
-      throw new Error("Expected: " + expected +
-          "(0x" + Long.toHexString(expected_bits) + "), found: " + result +
-          "(0x" + Long.toHexString(result_bits) + ")");
-    }
-  }
-}
diff --git a/test/651-checker-simd-minmax/src/FloatSimdMinMax.java b/test/651-checker-simd-minmax/src/FloatSimdMinMax.java
deleted file mode 100644
index 6450812..0000000
--- a/test/651-checker-simd-minmax/src/FloatSimdMinMax.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Tests for MIN/MAX vectorization.
- */
-public class FloatSimdMinMax {
-
-  /// CHECK-START: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:f\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>>      outer_loop:none
-  //
-  // TODO x86: 0.0 vs -0.0?
-  // TODO MIPS64: min(x, NaN)?
-  //
-  /// CHECK-START-ARM64: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMin(float[] x, float[] y, float[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = Math.min(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:f\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>>      outer_loop:none
-  //
-  // TODO x86: 0.0 vs -0.0?
-  // TODO MIPS64: max(x, NaN)?
-  //
-  /// CHECK-START-ARM64: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMax(float[] x, float[] y, float[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = Math.max(y[i], z[i]);
-    }
-  }
-
-  public static void main() {
-    float[] interesting = {
-      -0.0f,
-      +0.0f,
-      -1.0f,
-      +1.0f,
-      -3.14f,
-      +3.14f,
-      -100.0f,
-      +100.0f,
-      -4444.44f,
-      +4444.44f,
-      Float.MIN_NORMAL,
-      Float.MIN_VALUE,
-      Float.MAX_VALUE,
-      Float.NEGATIVE_INFINITY,
-      Float.POSITIVE_INFINITY,
-      Float.NaN
-    };
-    // Initialize cross-values for the interesting values.
-    int total = interesting.length * interesting.length;
-    float[] x = new float[total];
-    float[] y = new float[total];
-    float[] z = new float[total];
-    int k = 0;
-    for (int i = 0; i < interesting.length; i++) {
-      for (int j = 0; j < interesting.length; j++) {
-        x[k] = 0;
-        y[k] = interesting[i];
-        z[k] = interesting[j];
-        k++;
-      }
-    }
-
-    // And test.
-    doitMin(x, y, z);
-    for (int i = 0; i < total; i++) {
-      float expected = Math.min(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMax(x, y, z);
-    for (int i = 0; i < total; i++) {
-      float expected = Math.max(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-
-    System.out.println("FloatSimdMinMax passed");
-  }
-
-  private static void expectEquals(float expected, float result) {
-    // Tests the bits directly. This distinguishes correctly between +0.0
-    // and -0.0 and returns a canonical representation for all NaN.
-    int expected_bits = Float.floatToIntBits(expected);
-    int result_bits = Float.floatToIntBits(result);
-    if (expected_bits != result_bits) {
-      throw new Error("Expected: " + expected +
-          "(0x" + Integer.toHexString(expected_bits) + "), found: " + result +
-          "(0x" + Integer.toHexString(result_bits) + ")");
-    }
-  }
-}
diff --git a/test/651-checker-simd-minmax/src/IntSimdMinMax.java b/test/651-checker-simd-minmax/src/IntSimdMinMax.java
deleted file mode 100644
index ad88843..0000000
--- a/test/651-checker-simd-minmax/src/IntSimdMinMax.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Tests for MIN/MAX vectorization.
- */
-public class IntSimdMinMax {
-
-  /// CHECK-START: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                                      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                                      loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Int32 loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>]         loop:<<Loop>>      outer_loop:none
-  private static void doitMin(int[] x, int[] y, int[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = Math.min(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                                      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                                      loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Int32 loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>]         loop:<<Loop>>      outer_loop:none
-  private static void doitMax(int[] x, int[] y, int[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = Math.max(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: int IntSimdMinMax.findMin(int[]) loop_optimization (after)
-  /// CHECK-DAG: <<Rep:d\d+>>  VecReplicateScalar          loop:none
-  /// CHECK-DAG: <<VPhi:d\d+>> Phi [<<Rep>>,<<Max:d\d+>>]  loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get:d\d+>>  VecLoad [{{l\d+}},{{i\d+}}] loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max>>       VecMin [<<Get>>,<<VPhi>>]   loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Red:d\d+>>  VecReduce [<<VPhi>>]        loop:none
-  /// CHECK-DAG:               VecExtractScalar [<<Red>>]  loop:none
-  private static int findMin(int[] a) {
-    int x = Integer.MAX_VALUE;
-    for (int i = 0; i < a.length; i++) {
-      if (a[i] < x)
-        x = a[i];
-    }
-    return x;
-  }
-
-  /// CHECK-START-{ARM,ARM64}: int IntSimdMinMax.findMax(int[]) loop_optimization (after)
-  /// CHECK-DAG: <<Rep:d\d+>>  VecReplicateScalar          loop:none
-  /// CHECK-DAG: <<VPhi:d\d+>> Phi [<<Rep>>,<<Max:d\d+>>]  loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get:d\d+>>  VecLoad [{{l\d+}},{{i\d+}}] loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max>>       VecMax [<<Get>>,<<VPhi>>]   loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Red:d\d+>>  VecReduce [<<VPhi>>]        loop:none
-  /// CHECK-DAG:               VecExtractScalar [<<Red>>]  loop:none
-  private static int findMax(int[] a) {
-    int x = Integer.MIN_VALUE;
-    for (int i = 0; i < a.length; i++) {
-      if (a[i] > x)
-        x = a[i];
-    }
-    return x;
-  }
-
-  public static void main() {
-    int[] interesting = {
-      0x00000000, 0x00000001, 0x00007fff, 0x00008000, 0x00008001, 0x0000ffff,
-      0x00010000, 0x00010001, 0x00017fff, 0x00018000, 0x00018001, 0x0001ffff,
-      0x7fff0000, 0x7fff0001, 0x7fff7fff, 0x7fff8000, 0x7fff8001, 0x7fffffff,
-      0x80000000, 0x80000001, 0x80007fff, 0x80008000, 0x80008001, 0x8000ffff,
-      0x80010000, 0x80010001, 0x80017fff, 0x80018000, 0x80018001, 0x8001ffff,
-      0xffff0000, 0xffff0001, 0xffff7fff, 0xffff8000, 0xffff8001, 0xffffffff
-    };
-    // Initialize cross-values for the interesting values.
-    int total = interesting.length * interesting.length;
-    int[] x = new int[total];
-    int[] y = new int[total];
-    int[] z = new int[total];
-    int k = 0;
-    for (int i = 0; i < interesting.length; i++) {
-      for (int j = 0; j < interesting.length; j++) {
-        x[k] = 0;
-        y[k] = interesting[i];
-        z[k] = interesting[j];
-        k++;
-      }
-    }
-
-    // And test.
-    doitMin(x, y, z);
-    for (int i = 0; i < total; i++) {
-      int expected = Math.min(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMax(x, y, z);
-    for (int i = 0; i < total; i++) {
-      int expected = Math.max(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    expectEquals(Integer.MIN_VALUE, findMin(x));
-    expectEquals(Integer.MAX_VALUE, findMax(x));
-    expectEquals(Integer.MIN_VALUE, findMin(y));
-    expectEquals(Integer.MAX_VALUE, findMax(y));
-    expectEquals(Integer.MIN_VALUE, findMin(z));
-    expectEquals(Integer.MAX_VALUE, findMax(z));
-
-    System.out.println("IntSimdMinMax passed");
-  }
-
-  private static void expectEquals(int expected, int result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
-}
diff --git a/test/651-checker-simd-minmax/src/LongSimdMinMax.java b/test/651-checker-simd-minmax/src/LongSimdMinMax.java
deleted file mode 100644
index bb0c604..0000000
--- a/test/651-checker-simd-minmax/src/LongSimdMinMax.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Tests for MIN/MAX vectorization.
- */
-public class LongSimdMinMax {
-
-  /// CHECK-START: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:j\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>>      outer_loop:none
-  //
-  // Not directly supported for longs.
-  //
-  /// CHECK-START-ARM64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after)
-  /// CHECK-NOT: VecMin
-  //
-  /// CHECK-START-MIPS64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>      outer_loop:none
-
-  private static void doitMin(long[] x, long[] y, long[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = Math.min(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:j\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>>      outer_loop:none
-  //
-  // Not directly supported for longs.
-  //
-  /// CHECK-START-ARM64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after)
-  /// CHECK-NOT: VecMax
-  //
-  /// CHECK-START-MIPS64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>      outer_loop:none
-  private static void doitMax(long[] x, long[] y, long[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = Math.max(y[i], z[i]);
-    }
-  }
-
-  public static void main() {
-    long[] interesting = {
-      0x0000000000000000L, 0x0000000000000001L, 0x000000007fffffffL,
-      0x0000000080000000L, 0x0000000080000001L, 0x00000000ffffffffL,
-      0x0000000100000000L, 0x0000000100000001L, 0x000000017fffffffL,
-      0x0000000180000000L, 0x0000000180000001L, 0x00000001ffffffffL,
-      0x7fffffff00000000L, 0x7fffffff00000001L, 0x7fffffff7fffffffL,
-      0x7fffffff80000000L, 0x7fffffff80000001L, 0x7fffffffffffffffL,
-      0x8000000000000000L, 0x8000000000000001L, 0x800000007fffffffL,
-      0x8000000080000000L, 0x8000000080000001L, 0x80000000ffffffffL,
-      0x8000000100000000L, 0x8000000100000001L, 0x800000017fffffffL,
-      0x8000000180000000L, 0x8000000180000001L, 0x80000001ffffffffL,
-      0xffffffff00000000L, 0xffffffff00000001L, 0xffffffff7fffffffL,
-      0xffffffff80000000L, 0xffffffff80000001L, 0xffffffffffffffffL
-    };
-    // Initialize cross-values for the interesting values.
-    int total = interesting.length * interesting.length;
-    long[] x = new long[total];
-    long[] y = new long[total];
-    long[] z = new long[total];
-    int k = 0;
-    for (int i = 0; i < interesting.length; i++) {
-      for (int j = 0; j < interesting.length; j++) {
-        x[k] = 0;
-        y[k] = interesting[i];
-        z[k] = interesting[j];
-        k++;
-      }
-    }
-
-    // And test.
-    doitMin(x, y, z);
-    for (int i = 0; i < total; i++) {
-      long expected = Math.min(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMax(x, y, z);
-    for (int i = 0; i < total; i++) {
-      long expected = Math.max(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-
-    System.out.println("LongSimdMinMax passed");
-  }
-
-  private static void expectEquals(long expected, long result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
-}
diff --git a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
deleted file mode 100644
index 9075d80..0000000
--- a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Tests for MIN/MAX vectorization.
- */
-public class ShortSimdMinMax {
-
-  /// CHECK-START: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>    outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>         outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>         outer_loop:none
-  private static void doitMin(short[] x, short[] y, short[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (short) Math.min(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) instruction_simplifier (before)
-  /// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535                   loop:none
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<IMAX>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<IMAX>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<And1>>,<<And2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>     outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>          outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>          outer_loop:none
-  private static void doitMinUnsigned(short[] x, short[] y, short[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (short) Math.min(y[i] & 0xffff, z[i] & 0xffff);
-    }
-  }
-
-  /// CHECK-START: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>    outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>         outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>         outer_loop:none
-  private static void doitMax(short[] x, short[] y, short[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (short) Math.max(y[i], z[i]);
-    }
-  }
-
-  /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) instruction_simplifier (before)
-  /// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535                   loop:none
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<IMAX>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<IMAX>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  InvokeStaticOrDirect [<<And1>>,<<And2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>     outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>          outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>          outer_loop:none
-  private static void doitMaxUnsigned(short[] x, short[] y, short[] z) {
-    int min = Math.min(x.length, Math.min(y.length, z.length));
-    for (int i = 0; i < min; i++) {
-      x[i] = (short) Math.max(y[i] & 0xffff, z[i] & 0xffff);
-    }
-  }
-
-  /// CHECK-START: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                     loop:none
-  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get:s\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get>>,<<I100>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none
-  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none
-  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>   outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Repl>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>>        outer_loop:none
-  private static void doitMin100(short[] x, short[] y) {
-    int min = Math.min(x.length, y.length);
-    for (int i = 0; i < min; i++) {
-      x[i] = (short) Math.min(y[i], 100);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMax(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<I11:i\d+>>  IntConstant -1111                    loop:none
-  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 2323                     loop:none
-  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
-  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
-  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
-  private static void doitMinMax(short[] x, short[] y) {
-    int n = Math.min(x.length, y.length);
-    for (int i = 0; i < n; i++) {
-      x[i] = (short) Math.max(-1111, Math.min(y[i], 2323));
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMaxUnsigned(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<I11:i\d+>>  IntConstant 1111                     loop:none
-  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 2323                     loop:none
-  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
-  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
-  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
-  private static void doitMinMaxUnsigned(short[] x, short[] y) {
-    int n = Math.min(x.length, y.length);
-    for (int i = 0; i < n; i++) {
-      x[i] = (short) Math.max(1111, Math.min(y[i] & 0xffff, 2323));
-    }
-  }
-
-  public static void main() {
-    short[] interesting = {
-      (short) 0x0000, (short) 0x0001, (short) 0x007f,
-      (short) 0x0080, (short) 0x0081, (short) 0x00ff,
-      (short) 0x0100, (short) 0x0101, (short) 0x017f,
-      (short) 0x0180, (short) 0x0181, (short) 0x01ff,
-      (short) 0x7f00, (short) 0x7f01, (short) 0x7f7f,
-      (short) 0x7f80, (short) 0x7f81, (short) 0x7fff,
-      (short) 0x8000, (short) 0x8001, (short) 0x807f,
-      (short) 0x8080, (short) 0x8081, (short) 0x80ff,
-      (short) 0x8100, (short) 0x8101, (short) 0x817f,
-      (short) 0x8180, (short) 0x8181, (short) 0x81ff,
-      (short) 0xff00, (short) 0xff01, (short) 0xff7f,
-      (short) 0xff80, (short) 0xff81, (short) 0xffff
-    };
-    // Initialize cross-values for the interesting values.
-    int total = interesting.length * interesting.length;
-    short[] x = new short[total];
-    short[] y = new short[total];
-    short[] z = new short[total];
-    int k = 0;
-    for (int i = 0; i < interesting.length; i++) {
-      for (int j = 0; j < interesting.length; j++) {
-        x[k] = 0;
-        y[k] = interesting[i];
-        z[k] = interesting[j];
-        k++;
-      }
-    }
-
-    // And test.
-    doitMin(x, y, z);
-    for (int i = 0; i < total; i++) {
-      short expected = (short) Math.min(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMinUnsigned(x, y, z);
-    for (int i = 0; i < total; i++) {
-      short expected = (short) Math.min(y[i] & 0xffff, z[i] & 0xffff);
-      expectEquals(expected, x[i]);
-    }
-    doitMax(x, y, z);
-    for (int i = 0; i < total; i++) {
-      short expected = (short) Math.max(y[i], z[i]);
-      expectEquals(expected, x[i]);
-    }
-    doitMaxUnsigned(x, y, z);
-    for (int i = 0; i < total; i++) {
-      short expected = (short) Math.max(y[i] & 0xffff, z[i] & 0xffff);
-      expectEquals(expected, x[i]);
-    }
-    doitMin100(x, y);
-    for (int i = 0; i < total; i++) {
-      short expected = (short) Math.min(y[i], 100);
-      expectEquals(expected, x[i]);
-    }
-    doitMinMax(x, y);
-    for (int i = 0; i < total; i++) {
-      int s = y[i];
-      short expected = (short) (s < -1111 ? -1111 : (s > 2323 ? 2323 : s));
-      expectEquals(expected, x[i]);
-    }
-    doitMinMaxUnsigned(x, y);
-    for (int i = 0; i < total; i++) {
-      int u = y[i] & 0xffff;
-      short expected = (short) (u < 1111 ? 1111 : (u > 2323 ? 2323 : u));
-      expectEquals(expected, x[i]);
-    }
-
-    System.out.println("ShortSimdMinMax passed");
-  }
-
-  private static void expectEquals(short expected, short result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
-}
diff --git a/test/660-checker-sad-byte/src/Main.java b/test/660-checker-sad-byte/src/Main.java
index cd7fbcb..bcd62c4 100644
--- a/test/660-checker-sad-byte/src/Main.java
+++ b/test/660-checker-sad-byte/src/Main.java
@@ -19,22 +19,22 @@
  */
 public class Main {
 
-  /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad1(byte x, byte y) {
     return x >= y ? x - y : y - x;
   }
 
-  /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad2(byte x, byte y) {
@@ -43,11 +43,11 @@
     return diff;
   }
 
-  /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad3(byte x, byte y) {
@@ -55,11 +55,11 @@
     return diff >= 0 ? diff : -diff;
   }
 
-  /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad3Alt(byte x, byte y) {
@@ -67,11 +67,11 @@
     return 0 <= diff ? diff : -diff;
   }
 
-  /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL1(byte x, byte y) {
@@ -80,11 +80,11 @@
     return xl >= yl ? xl - yl : yl - xl;
   }
 
-  /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL2(byte x, byte y) {
@@ -93,11 +93,11 @@
     return diff;
   }
 
-  /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL3(byte x, byte y) {
@@ -105,11 +105,11 @@
     return diff >= 0L ? diff : -diff;
   }
 
-  /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL3Alt(byte x, byte y) {
diff --git a/test/660-checker-sad-char/src/Main.java b/test/660-checker-sad-char/src/Main.java
index ecf748a..998ec33 100644
--- a/test/660-checker-sad-char/src/Main.java
+++ b/test/660-checker-sad-char/src/Main.java
@@ -19,22 +19,22 @@
  */
 public class Main {
 
-  /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad1(char x, char y) {
     return x >= y ? x - y : y - x;
   }
 
-  /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad2(char x, char y) {
@@ -43,11 +43,11 @@
     return diff;
   }
 
-  /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad3(char x, char y) {
@@ -55,11 +55,11 @@
     return diff >= 0 ? diff : -diff;
   }
 
-  /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad3Alt(char x, char y) {
@@ -67,11 +67,11 @@
     return 0 <= diff ? diff : -diff;
   }
 
-  /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL1(char x, char y) {
@@ -80,11 +80,11 @@
     return xl >= yl ? xl - yl : yl - xl;
   }
 
-  /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL2(char x, char y) {
@@ -93,11 +93,11 @@
     return diff;
   }
 
-  /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL3(char x, char y) {
@@ -105,11 +105,11 @@
     return diff >= 0L ? diff : -diff;
   }
 
-  /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL3Alt(char x, char y) {
diff --git a/test/660-checker-sad-int/src/Main.java b/test/660-checker-sad-int/src/Main.java
index 280dd66..09878a5 100644
--- a/test/660-checker-sad-int/src/Main.java
+++ b/test/660-checker-sad-int/src/Main.java
@@ -19,15 +19,15 @@
  */
 public class Main {
 
-  /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT: Abs
   //
   // NOTE: for direct 32-bit operands, this is not an ABS.
@@ -35,11 +35,11 @@
     return x >= y ? x - y : y - x;
   }
 
-  /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad2(int x, int y) {
@@ -48,11 +48,11 @@
     return diff;
   }
 
-  /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad3(int x, int y) {
@@ -60,11 +60,11 @@
     return diff >= 0 ? diff : -diff;
   }
 
-  /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad3Alt(int x, int y) {
@@ -72,11 +72,11 @@
     return 0 <= diff ? diff : -diff;
   }
 
-  /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL1(int x, int y) {
@@ -85,11 +85,11 @@
     return xl >= yl ? xl - yl : yl - xl;
   }
 
-  /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL2(int x, int y) {
@@ -98,11 +98,11 @@
     return diff;
   }
 
-  /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL3(int x, int y) {
@@ -110,11 +110,11 @@
     return diff >= 0L ? diff : -diff;
   }
 
-  /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL3Alt(int x, int y) {
diff --git a/test/660-checker-sad-long/src/Main.java b/test/660-checker-sad-long/src/Main.java
index ca0f4b7..b9eeb5f 100644
--- a/test/660-checker-sad-long/src/Main.java
+++ b/test/660-checker-sad-long/src/Main.java
@@ -19,15 +19,15 @@
  */
 public class Main {
 
-  /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT: Abs
   //
   // NOTE: for direct 64-bit operands, this is not an ABS.
@@ -35,11 +35,11 @@
     return x >= y ? x - y : y - x;
   }
 
-  /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sad2(long x, long y) {
@@ -48,11 +48,11 @@
     return diff;
   }
 
-  /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sad3(long x, long y) {
@@ -60,11 +60,11 @@
     return diff >= 0 ? diff : -diff;
   }
 
-  /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sad3Alt(long x, long y) {
diff --git a/test/660-checker-sad-short/src/Main.java b/test/660-checker-sad-short/src/Main.java
index b712a14..0a1a4dc 100644
--- a/test/660-checker-sad-short/src/Main.java
+++ b/test/660-checker-sad-short/src/Main.java
@@ -19,22 +19,22 @@
  */
 public class Main {
 
-  /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad1(short x, short y) {
     return x >= y ? x - y : y - x;
   }
 
-  /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad2(short x, short y) {
@@ -43,11 +43,11 @@
     return diff;
   }
 
-  /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad3(short x, short y) {
@@ -55,11 +55,11 @@
     return diff >= 0 ? diff : -diff;
   }
 
-  /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:i\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:i\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static int sad3Alt(short x, short y) {
@@ -67,11 +67,11 @@
     return 0 <= diff ? diff : -diff;
   }
 
-  /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL1(short x, short y) {
@@ -80,11 +80,11 @@
     return xl >= yl ? xl - yl : yl - xl;
   }
 
-  /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL2(short x, short y) {
@@ -93,11 +93,11 @@
     return diff;
   }
 
-  /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL3(short x, short y) {
@@ -105,11 +105,11 @@
     return diff >= 0L ? diff : -diff;
   }
 
-  /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Select:j\d+>> Select
   /// CHECK-DAG:                 Return [<<Select>>]
   //
-  /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Intrin:j\d+>> Abs
   /// CHECK-DAG:                 Return [<<Intrin>>]
   static long sadL3Alt(short x, short y) {
diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java
index fcd50a6..eff2018 100644
--- a/test/661-checker-simd-reduc/src/Main.java
+++ b/test/661-checker-simd-reduc/src/Main.java
@@ -347,126 +347,6 @@
     return sum;
   }
 
-  private static byte reductionMinByte(byte[] x) {
-    byte min = Byte.MAX_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      min = (byte) Math.min(min, x[i]);
-    }
-    return min;
-  }
-
-  private static short reductionMinShort(short[] x) {
-    short min = Short.MAX_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      min = (short) Math.min(min, x[i]);
-    }
-    return min;
-  }
-
-  private static char reductionMinChar(char[] x) {
-    char min = Character.MAX_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      min = (char) Math.min(min, x[i]);
-    }
-    return min;
-  }
-
-  /// CHECK-START: int Main.reductionMinInt(int[]) loop_optimization (before)
-  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
-  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
-  /// CHECK-DAG: <<ConsM:i\d+>>  IntConstant 2147483647        loop:none
-  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<ConsM>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 Min [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 Return [<<Phi2>>]             loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMinInt(int[]) loop_optimization (after)
-  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant {{2|4}}           loop:none
-  /// CHECK-DAG: <<Set:d\d+>>    VecReplicateScalar [{{i\d+}}] loop:none
-  /// CHECK-DAG: <<Phi:d\d+>>    Phi [<<Set>>,{{d\d+}}]        loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 VecMin [<<Phi>>,<<Load>>]     loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 Add [<<I>>,<<Cons>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi>>]           loop:none
-  /// CHECK-DAG: <<Extr:i\d+>>   VecExtractScalar [<<Red>>]    loop:none
-  private static int reductionMinInt(int[] x) {
-    int min = Integer.MAX_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      min = Math.min(min, x[i]);
-    }
-    return min;
-  }
-
-  private static long reductionMinLong(long[] x) {
-    long min = Long.MAX_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      min = Math.min(min, x[i]);
-    }
-    return min;
-  }
-
-  private static byte reductionMaxByte(byte[] x) {
-    byte max = Byte.MIN_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      max = (byte) Math.max(max, x[i]);
-    }
-    return max;
-  }
-
-  private static short reductionMaxShort(short[] x) {
-    short max = Short.MIN_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      max = (short) Math.max(max, x[i]);
-    }
-    return max;
-  }
-
-  private static char reductionMaxChar(char[] x) {
-    char max = Character.MIN_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      max = (char) Math.max(max, x[i]);
-    }
-    return max;
-  }
-
-  /// CHECK-START: int Main.reductionMaxInt(int[]) loop_optimization (before)
-  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
-  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
-  /// CHECK-DAG: <<ConsM:i\d+>>  IntConstant -2147483648       loop:none
-  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<ConsM>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 Max [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 Return [<<Phi2>>]             loop:none
-  //
-  /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMaxInt(int[]) loop_optimization (after)
-  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant {{2|4}}           loop:none
-  /// CHECK-DAG: <<Set:d\d+>>    VecReplicateScalar [{{i\d+}}] loop:none
-  /// CHECK-DAG: <<Phi:d\d+>>    Phi [<<Set>>,{{d\d+}}]        loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 VecMax [<<Phi>>,<<Load>>]     loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 Add [<<I>>,<<Cons>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi>>]           loop:none
-  /// CHECK-DAG: <<Extr:i\d+>>   VecExtractScalar [<<Red>>]    loop:none
-  private static int reductionMaxInt(int[] x) {
-    int max = Integer.MIN_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      max = Math.max(max, x[i]);
-    }
-    return max;
-  }
-
-  private static long reductionMaxLong(long[] x) {
-    long max = Long.MIN_VALUE;
-    for (int i = 0; i < x.length; i++) {
-      max = Math.max(max, x[i]);
-    }
-    return max;
-  }
-
   //
   // A few special cases.
   //
@@ -491,24 +371,6 @@
     return sum;
   }
 
-  private static int reductionMinInt10(int[] x) {
-    int min = Integer.MAX_VALUE;
-    // Amenable to complete unrolling.
-    for (int i = 10; i <= 10; i++) {
-      min = Math.min(min, x[i]);
-    }
-    return min;
-  }
-
-  private static int reductionMaxInt10(int[] x) {
-    int max = Integer.MIN_VALUE;
-    // Amenable to complete unrolling.
-    for (int i = 10; i <= 10; i++) {
-      max = Math.max(max, x[i]);
-    }
-    return max;
-  }
-
   //
   // Main driver.
   //
@@ -587,40 +449,10 @@
     expectEquals(27466, reductionMinusChar(xc));
     expectEquals(-365750, reductionMinusInt(xi));
     expectEquals(-365750L, reductionMinusLong(xl));
-    expectEquals(-128, reductionMinByte(xb));
-    expectEquals(-17, reductionMinShort(xs));
-    expectEquals(1, reductionMinChar(xc));
-    expectEquals(-17, reductionMinInt(xi));
-    expectEquals(-17L, reductionMinLong(xl));
-    expectEquals(3, reductionMinByte(xpb));
-    expectEquals(3, reductionMinShort(xps));
-    expectEquals(3, reductionMinChar(xpc));
-    expectEquals(3, reductionMinInt(xpi));
-    expectEquals(3L, reductionMinLong(xpl));
-    expectEquals(-103, reductionMinByte(xnb));
-    expectEquals(-103, reductionMinShort(xns));
-    expectEquals(-103, reductionMinInt(xni));
-    expectEquals(-103L, reductionMinLong(xnl));
-    expectEquals(127, reductionMaxByte(xb));
-    expectEquals(1480, reductionMaxShort(xs));
-    expectEquals(65534, reductionMaxChar(xc));
-    expectEquals(1480, reductionMaxInt(xi));
-    expectEquals(1480L, reductionMaxLong(xl));
-    expectEquals(102, reductionMaxByte(xpb));
-    expectEquals(102, reductionMaxShort(xps));
-    expectEquals(102, reductionMaxChar(xpc));
-    expectEquals(102, reductionMaxInt(xpi));
-    expectEquals(102L, reductionMaxLong(xpl));
-    expectEquals(-4, reductionMaxByte(xnb));
-    expectEquals(-4, reductionMaxShort(xns));
-    expectEquals(-4, reductionMaxInt(xni));
-    expectEquals(-4L, reductionMaxLong(xnl));
 
     // Test special cases.
     expectEquals(13, reductionInt10(xi));
     expectEquals(-13, reductionMinusInt10(xi));
-    expectEquals(13, reductionMinInt10(xi));
-    expectEquals(13, reductionMaxInt10(xi));
 
     System.out.println("passed");
   }
diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java
index a808e94..782748c 100644
--- a/test/674-hiddenapi/src-art/Main.java
+++ b/test/674-hiddenapi/src-art/Main.java
@@ -16,6 +16,7 @@
 
 import dalvik.system.InMemoryDexClassLoader;
 import dalvik.system.PathClassLoader;
+import dalvik.system.VMRuntime;
 import java.io.File;
 import java.io.InputStream;
 import java.lang.reflect.Constructor;
@@ -34,26 +35,41 @@
     // Enable hidden API checks in case they are disabled by default.
     init();
 
+    // TODO there are sequential depencies between these test cases, and bugs
+    // in the production code may lead to subsequent tests to erroneously pass,
+    // or test the wrong thing. We rely on not deduping hidden API warnings
+    // here for the same reasons), meaning the code under test and production
+    // code are running in different configurations. Each test should be run in
+    // a fresh process to ensure that they are working correcting and not
+    // accidentally interfering with eachother.
+
     // Run test with both parent and child dex files loaded with class loaders.
     // The expectation is that hidden members in parent should be visible to
     // the child.
-    doTest(false, false);
+    doTest(false, false, false);
     doUnloading();
 
     // Now append parent dex file to boot class path and run again. This time
-    // the child dex file should not be able to access private APIs of the parent.
+    // the child dex file should not be able to access private APIs of the
+    // parent.
     appendToBootClassLoader(DEX_PARENT_BOOT);
-    doTest(true, false);
+    doTest(true, false, false);
+    doUnloading();
+
+    // Now run the same test again, but with the blacklist exmemptions list set
+    // to "L" which matches everything.
+    doTest(true, false, true);
     doUnloading();
 
     // And finally append to child to boot class path as well. With both in the
     // boot class path, access should be granted.
     appendToBootClassLoader(DEX_CHILD);
-    doTest(true, true);
+    doTest(true, true, false);
     doUnloading();
   }
 
-  private static void doTest(boolean parentInBoot, boolean childInBoot) throws Exception {
+  private static void doTest(boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis)
+      throws Exception {
     // Load parent dex if it is not in boot class path.
     ClassLoader parentLoader = null;
     if (parentInBoot) {
@@ -78,12 +94,18 @@
     // be loaded once, but for some reason even classes from a class loader
     // cannot register their native methods against symbols in a shared library
     // loaded by their parent class loader.
-    String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot);
+    String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot, whitelistAllApis);
+
+    if (whitelistAllApis) {
+      VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"});
+    }
 
     // Invoke ChildClass.runTest
     Class.forName("ChildClass", true, childLoader)
-        .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE)
-            .invoke(null, nativeLibCopy, parentInBoot, childInBoot);
+        .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
+            .invoke(null, nativeLibCopy, parentInBoot, childInBoot, whitelistAllApis);
+
+    VMRuntime.getRuntime().setHiddenApiExemptions(new String[0]);
   }
 
   // Routine which tries to figure out the absolute path of our native library.
@@ -122,20 +144,21 @@
     return buffer;
   }
 
-  // Copy native library to a new file with a unique name so it does not conflict
-  // with other loaded instance of the same binary file.
-  private static String createNativeLibCopy(boolean parentInBoot, boolean childInBoot)
-      throws Exception {
+  // Copy native library to a new file with a unique name so it does not
+  // conflict with other loaded instance of the same binary file.
+  private static String createNativeLibCopy(
+      boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) throws Exception {
     String tempFileName = System.mapLibraryName(
-        "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0"));
+        "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0") +
+         (whitelistAllApis ? "1" : "0"));
     File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName);
     Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath());
     return tempFile.getAbsolutePath();
   }
 
   private static void doUnloading() {
-    // Do multiple GCs to prevent rare flakiness if some other thread is keeping the
-    // classloader live.
+    // Do multiple GCs to prevent rare flakiness if some other thread is
+    // keeping the classloader live.
     for (int i = 0; i < 5; ++i) {
        Runtime.getRuntime().gc();
     }
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index ea66f16..db3ba6d 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -70,7 +70,7 @@
   private static final boolean booleanValues[] = new boolean[] { false, true };
 
   public static void runTest(String libFileName, boolean expectedParentInBoot,
-      boolean expectedChildInBoot) throws Exception {
+      boolean expectedChildInBoot, boolean everythingWhitelisted) throws Exception {
     System.load(libFileName);
 
     // Check expectations about loading into boot class path.
@@ -84,18 +84,24 @@
       throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
                                  "in boot class path");
     }
+    ChildClass.everythingWhitelisted = everythingWhitelisted;
 
     boolean isSameBoot = (isParentInBoot == isChildInBoot);
+    boolean isDebuggable = VMRuntime.getRuntime().isJavaDebuggable();
 
     // Run meaningful combinations of access flags.
     for (Hiddenness hiddenness : Hiddenness.values()) {
       final Behaviour expected;
-      if (isSameBoot || hiddenness == Hiddenness.Whitelist) {
+      // Warnings are now disabled whenever access is granted, even for
+      // greylisted APIs. This is the behaviour for release builds.
+      if (isSameBoot || everythingWhitelisted || hiddenness == Hiddenness.Whitelist) {
         expected = Behaviour.Granted;
       } else if (hiddenness == Hiddenness.Blacklist) {
         expected = Behaviour.Denied;
-      } else {
+      } else if (isDebuggable) {
         expected = Behaviour.Warning;
+      } else {
+        expected = Behaviour.Granted;
       }
 
       for (boolean isStatic : booleanValues) {
@@ -121,6 +127,7 @@
         checkLinking("LinkFieldGet" + suffix, /*takesParameter*/ false, expected);
         checkLinking("LinkFieldSet" + suffix, /*takesParameter*/ true, expected);
         checkLinking("LinkMethod" + suffix, /*takesParameter*/ false, expected);
+        checkLinking("LinkMethodInterface" + suffix, /*takesParameter*/ false, expected);
       }
 
       // Check whether Class.newInstance succeeds.
@@ -509,14 +516,16 @@
       String fn, boolean canAccess) {
     throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
         "." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
-        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
+        "everythingWhitelisted = " + everythingWhitelisted);
   }
 
   private static void throwAccessException(Class<?> klass, String name, boolean isField,
       String fn) {
     throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
         klass.getName() + "." + name + " using " + fn + ". " +
-        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
+        "everythingWhitelisted = " + everythingWhitelisted);
   }
 
   private static void throwWarningException(Class<?> klass, String name, boolean isField,
@@ -524,7 +533,8 @@
     throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
         klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
         "set the warning flag. " +
-        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
+        "everythingWhitelisted = " + everythingWhitelisted);
   }
 
   private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
@@ -534,6 +544,7 @@
 
   private static boolean isParentInBoot;
   private static boolean isChildInBoot;
+  private static boolean everythingWhitelisted;
 
   private static native boolean hasPendingWarning();
   private static native void clearWarning();
diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java
index a89b92b..0fa0b19 100644
--- a/test/674-hiddenapi/src-ex/Linking.java
+++ b/test/674-hiddenapi/src-ex/Linking.java
@@ -174,6 +174,32 @@
   }
 }
 
+// INVOKE INSTANCE INTERFACE METHOD
+
+class LinkMethodInterfaceWhitelist {
+  public static int access() {
+    return DummyClass.getInterfaceInstance().methodPublicWhitelist();
+  }
+}
+
+class LinkMethodInterfaceLightGreylist {
+  public static int access() {
+    return DummyClass.getInterfaceInstance().methodPublicLightGreylist();
+  }
+}
+
+class LinkMethodInterfaceDarkGreylist {
+  public static int access() {
+    return DummyClass.getInterfaceInstance().methodPublicDarkGreylist();
+  }
+}
+
+class LinkMethodInterfaceBlacklist {
+  public static int access() {
+    return DummyClass.getInterfaceInstance().methodPublicBlacklist();
+  }
+}
+
 // INVOKE STATIC METHOD
 
 class LinkMethodStaticWhitelist {
@@ -199,3 +225,29 @@
     return ParentClass.methodPublicStaticBlacklist();
   }
 }
+
+// INVOKE INTERFACE STATIC METHOD
+
+class LinkMethodInterfaceStaticWhitelist {
+  public static int access() {
+    return ParentInterface.methodPublicStaticWhitelist();
+  }
+}
+
+class LinkMethodInterfaceStaticLightGreylist {
+  public static int access() {
+    return ParentInterface.methodPublicStaticLightGreylist();
+  }
+}
+
+class LinkMethodInterfaceStaticDarkGreylist {
+  public static int access() {
+    return ParentInterface.methodPublicStaticDarkGreylist();
+  }
+}
+
+class LinkMethodInterfaceStaticBlacklist {
+  public static int access() {
+    return ParentInterface.methodPublicStaticBlacklist();
+  }
+}
diff --git a/test/674-hiddenapi/src/DummyClass.java b/test/674-hiddenapi/src/DummyClass.java
new file mode 100644
index 0000000..51281a2
--- /dev/null
+++ b/test/674-hiddenapi/src/DummyClass.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class DummyClass implements ParentInterface {
+  public int methodPublicWhitelist() { return 1; }
+  public int methodPublicLightGreylist() { return 2; }
+  public int methodPublicDarkGreylist() { return 3; }
+  public int methodPublicBlacklist() { return 4; }
+
+  public static ParentInterface getInterfaceInstance() {
+    return new DummyClass();
+  }
+}
diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java
index e36fe0e..f79ac9d 100644
--- a/test/674-hiddenapi/src/ParentInterface.java
+++ b/test/674-hiddenapi/src/ParentInterface.java
@@ -23,9 +23,9 @@
 
   // INSTANCE METHOD
   int methodPublicWhitelist();
-  int methodPublicBlacklist();
   int methodPublicLightGreylist();
   int methodPublicDarkGreylist();
+  int methodPublicBlacklist();
 
   // STATIC METHOD
   static int methodPublicStaticWhitelist() { return 21; }
diff --git a/test/677-fsi2/expected.txt b/test/677-fsi2/expected.txt
new file mode 100644
index 0000000..de00847
--- /dev/null
+++ b/test/677-fsi2/expected.txt
@@ -0,0 +1,4 @@
+Run default
+Hello World
+Run without dex2oat
+Hello World
diff --git a/test/677-fsi2/info.txt b/test/677-fsi2/info.txt
new file mode 100644
index 0000000..ed0a0f2
--- /dev/null
+++ b/test/677-fsi2/info.txt
@@ -0,0 +1 @@
+Test that -Xonly-use-system-oat-files works.
diff --git a/test/677-fsi2/run b/test/677-fsi2/run
new file mode 100644
index 0000000..039a6a7
--- /dev/null
+++ b/test/677-fsi2/run
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+
+echo "Run default"
+${RUN} $@ --runtime-option -Xonly-use-system-oat-files
+return_status1=$?
+
+echo "Run without dex2oat"
+${RUN} $@ --no-dex2oat --runtime-option -Xonly-use-system-oat-files
+return_status2=$?
+
+(exit $return_status1) && (exit $return_status2)
diff --git a/test/651-checker-simd-minmax/src/Main.java b/test/677-fsi2/src/Main.java
similarity index 78%
rename from test/651-checker-simd-minmax/src/Main.java
rename to test/677-fsi2/src/Main.java
index 9134dd1..834075f 100644
--- a/test/651-checker-simd-minmax/src/Main.java
+++ b/test/677-fsi2/src/Main.java
@@ -16,12 +16,6 @@
 
 public class Main {
   public static void main(String[] args) {
-    ByteSimdMinMax.main();
-    CharSimdMinMax.main();
-    ShortSimdMinMax.main();
-    IntSimdMinMax.main();
-    LongSimdMinMax.main();
-    DoubleSimdMinMax.main();
-    FloatSimdMinMax.main();
+    System.out.println("Hello World");
   }
 }
diff --git a/test/678-checker-simd-saturation/info.txt b/test/678-checker-simd-saturation/info.txt
deleted file mode 100644
index ab7a802..0000000
--- a/test/678-checker-simd-saturation/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on saturation arithmetic vectorization.
diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java
deleted file mode 100644
index 7a22ca1..0000000
--- a/test/678-checker-simd-saturation/src/Main.java
+++ /dev/null
@@ -1,784 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-/**
- * Functional tests for saturation aritmethic vectorization.
- */
-public class Main {
-
-  static final int $inline$p15() {
-    return 15;
-  }
-
-  static final int $inline$m15() {
-    return -15;
-  }
-
-  //
-  // Direct min-max.
-  //
-
-  /// CHECK-START: void Main.satAddUByte(byte[], byte[], byte[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clip:i\d+>> IntConstant 255                      loop:none
-  /// CHECK-DAG: <<Get1:a\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:a\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:i\d+>>  Add [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Add>>,<<Clip>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:b\d+>> TypeConversion [<<Min>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satAddUByte(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void satAddUByte(byte[] a, byte[] b, byte[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      c[i] = (byte) Math.min((a[i] & 0xff) + (b[i] & 0xff), 255);
-    }
-  }
-
-  /// CHECK-START: void Main.satAddSByte(byte[], byte[], byte[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -128                     loop:none
-  /// CHECK-DAG: <<Clp2:i\d+>> IntConstant  127                     loop:none
-  /// CHECK-DAG: <<Get1:b\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:b\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:i\d+>>  Add [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Add>>,<<Clp2>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<Clp1>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:b\d+>> TypeConversion [<<Max>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satAddSByte(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void satAddSByte(byte[] a, byte[] b, byte[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      c[i] = (byte) Math.max(Math.min(a[i] + b[i], 127), -128);
-    }
-  }
-
-  /// CHECK-START: void Main.satAddUShort(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clip:i\d+>> IntConstant 65535                    loop:none
-  /// CHECK-DAG: <<Get1:c\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:c\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:i\d+>>  Add [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Add>>,<<Clip>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Min>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satAddUShort(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void satAddUShort(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      c[i] = (short) Math.min((a[i] & 0xffff) + (b[i] & 0xffff), 65535);
-    }
-  }
-
-  /// CHECK-START: void Main.satAddSShort(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768                   loop:none
-  /// CHECK-DAG: <<Clp2:i\d+>> IntConstant  32767                   loop:none
-  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:i\d+>>  Add [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Add>>,<<Clp2>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<Clp1>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Max>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satAddSShort(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void satAddSShort(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      c[i] = (short) Math.max(Math.min(a[i] + b[i], 32767), -32768);
-    }
-  }
-
-  /// CHECK-START: void Main.satSubUByte(byte[], byte[], byte[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clip:i\d+>> IntConstant 0                        loop:none
-  /// CHECK-DAG: <<Get1:a\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:a\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Sub:i\d+>>  Sub [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Sub>>,<<Clip>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:b\d+>> TypeConversion [<<Max>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satSubUByte(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>>      outer_loop:none
-  public static void satSubUByte(byte[] a, byte[] b, byte[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      c[i] = (byte) Math.max((a[i] & 0xff) - (b[i] & 0xff), 0);
-    }
-  }
-
-  /// CHECK-START: void Main.satSubSByte(byte[], byte[], byte[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -128                     loop:none
-  /// CHECK-DAG: <<Clp2:i\d+>> IntConstant  127                     loop:none
-  /// CHECK-DAG: <<Get1:b\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:b\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Sub:i\d+>>  Sub [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Sub>>,<<Clp2>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<Clp1>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:b\d+>> TypeConversion [<<Max>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satSubSByte(byte[], byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>>      outer_loop:none
-  public static void satSubSByte(byte[] a, byte[] b, byte[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      c[i] = (byte) Math.max(Math.min(a[i] - b[i], 127), -128);
-    }
-  }
-
-  /// CHECK-START: void Main.satSubUShort(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clip:i\d+>> IntConstant 0                        loop:none
-  /// CHECK-DAG: <<Get1:c\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:c\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Sub:i\d+>>  Sub [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Sub>>,<<Clip>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Max>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satSubUShort(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>>      outer_loop:none
-  public static void satSubUShort(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      c[i] = (short) Math.max((a[i] & 0xffff) - (b[i] & 0xffff), 0);
-    }
-  }
-
-  /// CHECK-START: void Main.satSubSShort(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768                   loop:none
-  /// CHECK-DAG: <<Clp2:i\d+>> IntConstant  32767                   loop:none
-  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Sub:i\d+>>  Sub [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Sub>>,<<Clp2>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<Clp1>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Max>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satSubSShort(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>>      outer_loop:none
-  public static void satSubSShort(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      c[i] = (short) Math.max(Math.min(a[i] - b[i], 32767), -32768);
-    }
-  }
-
-  //
-  // Single clipping signed 8-bit saturation.
-  //
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satAddPConstSByte(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
-  public static void satAddPConstSByte(byte[] a, byte[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (byte) Math.min(a[i] + 15, 127);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satAddNConstSByte(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
-  public static void satAddNConstSByte(byte[] a, byte[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (byte) Math.max(a[i] - 15, -128);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSByte(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
-  public static void satSubPConstSByte(byte[] a, byte[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (byte) Math.min(15 - a[i], 127);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSByte(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
-  public static void satSubNConstSByte(byte[] a, byte[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (byte) Math.max(-15 - a[i], -128);
-    }
-  }
-
-  //
-  // Single clipping signed 16-bit saturation.
-  //
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satAddPConstSShort(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
-  public static void satAddPConstSShort(short[] a, short[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (short) Math.min(a[i] + 15, 32767);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satAddNConstSShort(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
-  public static void satAddNConstSShort(short[] a, short[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (short) Math.max(a[i] - 15, -32768);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSShort(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
-  public static void satSubPConstSShort(short[] a, short[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (short) Math.min(15 - a[i], 32767);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSShort(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
-  public static void satSubNConstSShort(short[] a, short[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (short) Math.max(-15 - a[i], -32768);
-    }
-  }
-
-  //
-  // Alternatives 8-bit clipping.
-  //
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatAddConst(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
-  public static void usatAddConst(byte[] a, byte[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (byte) Math.min((a[i] & 0xff) + $inline$p15(), 255);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatAddConstAlt(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
-  public static void usatAddConstAlt(byte[] a, byte[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (byte) Math.min((a[i] & 0xff) - $inline$m15(), 255);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
-  public static void usatSubConst(byte[] a, byte[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (byte) Math.max((a[i] & 0xff) - $inline$p15(), 0);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(byte[], byte[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
-  public static void usatSubConstAlt(byte[] a, byte[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      b[i] = (byte) Math.max((a[i] & 0xff) + $inline$m15(), 0);
-    }
-  }
-
-  //
-  // Alternatives 16-bit clipping.
-  //
-
-  /// CHECK-START: void Main.satAlt1(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768                   loop:none
-  /// CHECK-DAG: <<Clp2:i\d+>> IntConstant  32767                   loop:none
-  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:i\d+>>  Add [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Add>>,<<Clp2>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<Clp1>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Max>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satAlt1(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void satAlt1(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      int s = a[i] + b[i];
-      if (s > 32767) {
-        s = 32767;
-      }
-      if (s < -32768) {
-        s = -32768;
-      }
-      c[i] = (short) s;
-    }
-  }
-
-  /// CHECK-START: void Main.satAlt2(short[], short[], short[]) loop_optimization (before)
-  /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768                   loop:none
-  /// CHECK-DAG: <<Clp2:i\d+>> IntConstant  32767                   loop:none
-  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>]     loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:i\d+>>  Add [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Add>>,<<Clp1>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Max>>,<<Clp2>>]               loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Min>>]             loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-START-{ARM,ARM64}: void Main.satAlt2(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void satAlt2(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      int s = a[i] + b[i];
-      if (s > 32767) {
-        s = 32767;
-      } else if (s < -32768) {
-        s = -32768;
-      }
-      c[i] = (short) s;
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satAlt3(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void satAlt3(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      int s = a[i] + b[i];
-      s = (s > 32767) ? 32767 : ((s < -32768) ? -32768 : s);
-      c[i] = (short) s;
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatAlt1(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void usatAlt1(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      int t = (0xffff & a[i]) + (0xffff & b[i]);
-      c[i] = (short) (t <= 65535 ? t : 65535);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatAlt2(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void usatAlt2(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      int t = (a[i] & 0xffff) + (b[i] & 0xffff);
-      c[i] = (short) (t < 65535 ? t : 65535);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatAlt3(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void usatAlt3(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      int x = (a[i] & 0xffff);
-      int y = (b[i] & 0xffff);
-      int t = y + x ;
-      if (t >= 65535) t = 65535;
-      c[i] = (short) t;
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatAlt4(short[], short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>>      outer_loop:none
-  public static void usatAlt4(short[] a, short[] b, short[] c) {
-    int n = Math.min(a.length, Math.min(b.length, c.length));
-    for (int i = 0; i < n; i++) {
-      int x = (a[i] & 0xffff);
-      int y = (b[i] & 0xffff);
-      int t = y + x ;
-      if (t > 65535) t = 65535;
-      c[i] = (short) t;
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.satRedundantClip(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none
-  public static void satRedundantClip(short[] a, short[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      // Max clipping redundant.
-      b[i] = (short) Math.max(Math.min(a[i] + 15, 32767), -32768 + 15);
-    }
-  }
-
-  /// CHECK-START: void Main.satNonRedundantClip(short[], short[]) loop_optimization (after)
-  /// CHECK-NOT: VecSaturationAdd
-  public static void satNonRedundantClip(short[] a, short[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      // Max clipping not redundant (one off).
-      b[i] = (short) Math.max(Math.min(a[i] + 15, 32767), -32768 + 16);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
-  public static void usatSubConst(short[] a, short[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      int t = a[i] & 0xffff;
-      int s = t - $inline$p15();
-      b[i] = (short)(s > 0 ? s : 0);
-    }
-  }
-
-  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(short[], short[]) loop_optimization (after)
-  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none
-  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none
-  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
-  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none
-  public static void usatSubConstAlt(short[] a, short[] b) {
-    int n = Math.min(a.length, b.length);
-    for (int i = 0; i < n; i++) {
-      int t = a[i] & 0xffff;
-      int s = t + $inline$m15();
-      b[i] = (short)(s > 0 ? s : 0);
-    }
-  }
-
-  //
-  // Test drivers.
-  //
-
-  private static void test08Bit() {
-    // Use cross-values to test all cases.
-    int n = 256;
-    int m = n * n;
-    int k = 0;
-    byte[] b1 = new byte[m];
-    byte[] b2 = new byte[m];
-    for (int i = 0; i < n; i++) {
-      for (int j = 0; j < n; j++) {
-        b1[k] = (byte) i;
-        b2[k] = (byte) j;
-        k++;
-      }
-    }
-    // Tests.
-    byte[] out = new byte[m];
-    satAddUByte(b1, b2, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.min((b1[i] & 0xff) + (b2[i] & 0xff), 255);
-      expectEquals(e, out[i]);
-    }
-    satAddSByte( b1, b2, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.max(Math.min(b1[i] + b2[i], 127), -128);
-      expectEquals(e, out[i]);
-    }
-    satSubUByte(b1, b2, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.max((b1[i] & 0xff) - (b2[i] & 0xff), 0);
-      expectEquals(e, out[i]);
-    }
-    satSubSByte(b1, b2, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.max(Math.min(b1[i] - b2[i], 127), -128);
-      expectEquals(e, out[i]);
-    }
-    // Single clipping.
-    satAddPConstSByte(b1, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.min(b1[i] + 15, 127);
-      expectEquals(e, out[i]);
-    }
-    satAddNConstSByte(b1, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.max(b1[i] - 15, -128);
-      expectEquals(e, out[i]);
-    }
-    satSubPConstSByte(b1, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.min(15 - b1[i], 127);
-      expectEquals(e, out[i]);
-    }
-    satSubNConstSByte(b1, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.max(-15 - b1[i], -128);
-      expectEquals(e, out[i]);
-    }
-    // Alternatives.
-    usatAddConst(b1, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255);
-      expectEquals(e, out[i]);
-    }
-    usatAddConstAlt(b1, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255);
-      expectEquals(e, out[i]);
-    }
-    usatSubConst(b1, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0);
-      expectEquals(e, out[i]);
-    }
-    usatSubConstAlt(b1, out);
-    for (int i = 0; i < m; i++) {
-      byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0);
-      expectEquals(e, out[i]);
-    }
-  }
-
-  private static void test16Bit() {
-    // Use cross-values to test interesting cases.
-    short[] interesting = {
-      (short) 0x0000,
-      (short) 0x0001,
-      (short) 0x0002,
-      (short) 0x0003,
-      (short) 0x0004,
-      (short) 0x007f,
-      (short) 0x0080,
-      (short) 0x00ff,
-      (short) 0x7f00,
-      (short) 0x7f7f,
-      (short) 0x7f80,
-      (short) 0x7fff,
-      (short) 0x8000,
-      (short) 0x807f,
-      (short) 0x8080,
-      (short) 0x80ff,
-      (short) 0xff00,
-      (short) 0xff7f,
-      (short) 0xff80,
-      (short) 0xffff,
-    };
-    int n = interesting.length;
-    int m = n * n;
-    short[] s1 = new short[m];
-    short[] s2 = new short[m];
-    int k = 0;
-    for (int i = 0; i < n; i++) {
-      for (int j = 0; j < n; j++) {
-        s1[k] = interesting[i];
-        s2[k] = interesting[j];
-        k++;
-      }
-    }
-    // Tests.
-    short[] out = new short[m];
-    satAddUShort(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.min((s1[i] & 0xffff) + (s2[i] & 0xffff), 65535);
-      expectEquals(e, out[i]);
-    }
-    satAddSShort(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max(Math.min(s1[i] + s2[i], 32767), -32768);
-      expectEquals(e, out[i]);
-    }
-    satSubUShort(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max((s1[i] & 0xffff) - (s2[i] & 0xffff), 0);
-      expectEquals(e, out[i]);
-    }
-    satSubSShort(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max(Math.min(s1[i] - s2[i], 32767), -32768);
-      expectEquals(e, out[i]);
-    }
-    // Single clipping.
-    satAddPConstSShort(s1, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.min(s1[i] + 15, 32767);
-      expectEquals(e, out[i]);
-    }
-    satAddNConstSShort(s1, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max(s1[i] - 15, -32768);
-      expectEquals(e, out[i]);
-    }
-    satSubPConstSShort(s1, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.min(15 - s1[i], 32767);
-      expectEquals(e, out[i]);
-    }
-    satSubNConstSShort(s1, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max(-15 - s1[i], -32768);
-      expectEquals(e, out[i]);
-    }
-    // Alternatives.
-    satAlt1(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max(Math.min(s1[i] + s2[i], 32767), -32768);
-      expectEquals(e, out[i]);
-    }
-    satAlt2(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max(Math.min(s1[i] + s2[i], 32767), -32768);
-      expectEquals(e, out[i]);
-    }
-    satAlt3(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max(Math.min(s1[i] + s2[i], 32767), -32768);
-      expectEquals(e, out[i]);
-    }
-    usatAlt1(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.min((s1[i] & 0xffff) + (s2[i] & 0xffff), 65535);
-      expectEquals(e, out[i]);
-    }
-    usatAlt2(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.min((s1[i] & 0xffff) + (s2[i] & 0xffff), 65535);
-      expectEquals(e, out[i]);
-    }
-    usatAlt3(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.min((s1[i] & 0xffff) + (s2[i] & 0xffff), 65535);
-      expectEquals(e, out[i]);
-    }
-    usatAlt4(s1, s2, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.min((s1[i] & 0xffff) + (s2[i] & 0xffff), 65535);
-      expectEquals(e, out[i]);
-    }
-    satRedundantClip(s1, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.min(s1[i] + 15, 32767);
-      expectEquals(e, out[i]);
-    }
-    satNonRedundantClip(s1, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max(Math.min(s1[i] + 15, 32767), -32752);
-      expectEquals(e, out[i]);
-    }
-    usatSubConst(s1, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max((s1[i] & 0xffff) - 15, 0);
-      expectEquals(e, out[i]);
-    }
-    usatSubConstAlt(s1, out);
-    for (int i = 0; i < m; i++) {
-      short e = (short) Math.max((s1[i] & 0xffff) - 15, 0);
-      expectEquals(e, out[i]);
-    }
-  }
-
-  public static void main(String[] args) {
-    test08Bit();
-    test16Bit();
-    System.out.println("passed");
-  }
-
-  private static void expectEquals(int expected, int result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
-}
diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java
index 48de1da..abf8c27 100644
--- a/test/679-checker-minmax/src/Main.java
+++ b/test/679-checker-minmax/src/Main.java
@@ -99,211 +99,211 @@
   // Different types.
   //
 
-  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Min:i\d+>> Min
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int min1(int a, int b) {
     return a < b ? a : b;
   }
 
-  /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> GreaterThan [<<Op1:i\d+>>,<<Op2:i\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Min:i\d+>> Min
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int min2(int a, int b) {
     return a <= b ? a : b;
   }
 
-  /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Min:i\d+>> Min
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int min3(int a, int b) {
     return a > b ? b : a;
   }
 
-  /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:i\d+>>,<<Op2:i\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Min:i\d+>> Min
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int min4(int a, int b) {
     return a >= b ? b : a;
   }
 
-  /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:s\d+>>,<<Op2:s\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Min:i\d+>> Min
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int min5(short a, short b) {
     return a >= b ? b : a;
   }
 
-  /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:b\d+>>,<<Op2:b\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Min:i\d+>> Min
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int min6(byte a, byte b) {
     return a >= b ? b : a;
   }
 
-  /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:j\d+>>,<<Op2:j\d+>>]
   /// CHECK-DAG: <<Sel:j\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Min:j\d+>> Min
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static long min7(long a, long b) {
     return a >= b ? b : a;
   }
 
-  /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Max:i\d+>> Max
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int max1(int a, int b) {
     return a < b ? b : a;
   }
 
-  /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> GreaterThan [<<Op1:i\d+>>,<<Op2:i\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Max:i\d+>> Max
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int max2(int a, int b) {
     return a <= b ? b : a;
   }
 
-  /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Max:i\d+>> Max
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int max3(int a, int b) {
     return a > b ? a : b;
   }
 
-  /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:i\d+>>,<<Op2:i\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Max:i\d+>> Max
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int max4(int a, int b) {
     return a >= b ? a : b;
   }
 
-  /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:s\d+>>,<<Op2:s\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Max:i\d+>> Max
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int max5(short a, short b) {
     return a >= b ? a : b;
   }
 
-  /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:b\d+>>,<<Op2:b\d+>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Max:i\d+>> Max
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int max6(byte a, byte b) {
     return a >= b ? a : b;
   }
 
-  /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:j\d+>>,<<Op2:j\d+>>]
   /// CHECK-DAG: <<Sel:j\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Max:j\d+>> Max
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static long max7(long a, long b) {
     return a >= b ? a : b;
@@ -313,18 +313,18 @@
   // Complications.
   //
 
-  /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Ar1:i\d+>> ArrayGet [{{l\d+}},{{i\d+}}]
   /// CHECK-DAG: <<Ar2:i\d+>> ArrayGet [{{l\d+}},{{i\d+}}]
   /// CHECK-DAG: <<Cnd:z\d+>> GreaterThan [<<Ar1>>,<<Ar2>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Ar1>>,<<Ar2>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Min:i\d+>> Min
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int min0(int[] a, int[] b) {
     // Repeat of array references needs finding the common subexpressions
@@ -332,18 +332,18 @@
     return a[0] <= b[0] ? a[0] : b[0];
   }
 
-  /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Ar1:i\d+>> ArrayGet [{{l\d+}},{{i\d+}}]
   /// CHECK-DAG: <<Ar2:i\d+>> ArrayGet [{{l\d+}},{{i\d+}}]
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Ar1>>,<<Ar2>>]
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Ar1>>,<<Ar2>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Max:i\d+>> Max
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int max0(int[] a, int[] b) {
     // Repeat of array references needs finding the common subexpressions
@@ -351,7 +351,7 @@
     return a[0] >= b[0] ? a[0] : b[0];
   }
 
-  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
   /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
   /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
@@ -361,7 +361,7 @@
   /// CHECK-DAG: <<Sel2:i\d+>> Select [<<M100>>,<<Sel1>>,<<Cnd2>>]
   /// CHECK-DAG:               Return [<<Sel2>>]
   //
-  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
   /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
   /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
@@ -369,7 +369,7 @@
   /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<M100>>]
   /// CHECK-DAG:               Return [<<Max>>]
   //
-  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:               Select
   public static int minmax1(int x) {
     // Simple if-if gives clean select sequence.
@@ -382,7 +382,7 @@
     return x;
   }
 
-  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
   /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
   /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
@@ -392,7 +392,7 @@
   /// CHECK-DAG: <<Sel2:i\d+>> Select [<<P100>>,<<Sel1>>,<<Cnd1>>]
   /// CHECK-DAG:               Return [<<Sel2>>]
   //
-  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
   /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
   /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
@@ -400,7 +400,7 @@
   /// CHECK-DAG: <<Min:i\d+>>  Min [<<Max>>,<<P100>>]
   /// CHECK-DAG:               Return [<<Min>>]
   //
-  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:               Select
   public static int minmax2(int x) {
     // Simple if-else requires inspecting bounds of resulting selects.
@@ -412,7 +412,7 @@
     return x;
   }
 
-  /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
   /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
   /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
@@ -420,13 +420,13 @@
   /// CHECK-DAG: <<Min:i\d+>>  Min [<<Max>>,<<P100>>]
   /// CHECK-DAG:               Return [<<Min>>]
   //
-  /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:               Select
   public static int minmax3(int x) {
     return (x > 100) ? 100 : ((x < -100) ? -100 : x);
   }
 
-  /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:i\d+>>  ParameterValue
   /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
   /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
@@ -434,7 +434,7 @@
   /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<M100>>]
   /// CHECK-DAG:               Return [<<Max>>]
   //
-  /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:               Select
   public static int minmax4(int x) {
     return (x < -100) ? -100 : ((x > 100) ? 100 : x);
@@ -454,7 +454,7 @@
   /// CHECK-DAG: <<Add5:i\d+>> Add                [<<Sel2>>,<<Add4>>]
   /// CHECK-DAG:               Return             [<<Add5>>]
   //
-  /// CHECK-START: int Main.minmaxCSEScalar(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmaxCSEScalar(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par1:i\d+>> ParameterValue
   /// CHECK-DAG: <<Par2:i\d+>> ParameterValue
   /// CHECK-DAG: <<Max:i\d+>>  Max    [<<Par1>>,<<Par2>>]
@@ -490,7 +490,7 @@
   /// CHECK-DAG: <<Add5:i\d+>> Add                [<<Sel2>>,<<Add4>>]
   /// CHECK-DAG:               Return             [<<Add5>>]
   //
-  /// CHECK-START: int Main.minmaxCSEArray(int[], int[]) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmaxCSEArray(int[], int[]) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Arr1:i\d+>> ArrayGet
   /// CHECK-DAG: <<Arr2:i\d+>> ArrayGet
   /// CHECK-DAG: <<Max:i\d+>>  Max    [<<Arr1>>,<<Arr2>>]
@@ -512,7 +512,7 @@
     return t1 + t2 + t3 + t4 + t5 + t6;
   }
 
-  /// CHECK-START: int Main.minmaxCSEScalarAndCond(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.minmaxCSEScalarAndCond(int, int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par1:i\d+>> ParameterValue
   /// CHECK-DAG: <<Par2:i\d+>> ParameterValue
   /// CHECK-DAG: <<Max:i\d+>>  Max    [<<Par1>>,<<Par2>>]
diff --git a/test/681-checker-abs/src/Main.java b/test/681-checker-abs/src/Main.java
index 2b95a8d..00390c3 100644
--- a/test/681-checker-abs/src/Main.java
+++ b/test/681-checker-abs/src/Main.java
@@ -59,7 +59,7 @@
   // Types.
   //
 
-  /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0
   /// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Par>>,<<Zer>>]
@@ -67,18 +67,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Neg>>,<<Par>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int abs1(int a) {
     return a < 0 ? -a : a;
   }
 
-  /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0
   /// CHECK-DAG: <<Cnd:z\d+>> GreaterThan [<<Par>>,<<Zer>>]
@@ -86,18 +86,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Neg>>,<<Par>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int abs2(int a) {
     return a <= 0 ? -a : a;
   }
 
-  /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0
   /// CHECK-DAG: <<Cnd:z\d+>> LessThanOrEqual [<<Par>>,<<Zer>>]
@@ -105,18 +105,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int abs3(int a) {
     return a > 0 ? a : -a;
   }
 
-  /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Par>>,<<Zer>>]
@@ -124,18 +124,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int abs4(int a) {
     return a >= 0 ? a : -a;
   }
 
-  /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Par:s\d+>> ParameterValue
   /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Par>>,<<Zer>>]
@@ -143,18 +143,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:s\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int abs5(short a) {
     return a >= 0 ? a : -a;
   }
 
-  /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Par:b\d+>> ParameterValue
   /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Par>>,<<Zer>>]
@@ -162,18 +162,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:b\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int abs6(byte a) {
     return a >= 0 ? a : -a;
   }
 
-  /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Par:j\d+>> ParameterValue
   /// CHECK-DAG: <<Zer:j\d+>> LongConstant 0
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Par>>,<<Zer>>]
@@ -181,12 +181,12 @@
   /// CHECK-DAG: <<Sel:j\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:j\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:j\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static long abs7(long a) {
     return a >= 0 ? a : -a;
@@ -196,7 +196,7 @@
   // Complications.
   //
 
-  /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_inlining (before)
+  /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_gvn (before)
   /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0
   /// CHECK-DAG: <<Arr:i\d+>> ArrayGet [{{l\d+}},{{i\d+}}]
   /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Arr>>,<<Zer>>]
@@ -204,12 +204,12 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Arr>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Arr:i\d+>> ArrayGet [{{l\d+}},{{i\d+}}]
   /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Arr>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int abs0(int[] a) {
     return a[0] >= 0 ? a[0] : -a[0];
@@ -267,11 +267,11 @@
   /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_gvn (after)
   /// CHECK-DAG: <<Par:c\d+>> ParameterValue
   /// CHECK-DAG:              Return [<<Par>>]
   //
-  /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after)
+  /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
   /// CHECK-NOT:              Abs
   public static int zabs3(char a) {
diff --git a/test/708-jit-cache-churn/jit.cc b/test/708-jit-cache-churn/jit.cc
index 1284a87..1b80eb3 100644
--- a/test/708-jit-cache-churn/jit.cc
+++ b/test/708-jit-cache-churn/jit.cc
@@ -19,7 +19,7 @@
 #include "art_method.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/test/712-varhandle-invocations/src/SampleValues.java b/test/712-varhandle-invocations/src/SampleValues.java
new file mode 100644
index 0000000..79f4f19
--- /dev/null
+++ b/test/712-varhandle-invocations/src/SampleValues.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+/** Sample values for use in VarHandle tests. These are here to avoid repeatedly boxing which
+ * makes gcstress tests run slowly. */
+public class SampleValues {
+    public static final boolean[] PRIMITIVE_BOOLEANS = new boolean[] {true, false};
+
+    public static final Boolean[] BOOLEANS = new Boolean[] {true, false};
+
+    public static final byte[] PRIMITIVE_BYTES =
+            new byte[] {(byte) -128, (byte) -61, (byte) 7, (byte) 127, (byte) 33};
+
+    public static final Byte[] BYTES =
+            new Byte[] {(byte) -128, (byte) -61, (byte) 7, (byte) 127, (byte) 33};
+
+    public static final short[] PRIMITIVE_SHORTS =
+            new short[] {(short) -32768, (short) -384, (short) 32767, (short) 0xaa55};
+
+    public static final Short[] SHORTS =
+            new Short[] {(short) -32768, (short) -384, (short) 32767, (short) 0xaa55};
+
+    public static final char[] PRIMITIVE_CHARS =
+            new char[] {'A', '#', '$', 'Z', 't', 'c'};
+
+    public static final Character[] CHARACTERS =
+            new Character[] {'A', '#', '$', 'Z', 't', 'c'};
+
+    public static final int[] PRIMITIVE_INTS =
+            new int[] {-0x01234567, 0x7f6e5d4c, 0x12345678, 0x10215220, 42};
+
+    public static final Integer[] INTEGERS =
+            new Integer[] {-0x01234567, 0x7f6e5d4c, 0x12345678, 0x10215220, 42};
+
+    public static final long[] PRIMITIVE_LONGS =
+            new long[] {-0x0123456789abcdefl, 0x789abcdef0123456l, 0xfedcba9876543210l};
+
+    public static final Long[] LONGS =
+            new Long[] {-0x0123456789abcdefl, 0x789abcdef0123456l, 0xfedcba9876543210l};
+
+    public static final float[] PRIMITIVE_FLOATS =
+            new float[] {-7.77e23f, 1.234e-17f, 3.40e36f, -8.888e3f, 4.442e11f};
+
+    public static final Float[] FLOATS =
+            new Float[] {-7.77e23f, 1.234e-17f, 3.40e36f, -8.888e3f, 4.442e11f};
+
+    public static final double[] PRIMITIVE_DOUBLES =
+            new double[] {-1.0e-200, 1.11e200, 3.141, 1.1111, 6.022e23, 6.626e-34};
+
+    public static final Double[] DOUBLES =
+            new Double[] {-1.0e-200, 1.11e200, 3.141, 1.1111, 6.022e23, 6.626e-34};
+
+    public static boolean get_boolean(int index) {
+        return PRIMITIVE_BOOLEANS[index];
+    }
+
+    public static Boolean get_Boolean(int index) {
+        return BOOLEANS[index];
+    }
+
+    public static byte get_byte(int index) {
+        return PRIMITIVE_BYTES[index];
+    }
+
+    public static Byte get_Byte(int index) {
+        return BYTES[index];
+    }
+
+    public static short get_short(int index) {
+        return PRIMITIVE_SHORTS[index];
+    }
+
+    public static Short get_Short(int index) {
+        return SHORTS[index];
+    }
+
+    public static char get_char(int index) {
+        return PRIMITIVE_CHARS[index];
+    }
+
+    public static Character get_Character(int index) {
+        return CHARACTERS[index];
+    }
+
+    public static int get_int(int index) {
+        return PRIMITIVE_INTS[index];
+    }
+
+    public static Integer get_Integer(int index) {
+        return INTEGERS[index];
+    }
+
+    public static long get_long(int index) {
+        return PRIMITIVE_LONGS[index];
+    }
+
+    public static Long get_Long(int index) {
+        return LONGS[index];
+    }
+
+    public static float get_float(int index) {
+        return PRIMITIVE_FLOATS[index];
+    }
+
+    public static Float get_Float(int index) {
+        return FLOATS[index];
+    }
+
+    public static double get_double(int index) {
+        return PRIMITIVE_DOUBLES[index];
+    }
+
+    public static Double get_Double(int index) {
+        return DOUBLES[index];
+    }
+}
+
diff --git a/test/712-varhandle-invocations/src/VarHandleUnitTestCollector.java b/test/712-varhandle-invocations/src/VarHandleUnitTestCollector.java
index bc64c0c..5a69b54 100644
--- a/test/712-varhandle-invocations/src/VarHandleUnitTestCollector.java
+++ b/test/712-varhandle-invocations/src/VarHandleUnitTestCollector.java
@@ -19,30 +19,52 @@
 // Results collector for VarHandle Unit tests
 public final class VarHandleUnitTestCollector {
     private final PrintStream out = System.out;
+    private final boolean verbose = false;
 
     private int numberOfSuccesses;
     private int numberOfSkips;
     private int numberOfFailures;
+    private int consecutiveResults = 0;
+    private String current;
+    private long startMillis;
 
     public void start(String testName) {
-        out.print(testName);
-        out.print("...");
+        out.append(testName)
+                .append("...");
+        consecutiveResults = 0;
+        current = testName;
+        startMillis = System.currentTimeMillis();
+    }
+
+    private void printStatus(String status) {
+        out.print(status);
+        if (verbose) {
+            out.print('[');
+            out.print(System.currentTimeMillis() - startMillis);
+            out.print(']');
+        }
+        out.println();
     }
 
     public void skip() {
         numberOfSkips += 1;
-        out.println("SKIP");
+        printStatus("SKIP");
+        consecutiveResults++;
     }
 
     public void success() {
         numberOfSuccesses += 1;
-        out.println("OK");
+        printStatus("OK");
+        if (consecutiveResults++ > 1) {
+            throw new AssertionError("Oops: " + consecutiveResults);
+        }
     }
 
     public void fail(String errorMessage) {
         numberOfFailures += 1;
-        out.println("FAIL");
+        printStatus("FAIL");
         out.print(errorMessage);
+        consecutiveResults++;
     }
 
     public void printSummary() {
diff --git a/test/712-varhandle-invocations/util-src/generate_java.py b/test/712-varhandle-invocations/util-src/generate_java.py
index 9520b53..f535b40 100644
--- a/test/712-varhandle-invocations/util-src/generate_java.py
+++ b/test/712-varhandle-invocations/util-src/generate_java.py
@@ -757,7 +757,9 @@
 """)
     with io.StringIO() as body_text:
         compatible_types = types_that_widen_to(var_type)
-        for value_type in VALUE_TYPES:
+        incompatible_types = { RANDOM.choice(list(VALUE_TYPES - compatible_types)) }
+        test_types = compatible_types | incompatible_types
+        for value_type in test_types:
             print("try {", file=body_text)
             return_type = accessor.get_return_type(var_type)
             if return_type:
@@ -765,7 +767,7 @@
             print("vh.{0}(this".format(accessor.method_name), end="", file=body_text)
             num_args = accessor.get_number_of_var_type_arguments()
             for i in range(0, num_args):
-                print(", {0}({1})".format(value_type.boxing_method(), value_type.examples[i]), end="", file=body_text)
+                print(", SampleValues.get_{0}({1})".format(value_type.boxed_type, i), end="", file=body_text)
             print(");", file=body_text)
             if value_type in compatible_types:
                 print("   assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode),
@@ -817,7 +819,9 @@
     with io.StringIO() as body_text:
         return_type = accessor.get_return_type(var_type)
         compatible_types = { return_type }
-        for value_type in VALUE_TYPES:
+        incompatible_types = { RANDOM.choice(list(VALUE_TYPES - compatible_types)) }
+        test_types = compatible_types | incompatible_types
+        for value_type in test_types:
             print("try {", file=body_text)
             print("{0} result = ({0}) ".format(value_type.boxed_type), end="", file=body_text)
             print("vh.{0}(this".format(accessor.method_name), end="", file=body_text)
diff --git a/test/678-checker-simd-saturation/build b/test/716-jli-jit-samples/build
old mode 100644
new mode 100755
similarity index 87%
copy from test/678-checker-simd-saturation/build
copy to test/716-jli-jit-samples/build
index d85147f..730a8a1
--- a/test/678-checker-simd-saturation/build
+++ b/test/716-jli-jit-samples/build
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# See b/65168732
-export USE_D8=false
+# make us exit on a failure
+set -e
 
-./default-build "$@"
+./default-build "$@" --experimental var-handles
diff --git a/test/716-jli-jit-samples/expected.txt b/test/716-jli-jit-samples/expected.txt
new file mode 100644
index 0000000..dc533f3
--- /dev/null
+++ b/test/716-jli-jit-samples/expected.txt
@@ -0,0 +1,3 @@
+JNI_OnLoad called
+MethodHandle OK
+VarHandle OK
diff --git a/test/716-jli-jit-samples/info.txt b/test/716-jli-jit-samples/info.txt
new file mode 100644
index 0000000..81a76f6
--- /dev/null
+++ b/test/716-jli-jit-samples/info.txt
@@ -0,0 +1,2 @@
+Test MethodHandle and VarHandle invokes do not accumulate JIT samples
+(regression test for b/78151261).
diff --git a/test/716-jli-jit-samples/src-art/Main.java b/test/716-jli-jit-samples/src-art/Main.java
new file mode 100644
index 0000000..def6b9f
--- /dev/null
+++ b/test/716-jli-jit-samples/src-art/Main.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 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.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+    private static final int ITERATIONS = 100;
+
+    private static final VarHandle widgetIdVarHandle;
+
+    public static native int getHotnessCounter(Class<?> cls, String methodName);
+
+    public static class Widget {
+        public Widget(int id) {
+            this.id = id;
+        }
+
+        int getId() {
+            return id;
+        }
+
+        int id;
+    }
+
+    static {
+        try {
+            widgetIdVarHandle = MethodHandles.lookup().findVarHandle(Widget.class, "id", int.class);
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+    private static void assertEquals(int i1, int i2) {
+        if (i1 == i2) {
+            return;
+        }
+        throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+    }
+
+    private static void assertEquals(Object o, Object p) {
+        if (o == p) {
+            return;
+        }
+        if (o != null && p != null && o.equals(p)) {
+            return;
+        }
+        throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+    }
+
+    private static void fail() {
+        System.out.println("fail");
+        Thread.dumpStack();
+    }
+
+    private static void fail(String message) {
+        System.out.println("fail: " + message);
+        Thread.dumpStack();
+    }
+
+    private static void testMethodHandleCounters() throws Throwable {
+        for (int i = 0; i < ITERATIONS; ++i) {
+            // Regular MethodHandle invocations
+            MethodHandle mh =
+                    MethodHandles.lookup()
+                            .findConstructor(
+                                    Widget.class, MethodType.methodType(void.class, int.class));
+            Widget w = (Widget) mh.invoke(3);
+            w = (Widget) mh.invokeExact(3);
+            assertEquals(0, getHotnessCounter(MethodHandle.class, "invoke"));
+            assertEquals(0, getHotnessCounter(MethodHandle.class, "invokeExact"));
+
+            // Reflective MethodHandle invocations
+            String[] methodNames = {"invoke", "invokeExact"};
+            for (String methodName : methodNames) {
+                Method invokeMethod = MethodHandle.class.getMethod(methodName, Object[].class);
+                MethodHandle instance =
+                        MethodHandles.lookup()
+                                .findVirtual(
+                                        Widget.class, "getId", MethodType.methodType(int.class));
+                try {
+                    invokeMethod.invoke(instance, new Object[] {new Object[] {}});
+                    fail();
+                } catch (InvocationTargetException ite) {
+                    assertEquals(ite.getCause().getClass(), UnsupportedOperationException.class);
+                }
+            }
+            assertEquals(0, getHotnessCounter(MethodHandle.class, "invoke"));
+            assertEquals(0, getHotnessCounter(MethodHandle.class, "invokeExact"));
+        }
+
+        System.out.println("MethodHandle OK");
+    }
+
+    private static void testVarHandleCounters() throws Throwable {
+        Widget w = new Widget(0);
+        for (int i = 0; i < ITERATIONS; ++i) {
+            // Regular accessor invocations
+            widgetIdVarHandle.set(w, i);
+            assertEquals(i, widgetIdVarHandle.get(w));
+            assertEquals(0, getHotnessCounter(VarHandle.class, "set"));
+            assertEquals(0, getHotnessCounter(VarHandle.class, "get"));
+
+            // Reflective accessor invocations
+            for (String accessorName : new String[] {"get", "set"}) {
+                Method setMethod = VarHandle.class.getMethod(accessorName, Object[].class);
+                try {
+                    setMethod.invoke(widgetIdVarHandle, new Object[] {new Object[0]});
+                    fail();
+                } catch (InvocationTargetException ite) {
+                    assertEquals(ite.getCause().getClass(), UnsupportedOperationException.class);
+                }
+            }
+            assertEquals(0, getHotnessCounter(VarHandle.class, "set"));
+            assertEquals(0, getHotnessCounter(VarHandle.class, "get"));
+        }
+        System.out.println("VarHandle OK");
+    }
+
+    public static void main(String[] args) throws Throwable {
+        System.loadLibrary(args[0]);
+        testMethodHandleCounters();
+        testVarHandleCounters();
+    }
+}
diff --git a/test/900-hello-plugin/load_unload.cc b/test/900-hello-plugin/load_unload.cc
index cab0abf..7121d10 100644
--- a/test/900-hello-plugin/load_unload.cc
+++ b/test/900-hello-plugin/load_unload.cc
@@ -21,7 +21,7 @@
 #include <android-base/macros.h>
 
 #include "art_method-inl.h"
-#include "java_vm_ext.h"
+#include "jni/java_vm_ext.h"
 #include "runtime.h"
 
 namespace art {
diff --git a/test/904-object-allocation/src/art/Test904.java b/test/904-object-allocation/src/art/Test904.java
index fda8985..a2848fb 100644
--- a/test/904-object-allocation/src/art/Test904.java
+++ b/test/904-object-allocation/src/art/Test904.java
@@ -47,7 +47,8 @@
     // Enable actual logging callback.
     setupObjectAllocCallback(true);
 
-    System.out.println(Arrays.toString(getTrackingEventMessages()));
+    System.out.println(Arrays.toString(getTrackingEventMessages(
+            new Thread[] { Thread.currentThread(), })));
 
     enableAllocationTracking(null, true);
 
@@ -66,22 +67,25 @@
 
     l.add(new Byte((byte)0));
 
-    System.out.println(Arrays.toString(getTrackingEventMessages()));
+    System.out.println(Arrays.toString(getTrackingEventMessages(
+            new Thread[] { Thread.currentThread(), })));
     System.out.println("Tracking on same thread");
 
-    testThread(l, true, true);
+    Thread test_thread = testThread(l, true, true);
 
     l.add(new Byte((byte)0));
 
-    System.out.println(Arrays.toString(getTrackingEventMessages()));
+    System.out.println(Arrays.toString(getTrackingEventMessages(
+            new Thread[] { Thread.currentThread(), test_thread, })));
     System.out.println("Tracking on same thread, not disabling tracking");
 
-    testThread(l, true, false);
+    test_thread = testThread(l, true, false);
 
-    System.out.println(Arrays.toString(getTrackingEventMessages()));
+    System.out.println(Arrays.toString(getTrackingEventMessages(
+            new Thread[] { Thread.currentThread(), test_thread, })));
     System.out.println("Tracking on different thread");
 
-    testThread(l, false, true);
+    test_thread = testThread(l, false, true);
 
     l.add(new Byte((byte)0));
 
@@ -89,12 +93,13 @@
     // check that shutdown works correctly.
     setupObjectAllocCallback(false);
 
-    System.out.println(Arrays.toString(getTrackingEventMessages()));
+    System.out.println(Arrays.toString(getTrackingEventMessages(
+            new Thread[] { Thread.currentThread(), test_thread, })));
 
     enableAllocationTracking(null, true);
   }
 
-  private static void testThread(final ArrayList<Object> l, final boolean sameThread,
+  private static Thread testThread(final ArrayList<Object> l, final boolean sameThread,
       final boolean disableTracking) throws Exception {
     final SimpleBarrier startBarrier = new SimpleBarrier(1);
     final SimpleBarrier trackBarrier = new SimpleBarrier(1);
@@ -126,6 +131,7 @@
     trackBarrier.dec();
 
     t.join();
+    return t;
   }
 
   private static class SimpleBarrier {
@@ -149,5 +155,5 @@
 
   private static native void setupObjectAllocCallback(boolean enable);
   private static native void enableAllocationTracking(Thread thread, boolean enable);
-  private static native String[] getTrackingEventMessages();
+  private static native String[] getTrackingEventMessages(Thread[] threads);
 }
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index 9d2592a..f7296b1 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -35,6 +35,8 @@
 namespace art {
 namespace Test904ObjectAllocation {
 
+static JavaVM* vm;
+
 static std::string GetClassName(JNIEnv* jni_env, jclass cls) {
   ScopedLocalRef<jclass> class_class(jni_env, jni_env->GetObjectClass(cls));
   jmethodID mid = jni_env->GetMethodID(class_class.get(), "getName", "()Ljava/lang/String;");
@@ -44,12 +46,45 @@
   return utf_chars.c_str();
 }
 
+template <typename T>
+class ScopedGlobalRef {
+ public:
+  ScopedGlobalRef(JNIEnv* env, T obj) : obj_(env->NewGlobalRef(obj)) {}
+  ScopedGlobalRef(const ScopedGlobalRef<T>& src) noexcept
+      : obj_(GetEnv()->NewGlobalRef(src.obj_)) {}
+  ScopedGlobalRef(ScopedGlobalRef<T>&& src) noexcept : obj_(src.obj_) {
+    src.obj_ = nullptr;
+  }
+
+  ~ScopedGlobalRef() {
+    GetEnv()->DeleteGlobalRef(obj_);
+  }
+
+  T Get(JNIEnv* env) const {
+    return env->NewLocalRef(obj_);
+  }
+
+ private:
+  JNIEnv* GetEnv() const {
+    JNIEnv* env = nullptr;
+    CHECK_EQ(vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6), 0);
+    return env;
+  }
+
+  jobject obj_;
+};
+
+struct EventLog {
+  std::string msg_;
+  ScopedGlobalRef<jthread> thr_;
+};
+
 static std::mutex gEventsMutex;
-static std::vector<std::string> gEvents;
+static std::vector<EventLog> gEvents;
 
 static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED,
                                     JNIEnv* jni_env,
-                                    jthread thread ATTRIBUTE_UNUSED,
+                                    jthread thread,
                                     jobject object,
                                     jclass object_klass,
                                     jlong size) {
@@ -58,14 +93,16 @@
   std::string object_klass_descriptor2 = GetClassName(jni_env, object_klass2.get());
 
   std::lock_guard<std::mutex> guard(gEventsMutex);
-  gEvents.push_back(android::base::StringPrintf("ObjectAllocated type %s/%s size %zu",
-                                                object_klass_descriptor.c_str(),
-                                                object_klass_descriptor2.c_str(),
-                                                static_cast<size_t>(size)));
+  gEvents.push_back({android::base::StringPrintf("ObjectAllocated type %s/%s size %zu",
+                                                 object_klass_descriptor.c_str(),
+                                                 object_klass_descriptor2.c_str(),
+                                                 static_cast<size_t>(size)),
+                     ScopedGlobalRef<jthread>(jni_env, thread)});
 }
 
 extern "C" JNIEXPORT void JNICALL Java_art_Test904_setupObjectAllocCallback(
     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
+  env->GetJavaVM(&vm);
   jvmtiEventCallbacks callbacks;
   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
   callbacks.VMObjectAlloc = enable ? ObjectAllocated : nullptr;
@@ -84,13 +121,32 @@
 }
 
 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test904_getTrackingEventMessages(
-    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobjectArray threads) {
   std::lock_guard<std::mutex> guard(gEventsMutex);
+  std::vector<std::string> real_events;
+  std::vector<jthread> thread_lst;
+  jint nthreads = env->GetArrayLength(threads);
+  {
+    env->PushLocalFrame(nthreads + 1);
+    for (jint i = 0; i < nthreads; i++) {
+      thread_lst.push_back(reinterpret_cast<jthread>(env->GetObjectArrayElement(threads, i)));
+    }
+    for (const EventLog& ev : gEvents) {
+      ScopedLocalRef<jthread> thr(env, ev.thr_.Get(env));
+      for (jthread req_thread : thread_lst) {
+        if (env->IsSameObject(req_thread, thr.get())) {
+          real_events.push_back(ev.msg_);
+          break;
+        }
+      }
+    }
+    env->PopLocalFrame(nullptr);
+  }
   jobjectArray ret = CreateObjectArray(env,
-                                       static_cast<jint>(gEvents.size()),
+                                       static_cast<jint>(real_events.size()),
                                        "java/lang/String",
                                        [&](jint i) {
-    return env->NewStringUTF(gEvents[i].c_str());
+    return env->NewStringUTF(real_events[i].c_str());
   });
   gEvents.clear();
   return ret;
diff --git a/test/913-heaps/expected_d8.diff b/test/913-heaps/expected_d8.diff
index 3ea3c0d..1ad0cbd 100644
--- a/test/913-heaps/expected_d8.diff
+++ b/test/913-heaps/expected_d8.diff
@@ -10,8 +10,8 @@
 51c50,51
 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
 ---
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1]
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
 102,103c102
 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1]
 < root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
@@ -24,8 +24,8 @@
 117c116,117
 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
 ---
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1]
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
 162c162
 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1]
 ---
@@ -37,8 +37,8 @@
 179c179,180
 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
 ---
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1]
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
 201,202c202
 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1]
 < root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
@@ -51,8 +51,8 @@
 248c248,249
 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
 ---
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1]
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
 292d292
 < root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
 347c347
@@ -66,5 +66,5 @@
 368c368,369
 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
 ---
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1]
-> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
+> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt
index bc943e3..bbaaedb 100644
--- a/test/979-const-method-handle/expected.txt
+++ b/test/979-const-method-handle/expected.txt
@@ -1,6 +1,9 @@
 (int,Integer,System)String
+repeatConstMethodType0((int,Integer,System)String)
+repeatConstMethodType1((LocalClass)void)
 Hello World! And Hello Zog
 Hello World! And Hello Zorba
 name is HoverFly
 2.718281828459045
+repeatConstMethodHandle()
 Attempting to set Math.E raised IAE
diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java
index 663814f..427ca7a 100644
--- a/test/979-const-method-handle/src/Main.java
+++ b/test/979-const-method-handle/src/Main.java
@@ -20,78 +20,146 @@
 import java.lang.invoke.MethodType;
 
 class Main {
+    /**
+     * Number of iterations run to attempt to trigger JIT compilation. These tests run on ART and
+     * the RI so they iterate rather than using the ART only native method ensureJitCompiled().
+     */
+    private static final int ITERATIONS_FOR_JIT = 12000;
+
+    /** A static field updated by method handle getters and setters. */
     private static String name = "default";
 
     private static void unreachable() {
         throw new Error("Unreachable");
     }
 
+    private static void assertEquals(Object expected, Object actual) {
+        if (!expected.equals(actual)) {
+            throw new AssertionError("Assertion failure: " + expected + " != " + actual);
+        }
+    }
+
+    private static class LocalClass {
+        public LocalClass() {}
+
+        private int field;
+    }
+
     @ConstantMethodType(
-        returnType = String.class,
-        parameterTypes = {int.class, Integer.class, System.class}
-    )
+            returnType = String.class,
+            parameterTypes = {int.class, Integer.class, System.class})
     private static MethodType methodType0() {
         unreachable();
         return null;
     }
 
+    @ConstantMethodType(
+            returnType = void.class,
+            parameterTypes = {LocalClass.class})
+    private static MethodType methodType1() {
+        unreachable();
+        return null;
+    }
+
+    private static void repeatConstMethodType0(MethodType expected) {
+        System.out.print("repeatConstMethodType0(");
+        System.out.print(expected);
+        System.out.println(")");
+        for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) {
+            MethodType actual = methodType0();
+            assertEquals(expected, actual);
+        }
+    }
+
+    private static void repeatConstMethodType1(MethodType expected) {
+        System.out.print("repeatConstMethodType1(");
+        System.out.print(expected);
+        System.out.println(")");
+        for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) {
+            MethodType actual = methodType1();
+            assertEquals(expected, actual);
+        }
+    }
+
     static void helloWorld(String who) {
         System.out.print("Hello World! And Hello ");
         System.out.println(who);
     }
 
     @ConstantMethodHandle(
-        kind = ConstantMethodHandle.INVOKE_STATIC,
-        owner = "Main",
-        fieldOrMethodName = "helloWorld",
-        descriptor = "(Ljava/lang/String;)V"
-    )
+            kind = ConstantMethodHandle.INVOKE_STATIC,
+            owner = "Main",
+            fieldOrMethodName = "helloWorld",
+            descriptor = "(Ljava/lang/String;)V")
     private static MethodHandle printHelloHandle() {
         unreachable();
         return null;
     }
 
     @ConstantMethodHandle(
-        kind = ConstantMethodHandle.STATIC_PUT,
-        owner = "Main",
-        fieldOrMethodName = "name",
-        descriptor = "Ljava/lang/String;"
-    )
+            kind = ConstantMethodHandle.STATIC_PUT,
+            owner = "Main",
+            fieldOrMethodName = "name",
+            descriptor = "Ljava/lang/String;")
     private static MethodHandle setNameHandle() {
         unreachable();
         return null;
     }
 
     @ConstantMethodHandle(
-        kind = ConstantMethodHandle.STATIC_GET,
-        owner = "java/lang/Math",
-        fieldOrMethodName = "E",
-        descriptor = "D"
-    )
+            kind = ConstantMethodHandle.STATIC_GET,
+            owner = "Main",
+            fieldOrMethodName = "name",
+            descriptor = "Ljava/lang/String;")
+    private static MethodHandle getNameHandle() {
+        unreachable();
+        return null;
+    }
+
+    @ConstantMethodHandle(
+            kind = ConstantMethodHandle.STATIC_GET,
+            owner = "java/lang/Math",
+            fieldOrMethodName = "E",
+            descriptor = "D")
     private static MethodHandle getMathE() {
         unreachable();
         return null;
     }
 
     @ConstantMethodHandle(
-        kind = ConstantMethodHandle.STATIC_PUT,
-        owner = "java/lang/Math",
-        fieldOrMethodName = "E",
-        descriptor = "D"
-    )
+            kind = ConstantMethodHandle.STATIC_PUT,
+            owner = "java/lang/Math",
+            fieldOrMethodName = "E",
+            descriptor = "D")
     private static MethodHandle putMathE() {
         unreachable();
         return null;
     }
 
+    private static void repeatConstMethodHandle() throws Throwable {
+        System.out.println("repeatConstMethodHandle()");
+        String[] values = {"A", "B", "C"};
+        for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) {
+            String value = values[i % values.length];
+            setNameHandle().invoke(value);
+            String actual = (String) getNameHandle().invokeExact();
+            assertEquals(value, actual);
+            assertEquals(value, name);
+        }
+    }
+
     public static void main(String[] args) throws Throwable {
         System.out.println(methodType0());
+        repeatConstMethodType0(
+                MethodType.methodType(String.class, int.class, Integer.class, System.class));
+        repeatConstMethodType1(MethodType.methodType(void.class, LocalClass.class));
         printHelloHandle().invokeExact("Zog");
         printHelloHandle().invokeExact("Zorba");
         setNameHandle().invokeExact("HoverFly");
         System.out.print("name is ");
         System.out.println(name);
         System.out.println(getMathE().invoke());
+        repeatConstMethodHandle();
         try {
             putMathE().invokeExact(Math.PI);
             unreachable();
diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt
index 7f64e23..6e16722 100644
--- a/test/988-method-trace/expected.txt
+++ b/test/988-method-trace/expected.txt
@@ -130,8 +130,10 @@
 ....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
 ...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
 ...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int)
+.....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
+.....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....<= public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
 ...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
 ...=> public java.lang.Error(java.lang.String)
 ....=> public java.lang.Throwable(java.lang.String)
@@ -231,8 +233,10 @@
 ....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
 ...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
 ...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int)
+.....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
+.....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....<= public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
 ...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
 ...=> public java.lang.Error(java.lang.String)
 ....=> public java.lang.Throwable(java.lang.String)
diff --git a/test/999-redefine-hiddenapi/api-blacklist.txt b/test/999-redefine-hiddenapi/api-blacklist.txt
new file mode 100644
index 0000000..63e37aa
--- /dev/null
+++ b/test/999-redefine-hiddenapi/api-blacklist.txt
@@ -0,0 +1,2 @@
+Lart/Test999;->foo()V
+Lart/Test999;->bar:I
diff --git a/test/678-checker-simd-saturation/build b/test/999-redefine-hiddenapi/build
similarity index 90%
rename from test/678-checker-simd-saturation/build
rename to test/999-redefine-hiddenapi/build
index d85147f..f4b029f 100644
--- a/test/678-checker-simd-saturation/build
+++ b/test/999-redefine-hiddenapi/build
@@ -14,7 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
+USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/999-redefine-hiddenapi/expected.txt b/test/999-redefine-hiddenapi/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/999-redefine-hiddenapi/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/999-redefine-hiddenapi/info.txt b/test/999-redefine-hiddenapi/info.txt
new file mode 100644
index 0000000..87bc30c
--- /dev/null
+++ b/test/999-redefine-hiddenapi/info.txt
@@ -0,0 +1 @@
+Tests that JVMTI class redefinition does not strip away hidden API access flags.
diff --git a/test/678-checker-simd-saturation/build b/test/999-redefine-hiddenapi/run
old mode 100644
new mode 100755
similarity index 83%
copy from test/678-checker-simd-saturation/build
copy to test/999-redefine-hiddenapi/run
index d85147f..c6e62ae
--- a/test/678-checker-simd-saturation/build
+++ b/test/999-redefine-hiddenapi/run
@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# Copyright 2018 The Android Open Source Project
+# Copyright 2016 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,7 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
+./default-run "$@" --jvmti
diff --git a/test/651-checker-simd-minmax/src/Main.java b/test/999-redefine-hiddenapi/src-ex/Test999.java
similarity index 70%
copy from test/651-checker-simd-minmax/src/Main.java
copy to test/999-redefine-hiddenapi/src-ex/Test999.java
index 9134dd1..97495c5 100644
--- a/test/651-checker-simd-minmax/src/Main.java
+++ b/test/999-redefine-hiddenapi/src-ex/Test999.java
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-public class Main {
-  public static void main(String[] args) {
-    ByteSimdMinMax.main();
-    CharSimdMinMax.main();
-    ShortSimdMinMax.main();
-    IntSimdMinMax.main();
-    LongSimdMinMax.main();
-    DoubleSimdMinMax.main();
-    FloatSimdMinMax.main();
+package art;
+
+public class Test999 {
+  public void foo() {
+    System.out.println("hello");
   }
+
+  public int bar = 42;
 }
diff --git a/test/651-checker-simd-minmax/src/Main.java b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
similarity index 70%
copy from test/651-checker-simd-minmax/src/Main.java
copy to test/999-redefine-hiddenapi/src-redefine/art/Test999.java
index 9134dd1..c1b838c 100644
--- a/test/651-checker-simd-minmax/src/Main.java
+++ b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-public class Main {
-  public static void main(String[] args) {
-    ByteSimdMinMax.main();
-    CharSimdMinMax.main();
-    ShortSimdMinMax.main();
-    IntSimdMinMax.main();
-    LongSimdMinMax.main();
-    DoubleSimdMinMax.main();
-    FloatSimdMinMax.main();
+package art;
+
+public class Test999 {
+  public void foo() {
+    System.out.println("Goodbye");
   }
+
+  public int bar = 64;
 }
diff --git a/test/999-redefine-hiddenapi/src-redefine/gen.sh b/test/999-redefine-hiddenapi/src-redefine/gen.sh
new file mode 100755
index 0000000..6948cbb
--- /dev/null
+++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2018 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.
+
+set -e
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+TMP=`mktemp -d`
+
+CLASS "art/Test999"
+
+(cd "$TMP" && javac -d "${TMP}" "$DIR/${CLASS}.java" && d8 --output . "$TMP/${CLASS}.class")
+
+echo '  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode('
+base64 "${TMP}/${CLASS}.class" | sed -E 's/^/    "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/'
+echo '  private static final byte[] DEX_BYTES = Base64.getDecoder().decode('
+base64 "${TMP}/classes.dex" | sed -E 's/^/    "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/'
+
+rm -rf "$TMP"
diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java
new file mode 100644
index 0000000..c6365ac
--- /dev/null
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 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.io.File;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+
+    // Run the initialization routine. This will enable hidden API checks in
+    // the runtime, in case they are not enabled by default.
+    init();
+
+    // Load the '-ex' APK and attach it to the boot class path.
+    appendToBootClassLoader(DEX_EXTRA);
+
+    // Find the test class in boot class loader and verify that its members are hidden.
+    Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER);
+    assertMethodIsHidden(klass, "before redefinition");
+    assertFieldIsHidden(klass, "before redefinition");
+
+    // Redefine the class using JVMTI.
+    art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE);
+    art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES);
+
+    // Verify that the class members are still hidden.
+    assertMethodIsHidden(klass, "after redefinition");
+    assertFieldIsHidden(klass, "after redefinition");
+  }
+
+  private static void assertMethodIsHidden(Class<?> klass, String msg) throws Exception {
+    try {
+      klass.getDeclaredMethod("foo");
+      // Unexpected. Should have thrown NoSuchMethodException.
+      throw new Exception("Method should not be accessible " + msg);
+    } catch (NoSuchMethodException ex) {
+      // Expected.
+    }
+  }
+
+  private static void assertFieldIsHidden(Class<?> klass, String msg) throws Exception {
+    try {
+      klass.getDeclaredField("bar");
+      // Unexpected. Should have thrown NoSuchFieldException.
+      throw new Exception("Field should not be accessible " + msg);
+    } catch (NoSuchFieldException ex) {
+      // Expected.
+    }
+  }
+
+  private static final String DEX_EXTRA =
+      new File(System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-ex.jar").getAbsolutePath();
+
+  private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+
+  // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
+  private static native void appendToBootClassLoader(String dexPath);
+  private static native void init();
+
+  /**
+   * base64 encoded class/dex file for
+   *
+   * public class Test999 {
+   *   public void foo() {
+   *     System.out.println("Goodbye");
+   *   }
+   *
+   *   public int bar = 64;
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" +
+    "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" +
+    "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" +
+    "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" +
+    "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" +
+    "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" +
+    "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" +
+    "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQD0dZ+IWxOi+cJDSWjfTnUerlZj1Lll3ONIAwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAQ" +
+    "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAUAgAANAEAAIYB" +
+    "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
+    "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
+    "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
+    "AAAAAAAAAAgAAAAAAAAAhwIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
+    "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
+    "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
+    "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
+    "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAFx+fkQ4eyJtaW4tYXBpIjox" +
+    "LCJzaGEtMSI6IjU2YzJlMzBmNTIzM2I4NDRmZjZkZGQ4N2ZiNTNkMzRmYjE3MjM3ZGYiLCJ2ZXJz" +
+    "aW9uIjoidjEuMi4xNS1kZXYifQAAAQEBAAEAgYAEtAIBAdQCAAAAAAAOAAAAAAAAAAEAAAAAAAAA" +
+    "AQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0" +
+    "AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAA" +
+    "AACGAQAAACAAAAEAAACHAgAAAxAAAAEAAACYAgAAABAAAAEAAACcAgAA");
+}
diff --git a/test/999-redefine-hiddenapi/src/art/Redefinition.java b/test/999-redefine-hiddenapi/src/art/Redefinition.java
new file mode 100644
index 0000000..1eec70b
--- /dev/null
+++ b/test/999-redefine-hiddenapi/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/Android.bp b/test/Android.bp
index b9312c8..7909bf8 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -63,6 +63,8 @@
         "libvixld-arm64",
         "libart-gtest",
         "libdexfiled",
+        "libprofiled",
+        "libartbased",
 
         "libbase",
         "libicuuc",
@@ -115,6 +117,8 @@
         "libartd",
         "libartd-compiler",
         "libdexfiled",
+        "libprofiled",
+        "libartbased",
     ],
     static_libs: [
         "libgtest",
@@ -146,12 +150,15 @@
     whole_static_libs: [
         "libart-compiler-gtest",
         "libart-runtime-gtest",
+        "libartbase-art-gtest",
         "libgtest",
     ],
     shared_libs: [
         "libartd",
         "libartd-compiler",
         "libdexfiled",
+        "libprofiled",
+        "libartbased",
         "libbase",
         "libbacktrace",
     ],
@@ -182,6 +189,8 @@
     shared_libs: [
         "libart",
         "libdexfile",
+        "libprofile",
+        "libartbase",
     ],
 }
 
@@ -195,6 +204,8 @@
     shared_libs: [
         "libartd",
         "libdexfiled",
+        "libprofiled",
+        "libartbased",
     ],
 }
 
@@ -315,6 +326,8 @@
     shared_libs: [
         "libart",
         "libdexfile",
+        "libprofile",
+        "libartbase",
     ],
 }
 
@@ -327,6 +340,8 @@
     shared_libs: [
         "libartd",
         "libdexfiled",
+        "libprofiled",
+        "libartbased",
     ],
 }
 
@@ -362,22 +377,27 @@
 }
 
 art_cc_defaults {
-    name: "libtistress-defaults",
+    name: "libtistress-srcs",
     defaults: ["libartagent-defaults"],
     srcs: [
         "ti-stress/stress.cc",
     ],
+    header_libs: ["libopenjdkjvmti_headers"],
+}
+
+art_cc_defaults {
+    name: "libtistress-defaults",
+    defaults: ["libtistress-srcs"],
     shared_libs: [
         "libbase",
         "slicer",
     ],
-    header_libs: ["libopenjdkjvmti_headers"],
 }
 
 art_cc_test_library {
     name: "libtistress",
     defaults: ["libtistress-defaults"],
-    shared_libs: ["libart"],
+    shared_libs: ["libartbase"],
 }
 
 art_cc_test_library {
@@ -386,7 +406,30 @@
         "art_debug_defaults",
         "libtistress-defaults",
     ],
-    shared_libs: ["libartd"],
+    shared_libs: ["libartbased"],
+}
+
+art_cc_defaults {
+    name: "libtistress-static-defaults",
+    defaults: ["libtistress-srcs"],
+    static_libs: art_static_dependencies + [
+        "slicer",
+    ],
+}
+
+art_cc_test_library {
+    name: "libtistresss",
+    defaults: ["libtistress-static-defaults"],
+    static_libs: ["libartbase"],
+}
+
+art_cc_test_library {
+    name: "libtistressds",
+    defaults: [
+        "art_debug_defaults",
+        "libtistress-static-defaults"
+    ],
+    static_libs: ["libartbased"],
 }
 
 cc_defaults {
@@ -415,6 +458,7 @@
         "154-gc-loop/heap_interface.cc",
         "167-visit-locks/visit_locks.cc",
         "169-threadgroup-jni/jni_daemon_thread.cc",
+        "172-app-image-twice/debug_print_class.cc",
         "1945-proxy-method-arguments/get_args.cc",
         "203-multi-checkpoint/multi_checkpoint.cc",
         "305-other-fault-handler/fault_handler.cc",
@@ -458,6 +502,8 @@
     shared_libs: [
         "libart",
         "libdexfile",
+        "libprofile",
+        "libartbase",
     ],
 }
 
@@ -470,6 +516,8 @@
     shared_libs: [
         "libartd",
         "libdexfiled",
+        "libprofiled",
+        "libartbased",
     ],
 }
 
diff --git a/test/651-checker-simd-minmax/src/Main.java b/test/HiddenApiSignatures/Interface.java
similarity index 70%
copy from test/651-checker-simd-minmax/src/Main.java
copy to test/HiddenApiSignatures/Interface.java
index 9134dd1..f141d09 100644
--- a/test/651-checker-simd-minmax/src/Main.java
+++ b/test/HiddenApiSignatures/Interface.java
@@ -14,14 +14,8 @@
  * limitations under the License.
  */
 
-public class Main {
-  public static void main(String[] args) {
-    ByteSimdMinMax.main();
-    CharSimdMinMax.main();
-    ShortSimdMinMax.main();
-    IntSimdMinMax.main();
-    LongSimdMinMax.main();
-    DoubleSimdMinMax.main();
-    FloatSimdMinMax.main();
-  }
+package mypackage.packagea;
+
+public interface Interface {
+    public void method();
 }
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 2203bdc..f89888b 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -25,12 +25,12 @@
 #include "instrumentation.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
-#include "jit/profile_compilation_info.h"
 #include "jit/profiling_info.h"
 #include "mirror/class-inl.h"
 #include "nativehelper/ScopedUtfChars.h"
 #include "oat_file.h"
 #include "oat_quick_method_header.h"
+#include "profile/profile_compilation_info.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
@@ -258,17 +258,23 @@
                                                              jclass,
                                                              jclass cls,
                                                              jstring method_name) {
-  ArtMethod* method = nullptr;
-  {
-    ScopedObjectAccess soa(Thread::Current());
-
-    ScopedUtfChars chars(env, method_name);
-    CHECK(chars.c_str() != nullptr);
-    method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
-        chars.c_str(), kRuntimePointerSize);
+  ScopedObjectAccess soa(Thread::Current());
+  ScopedUtfChars chars(env, method_name);
+  CHECK(chars.c_str() != nullptr);
+  ArtMethod* method =
+      soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(chars.c_str(),
+                                                                     kRuntimePointerSize);
+  if (method != nullptr) {
+    return method->GetCounter();
   }
 
-  return method->GetCounter();
+  method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(chars.c_str(),
+                                                                           kRuntimePointerSize);
+  if (method != nullptr) {
+    return method->GetCounter();
+  }
+
+  return std::numeric_limits<int32_t>::min();
 }
 
 extern "C" JNIEXPORT int JNICALL Java_Main_numberOfDeoptimizations(JNIEnv*, jclass) {
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index fd62737..192274e 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -20,7 +20,7 @@
 
 #include "base/mutex.h"
 #include "dex/dex_file-inl.h"
-#include "jni_internal.h"
+#include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
 #include "nth_caller_visitor.h"
 #include "oat_file.h"
diff --git a/test/etc/default-build b/test/etc/default-build
index 8bb898c..c61de0a 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -561,6 +561,11 @@
 if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then
   make_dex classes-ex
 
+  # Apply hiddenapi on the dex files if the test has API list file(s).
+  if [ ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then
+    make_hiddenapi classes-ex.dex
+  fi
+
   # quick shuffle so that the stored name is "classes.dex"
   mv classes.dex classes-1.dex
   mv classes-ex.dex classes.dex
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index c527754..1ba433e 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -13,6 +13,7 @@
 ARCHITECTURES_64="(arm64|x86_64|mips64|none)"
 ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
 BOOT_IMAGE=""
+CHROOT=
 COMPILE_FLAGS=""
 DALVIKVM="dalvikvm32"
 DEBUGGER="n"
@@ -80,11 +81,6 @@
 # The *hard* timeout where we really start trying to kill the dex2oat.
 DEX2OAT_RT_TIMEOUT="360" # 6 mins
 
-# if "y", set -Xstacktracedir and inform the test of its location. When
-# this is set, stack trace dumps (from signal 3) will be written to a file
-# under this directory instead of stdout.
-SET_STACK_TRACE_DUMP_DIR="n"
-
 # if "y", run 'sync' before dalvikvm to make sure all files from
 # build step (e.g. dex2oat) were finished writing.
 SYNC_BEFORE_RUN="n"
@@ -304,6 +300,10 @@
     elif [ "x$1" = "x--no-optimize" ]; then
         OPTIMIZE="n"
         shift
+    elif [ "x$1" = "x--chroot" ]; then
+        shift
+        CHROOT="$1"
+        shift
     elif [ "x$1" = "x--android-root" ]; then
         shift
         ANDROID_ROOT="$1"
@@ -364,9 +364,6 @@
     elif [ "x$1" = "x--random-profile" ]; then
         RANDOM_PROFILE="y"
         shift
-    elif [ "x$1" = "x--set-stack-trace-dump-dir" ]; then
-        SET_STACK_TRACE_DUMP_DIR="y"
-        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         exit 1
@@ -375,7 +372,8 @@
     fi
 done
 
-mkdir_locations=""
+# The DEX_LOCATION with the chroot prefix, if any.
+CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION"
 
 if [ "$USE_JVM" = "n" ]; then
     FLAGS="${FLAGS} ${ANDROID_FLAGS}"
@@ -383,14 +381,6 @@
         FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}"
         COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}"
     done
-
-    if [ "$SET_STACK_TRACE_DUMP_DIR" = "y" ]; then
-        # Note that DEX_LOCATION is used as a proxy for tmpdir throughout this
-        # file (it will be under the test specific folder).
-        mkdir_locations="${mkdir_locations} $DEX_LOCATION/stack_traces"
-        FLAGS="${FLAGS} -Xstacktracedir:$DEX_LOCATION/stack_traces"
-        ARGS="${ARGS} --stack-trace-dir $DEX_LOCATION/stack_traces"
-    fi
 fi
 
 if [ "x$1" = "x" ] ; then
@@ -684,7 +674,7 @@
 dex2oat_cmdline="true"
 vdex_cmdline="true"
 dm_cmdline="true"
-mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/dalvik-cache/$ISA"
+mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
 strip_cmdline="true"
 sync_cmdline="true"
 
@@ -835,28 +825,28 @@
     adb root > /dev/null
     adb wait-for-device
     if [ "$QUIET" = "n" ]; then
-      adb shell rm -rf $DEX_LOCATION
-      adb shell mkdir -p $DEX_LOCATION
-      adb push $TEST_NAME.jar $DEX_LOCATION
-      adb push $TEST_NAME-ex.jar $DEX_LOCATION
+      adb shell rm -rf $CHROOT_DEX_LOCATION
+      adb shell mkdir -p $CHROOT_DEX_LOCATION
+      adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION
+      adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION
       if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
-        adb push profile $DEX_LOCATION
+        adb push profile $CHROOT_DEX_LOCATION
       fi
       # Copy resource folder
       if [ -d res ]; then
-        adb push res $DEX_LOCATION
+        adb push res $CHROOT_DEX_LOCATION
       fi
     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
+      adb shell rm -rf $CHROOT_DEX_LOCATION >/dev/null 2>&1
+      adb shell mkdir -p $CHROOT_DEX_LOCATION >/dev/null 2>&1
+      adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
+      adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
       if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
-        adb push profile $DEX_LOCATION >/dev/null 2>&1
+        adb push profile $CHROOT_DEX_LOCATION >/dev/null 2>&1
       fi
       # Copy resource folder
       if [ -d res ]; then
-        adb push res $DEX_LOCATION >/dev/null 2>&1
+        adb push res $CHROOT_DEX_LOCATION >/dev/null 2>&1
       fi
     fi
 
@@ -865,15 +855,15 @@
       # 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:$LD_LIBRARY_PATH
+      LD_LIBRARY_PATH="$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH"
     fi
 
     # System libraries needed by libarttestd.so
     PUBLIC_LIBS=libc++.so:libbacktrace.so:libbase.so:libnativehelper.so
     if [ "$TEST_IS_NDEBUG" = "y" ]; then
-      PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so
+      PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so:libprofile.so:libartbase.so
     else
-      PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so
+      PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so:libprofiled.so:libartbased.so
     fi
 
     # Create a script with the command. The command can get longer than the longest
@@ -907,14 +897,18 @@
     fi
 
     if [ "$QUIET" = "n" ]; then
-      adb push $cmdfile $DEX_LOCATION/cmdline.sh
+      adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh
     else
-      adb push $cmdfile $DEX_LOCATION/cmdline.sh > /dev/null 2>&1
+      adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh >/dev/null 2>&1
     fi
 
     exit_status=0
     if [ "$DRY_RUN" != "y" ]; then
-      adb shell sh $DEX_LOCATION/cmdline.sh
+      if [ -n "$CHROOT" ]; then
+        adb shell chroot "$CHROOT" sh $DEX_LOCATION/cmdline.sh
+      else
+        adb shell sh $DEX_LOCATION/cmdline.sh
+      fi
       exit_status=$?
     fi
 
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 6d8abe1..493582f 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -254,10 +254,6 @@
         "variant": "jit"
     },
     {
-        "tests": ["904-object-allocation"],
-        "variant": "jit"
-    },
-    {
         "tests": ["570-checker-select",
                   "484-checker-register-hints"],
         "description": ["These tests were based on the linear scan allocator,",
@@ -416,10 +412,11 @@
             ".*methodhandle.*",
             ".*method-handle.*",
             ".*varhandle.*",
-            ".*var-handle.*"
+            ".*var-handle.*",
+            "716-jli-jit-samples"
         ],
         "description": [
-            "Tests that use invoke-polymorphic/invoke-custom which is not yet supported by",
+            "Tests for bytecodes introduced after DEX version 037 that are unsupported by",
             "dexter/slicer."
         ],
         "bug": "b/37272822",
@@ -457,7 +454,8 @@
             "674-hiddenapi",
             "649-vdex-duplicate-method",
             "804-class-extends-itself",
-            "921-hello-failure"
+            "921-hello-failure",
+            "999-redefine-hiddenapi"
         ],
         "description": [
             "Tests that use illegal dex files or otherwise break dexter assumptions"
@@ -474,7 +472,8 @@
             "629-vdex-speed",
             "647-jni-get-field-id",
             "674-hiddenapi",
-            "944-transform-classloaders"
+            "944-transform-classloaders",
+            "999-redefine-hiddenapi"
         ],
         "description": [
             "Tests that use custom class loaders or other features not supported ",
@@ -652,12 +651,6 @@
         "description": ["Requires zip, which isn't available on device"]
     },
     {
-        "tests": "712-varhandle-invocations",
-        "variant": "speed-profile & debug & gcstress & target",
-        "bug": "b/73275005",
-        "description": ["Time out"]
-    },
-    {
         "tests": ["1941-dispose-stress", "522-checker-regression-monitor-exit"],
         "variant": "jvm",
         "bug": "b/73888836",
@@ -737,6 +730,7 @@
           "164-resolution-trampoline-dex-cache",
           "167-visit-locks",
           "168-vmstack-annotated",
+          "172-app-image-twice",
           "201-built-in-except-detail-messages",
           "203-multi-checkpoint",
           "304-method-tracing",
@@ -878,7 +872,6 @@
           "667-jit-jni-stub",
           "667-out-of-bounds",
           "668-aiobe",
-          "674-hiddenapi",
           "674-hotness-compiled",
           "674-vdex-uncompress",
           "675-checker-unverified-method",
@@ -887,6 +880,7 @@
           "706-checker-scheduler",
           "707-checker-invalid-profile",
           "714-invoke-custom-lambda-metafactory",
+          "716-jli-jit-samples",
           "800-smali",
           "801-VoidCheckCast",
           "802-deoptimization",
@@ -955,8 +949,11 @@
     },
     {
         "tests": ["616-cha-unloading",
+                  "674-hiddenapi",
+                  "677-fsi2",
                   "678-quickening",
-                  "679-locks"],
+                  "679-locks",
+                  "999-redefine-hiddenapi"],
         "variant": "jvm",
         "description": ["Doesn't run on RI."]
     },
@@ -982,5 +979,11 @@
                   "991-field-trace-2"],
         "variant": "gcstress & debug & target",
         "description": ["Test can time out on gcstress with debug"]
+    },
+    {
+        "tests": ["080-oom-throw"],
+        "variant": "jit",
+        "bug": "b/77567088",
+        "description": ["Test throws exception before or during OOME."]
     }
 ]
diff --git a/test/run-test b/test/run-test
index 5f85b08..be0a88d 100755
--- a/test/run-test
+++ b/test/run-test
@@ -121,6 +121,8 @@
   export HIDDENAPI="${ANDROID_HOST_OUT}/bin/hiddenapi"
 fi
 
+chroot=
+
 info="info.txt"
 build="build"
 run="run"
@@ -380,6 +382,16 @@
             break
         fi
         shift
+    elif [ "x$1" = "x--chroot" ]; then
+        shift
+        if [ "x$1" = "x" ]; then
+            echo "$0 missing argument to --chroot" 1>&2
+            usage="yes"
+            break
+        fi
+        chroot="$1"
+        run_args="${run_args} --chroot $1"
+        shift
     elif [ "x$1" = "x--android-root" ]; then
         shift
         if [ "x$1" = "x" ]; then
@@ -449,6 +461,9 @@
     fi
 done
 
+# The DEX_LOCATION with the chroot prefix, if any.
+chroot_dex_location="$chroot$DEX_LOCATION"
+
 run_args="${run_args} ${image_args}"
 # Allocate file descriptor real_stderr and redirect it to the shell's error
 # output (fd 2).
@@ -476,7 +491,7 @@
 # tmp_dir may be relative, resolve.
 #
 # Cannot use realpath, as it does not exist on Mac.
-# Cannot us a simple "cd", as the path might not be created yet.
+# Cannot use 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
@@ -550,7 +565,13 @@
     if [ "$runtime" = "jvm" ]; then
         if [ "$prebuild_mode" = "yes" ]; then
             err_echo "--prebuild with --jvm is unsupported"
-            exit 1;
+            exit 1
+        fi
+    else
+        # ART/Dalvik host mode.
+        if [ -n "$chroot" ]; then
+            err_echo "--chroot with --host is unsupported"
+            exit 1
         fi
     fi
 fi
@@ -628,6 +649,12 @@
     usage="yes"
 fi
 
+# TODO: Chroot-based bisection search is not supported yet (see below); implement it.
+if [ "$bisection_search" = "yes" -a -n "$chroot" ]; then
+  err_echo "--chroot with --bisection-search is unsupported"
+  exit 1
+fi
+
 if [ "$usage" = "no" ]; then
     if [ "x$1" = "x" -o "x$1" = "x-" ]; then
         test_dir=`basename "$oldwd"`
@@ -732,6 +759,7 @@
         echo "                          Run with jvmti method redefinition stress testing"
         echo "    --always-clean        Delete the test files even if the test fails."
         echo "    --never-clean         Keep the test files even if the test succeeds."
+        echo "    --chroot [newroot]    Run with root directory set to newroot."
         echo "    --android-root [path] The path on target for the android root. (/system by default)."
         echo "    --dex2oat-swap        Use a dex2oat swap file."
         echo "    --instruction-set-features [string]"
@@ -866,7 +894,7 @@
         if [ "$run_exit" = "0" ]; then
             if [ "$run_checker" = "yes" ]; then
                 if [ "$target_mode" = "yes" ]; then
-                  adb pull $cfg_output_dir/$cfg_output &> /dev/null
+                  adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
                 fi
                 "$checker" $checker_args "$cfg_output" "$tmp_dir" 2>&1
                 checker_exit="$?"
@@ -888,7 +916,7 @@
         "./${run}" $run_args "$@" >"$output" 2>&1
         if [ "$run_checker" = "yes" ]; then
           if [ "$target_mode" = "yes" ]; then
-            adb pull $cfg_output_dir/$cfg_output &> /dev/null
+            adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
           fi
           "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1
         fi
@@ -926,7 +954,7 @@
             good_run="no"
         elif [ "$run_checker" = "yes" ]; then
             if [ "$target_mode" = "yes" ]; then
-              adb pull $cfg_output_dir/$cfg_output &> /dev/null
+              adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
             fi
             "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1
             checker_exit="$?"
@@ -986,6 +1014,7 @@
 ) 2>&${real_stderr} 1>&2
 
 # Attempt bisection only if the test failed.
+# TODO: Implement support for chroot-based bisection search.
 if [ "$bisection_search" = "yes" -a "$good" != "yes" ]; then
     # Bisecting works by skipping different optimization passes which breaks checker assertions.
     if [ "$run_checker" == "yes" ]; then
@@ -997,17 +1026,18 @@
       maybe_device_mode=""
       raw_cmd=""
       if [ "$target_mode" = "yes" ]; then
-        # Produce cmdline.sh in $DEX_LOCATION. "$@" is passed as a runtime option
+        # Produce cmdline.sh in $chroot_dex_location. "$@" is passed as a runtime option
         # so that cmdline.sh forwards its arguments to dalvikvm. invoke-with is set
         # to exec in order to preserve pid when calling dalvikvm. This is required
         # for bisection search to correctly retrieve logs from device.
         "./${run}" $run_args --runtime-option '"$@"' --invoke-with exec --dry-run "$@" &> /dev/null
-        adb shell chmod u+x "$DEX_LOCATION/cmdline.sh"
+        adb shell chmod u+x "$chroot_dex_location/cmdline.sh"
         maybe_device_mode="--device"
         raw_cmd="$DEX_LOCATION/cmdline.sh"
       else
         raw_cmd="$cwd/${run} --external-log-tags $run_args $@"
       fi
+      # TODO: Pass a `--chroot` option to the bisection_search.py script and use it there.
       $ANDROID_BUILD_TOP/art/tools/bisection_search/bisection_search.py \
         $maybe_device_mode \
         --raw-cmd="$raw_cmd" \
@@ -1023,7 +1053,7 @@
     cd "$oldwd"
     rm -rf "$tmp_dir"
     if [ "$target_mode" = "yes" -a "$build_exit" = "0" ]; then
-        adb shell rm -rf $DEX_LOCATION
+        adb shell rm -rf $chroot_dex_location
     fi
     if [ "$good" = "yes" ]; then
         exit 0
@@ -1040,7 +1070,7 @@
     else
         echo "${TEST_NAME} files left in ${tmp_dir} on host"
         if [ "$target_mode" == "yes" ]; then
-            echo "and in ${DEX_LOCATION} on target"
+            echo "and in ${chroot_dex_location} on target"
         fi
     fi
 
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 7564f5a..0c1c308 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -91,6 +91,8 @@
 HOST_2ND_ARCH_PREFIX_DEX2OAT_HOST_INSTRUCTION_SET_FEATURES = _env.get(
   HOST_2ND_ARCH_PREFIX + 'DEX2OAT_HOST_INSTRUCTION_SET_FEATURES')
 
+ART_TEST_CHROOT = _env.get('ART_TEST_CHROOT')
+
 ART_TEST_ANDROID_ROOT = _env.get('ART_TEST_ANDROID_ROOT')
 
 ART_TEST_WITH_STRACE = _getEnvBoolean('ART_TEST_DEBUG_GC', False)
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index e0757ab..faa4d91 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -266,14 +266,16 @@
         }
     },
     'art-gtest-valgrind32': {
-      # Disabled: x86 valgrind does not understand SSE4.x
+      # Disabled: Valgrind is no longer supported.
+      # Historical note: This was already disabled, as x86 valgrind did not understand SSE4.x
       # 'make' : 'valgrind-test-art-host32',
         'env': {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gtest-valgrind64': {
-        'make' : 'valgrind-test-art-host64',
+      # Disabled: Valgrind is no longer supported.
+      # 'make' : 'valgrind-test-art-host64',
         'env': {
             'ART_USE_READ_BARRIER' : 'false'
         }
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 88b509d..254ffc9 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -320,6 +320,9 @@
   if env.ART_TEST_BISECTION:
     options_all += ' --bisection-search'
 
+  if env.ART_TEST_CHROOT:
+    options_all += ' --chroot ' + env.ART_TEST_CHROOT
+
   if env.ART_TEST_ANDROID_ROOT:
     options_all += ' --android-root ' + env.ART_TEST_ANDROID_ROOT
 
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index bbe7465..0eba742 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -25,7 +25,6 @@
 #include <jni.h>
 
 #include "base/utils.h"
-#include "exec_utils.h"
 #include "jvmti.h"
 
 #pragma clang diagnostic push
@@ -920,4 +919,8 @@
   return 0;
 }
 
+extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
+  return Agent_OnLoad(vm, options, reserved);
+}
+
 }  // namespace art
diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt
deleted file mode 100644
index a97d03c..0000000
--- a/test/valgrind-suppressions.txt
+++ /dev/null
@@ -1,87 +0,0 @@
-{
-   b/27596582
-   Memcheck:Cond
-   fun:index
-   fun:expand_dynamic_string_token
-   fun:_dl_map_object
-   fun:map_doit
-   fun:_dl_catch_error
-   fun:do_preload
-   fun:dl_main
-   fun:_dl_sysdep_start
-   fun:_dl_start_final
-   fun:_dl_start
-   obj:/lib/x86_64-linux-gnu/ld-2.19.so
-}
-
-{
-   b/31275764
-   Memcheck:Leak
-   match-leak-kinds: definite
-   fun:malloc
-   ...
-   fun:_ZN3art7Runtime17InitNativeMethodsEv
-}
-
-# SigQuit runs libbacktrace
-{
-   BackTraceReading64
-   Memcheck:Addr8
-   fun:access_mem_unrestricted
-   fun:_Uelf64_memory_read
-   fun:_Uelf64_valid_object_memory
-   fun:map_create_list
-   fun:unw_map_local_create
-   fun:_ZN14UnwindMapLocal5BuildEv
-   fun:_ZN12BacktraceMap6CreateEib
-}
-{
-   BackTraceReading32
-   Memcheck:Addr4
-   fun:access_mem_unrestricted
-   fun:_Uelf32_memory_read
-   fun:_Uelf32_valid_object_memory
-   fun:map_create_list
-   fun:unw_map_local_create
-   fun:_ZN14UnwindMapLocal5BuildEv
-   fun:_ZN12BacktraceMap6CreateEib
-}
-{
-   BackTraceReading64
-   Memcheck:Addr8
-   fun:access_mem_unrestricted
-   fun:_Uelf64_memory_read
-   fun:_Uelf64_get_load_base
-   fun:map_create_list
-   fun:unw_map_local_create
-   fun:_ZN14UnwindMapLocal5BuildEv
-   fun:_ZN12BacktraceMap6CreateEib
-}
-{
-   BackTraceReading32
-   Memcheck:Addr4
-   fun:access_mem_unrestricted
-   fun:_Uelf32_memory_read
-   fun:_Uelf32_get_load_base
-   fun:map_create_list
-   fun:unw_map_local_create
-   fun:_ZN14UnwindMapLocal5BuildEv
-   fun:_ZN12BacktraceMap6CreateEib
-}
-
-{
-   process_vm_readv
-   Memcheck:Param
-   process_vm_readv(lvec[...])
-   fun:process_vm_readv
-}
-
-# Suppressions for IsAddressMapped check in MemMapTest
-{
-   MemMapTest_IsAddressMapped
-   Memcheck:Param
-   msync(start)
-   ...
-   fun:_ZN3art10MemMapTest15IsAddressMappedEPv
-   ...
-}
diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt
deleted file mode 100644
index 0d63a1c..0000000
--- a/test/valgrind-target-suppressions.txt
+++ /dev/null
@@ -1,76 +0,0 @@
-# Valgrind does not recognize the ashmen ioctl() calls on ARM64, so it assumes that a size
-# parameter is a pointer.
-{
-   ashmem ioctl
-   Memcheck:Param
-   ioctl(generic)
-   ...
-   fun:ioctl
-   fun:ashmem_create_region
-}
-
-# It seems that on ARM64 Valgrind considers the canary value used by the Clang stack protector to
-# be an uninitialized value.
-{
-   jemalloc chunk_alloc_cache
-   Memcheck:Cond
-   fun:je_chunk_alloc_cache
-}
-
-# The VectorImpl class does not hold a pointer to the allocated SharedBuffer structure, but to the
-# beginning of the data, which is effectively an interior pointer. Valgrind has limitations when
-# dealing with interior pointers.
-{
-   VectorImpl
-   Memcheck:Leak
-   match-leak-kinds:possible
-   fun:malloc
-   # The wildcards make this rule work both for 32-bit and 64-bit environments.
-   fun:_ZN7android12SharedBuffer5allocE?
-   fun:_ZN7android10VectorImpl5_growE??
-}
-
-# Clang/LLVM uses memcpy for *x = *y, even though x == y (which is undefined behavior). Ignore.
-# b/29279679, https://llvm.org/bugs/show_bug.cgi?id=11763
-{
-   MemCpySelfAssign
-   Memcheck:Overlap
-   fun:memcpy
-   ...
-   fun:je_malloc_tsd_boot0
-}
-
-# Setenv is known-leaking when overwriting mappings. This is triggered by re-initializing
-# ANDROID_DATA. Ignore all setenv leaks.
-{
-   SetenvAndroidDataReinit
-   Memcheck:Leak
-   match-leak-kinds: definite
-   fun:malloc
-   fun:setenv
-}
-
-{
-   b/31275764
-   Memcheck:Leak
-   match-leak-kinds: definite
-   fun:malloc
-   ...
-   fun:_ZN3art7Runtime17InitNativeMethodsEv
-}
-
-# art::MemMap::MapInternal() uses msync() to check for the existence of memory mappings.
-{
-  art::MemMap::MapInternal()
-  Memcheck:Param
-  msync(start)
-  fun:msync
-  fun:_ZN3art6MemMap11MapInternalEPvmiiilb
-}
-
-{
-   process_vm_readv
-   Memcheck:Param
-   process_vm_readv(lvec[...])
-   fun:process_vm_readv
-}
diff --git a/tools/art b/tools/art
index 1c603d4..781ee2f 100644
--- a/tools/art
+++ b/tools/art
@@ -77,7 +77,6 @@
 Supported OPTIONS include:
   --32                     Use the 32-bit Android Runtime.
   --64                     Use the 64-bit Android Runtime.
-  --callgrind              Launch the Android Runtime in callgrind.
   -d                       Use the debug ART library (libartd.so).
   --debug                  Equivalent to -d.
   --gdb                    Launch the Android Runtime in gdb.
@@ -269,9 +268,6 @@
   --64)
     ART_BINARY=dalvikvm64
     ;;
-  --callgrind)
-    LAUNCH_WRAPPER="valgrind --tool=callgrind"
-    ;;
   -d)
     ;& # Fallthrough
   --debug)
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index e447ab4..10eb936 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -80,8 +80,13 @@
   fi
   make_command="make $j_arg $extra_args $showcommands build-art-target-tests $common_targets"
   make_command+=" libjavacrypto-target libnetd_client-target linker toybox toolbox sh"
+  make_command+=" debuggerd su"
   make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ "
   make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt"
+  if [[ -n "$ART_TEST_CHROOT" ]]; then
+    # These targets are needed for the chroot environment.
+    make_command+=" crash_dump event-log-tags"
+  fi
   mode_suffix="-target"
 fi
 
diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh
new file mode 100755
index 0000000..53072ae
--- /dev/null
+++ b/tools/cleanup-buildbot-device.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+green='\033[0;32m'
+nc='\033[0m'
+
+# Setup as root, as device cleanup requires it.
+adb root
+adb wait-for-device
+
+if [[ -n "$ART_TEST_CHROOT" ]]; then
+  # Check that ART_TEST_CHROOT is correctly defined.
+  if [[ "x$ART_TEST_CHROOT" != x/* ]]; then
+    echo "$ART_TEST_CHROOT is not an absolute path"
+    exit 1
+  fi
+
+  echo -e "${green}Clean up /system in chroot${nc}"
+  # Remove all files under /system except the potential property_contexts file.
+  #
+  # The current ART Buildbot set-up runs the "setup device" step
+  # (performed by script tools/setup-buildbot-device.sh) before the
+  # "device cleanup" step (implemented by this script). As
+  # property_contexts file aliases are created during the former step,
+  # we need this exception to prevent the property_contexts file under
+  # /system in the chroot from being removed by the latter step.
+  #
+  # TODO: Reorder ART Buildbot steps so that "device cleanup" happens
+  # before "setup device" and remove this special case.
+  #
+  # TODO: Also consider adding a "tear down device" step on the ART
+  # Buildbot (at the very end of a build) undoing (some of) the work
+  # done in the "device setup" step.
+  adb shell find "$ART_TEST_CHROOT/system" \
+    ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \
+    ! -type d \
+    -exec rm -f \{\} +
+
+  echo -e "${green}Clean up some subdirs in /data in chroot${nc}"
+  adb shell rm -rf \
+    "$ART_TEST_CHROOT/data/local/tmp/*" \
+    "$ART_TEST_CHROOT/data/art-test" \
+    "$ART_TEST_CHROOT/data/nativetest" \
+    "$ART_TEST_CHROOT/data/nativetest64" \
+    "$ART_TEST_CHROOT/data/run-test" \
+    "$ART_TEST_CHROOT/data/dalvik-cache/*" \
+    "$ART_TEST_CHROOT/data/misc/trace/*"
+else
+  adb shell rm -rf \
+    /data/local/tmp /data/art-test /data/nativetest /data/nativetest64 '/data/misc/trace/*'
+fi
diff --git a/tools/cpp-define-generator/Android.bp b/tools/cpp-define-generator/Android.bp
index 39e57bd..23cc917 100644
--- a/tools/cpp-define-generator/Android.bp
+++ b/tools/cpp-define-generator/Android.bp
@@ -31,6 +31,7 @@
     include_dirs: [
         "art/libartbase",
         "art/libdexfile",
+        "art/libartbase",
         "art/runtime",
     ],
     srcs: ["main.cc"],
diff --git a/tools/cpp-define-generator/constant_globals.def b/tools/cpp-define-generator/constant_globals.def
index 539633e..d0d6350 100644
--- a/tools/cpp-define-generator/constant_globals.def
+++ b/tools/cpp-define-generator/constant_globals.def
@@ -18,8 +18,8 @@
 
 #if defined(DEFINE_INCLUDE_DEPENDENCIES)
 #include <atomic>            // std::memory_order_relaxed
+#include "base/globals.h"    // art::kObjectAlignment
 #include "dex/modifiers.h"
-#include "globals.h"         // art::kObjectAlignment
 #endif
 
 DEFINE_EXPR(STD_MEMORY_ORDER_RELAXED, int32_t, std::memory_order_relaxed)
diff --git a/tools/cpp-define-generator/constant_lockword.def b/tools/cpp-define-generator/constant_lockword.def
index 08d5885..977d1ca 100644
--- a/tools/cpp-define-generator/constant_lockword.def
+++ b/tools/cpp-define-generator/constant_lockword.def
@@ -23,23 +23,29 @@
 #define DEFINE_LOCK_WORD_EXPR(macro_name, type, constant_field_name) \
   DEFINE_EXPR(LOCK_WORD_ ## macro_name, type, art::LockWord::constant_field_name)
 
+// FIXME: The naming is inconsistent, the `Shifted` -> `_SHIFTED` suffix is sometimes missing.
 DEFINE_LOCK_WORD_EXPR(STATE_SHIFT,               int32_t,  kStateShift)
-DEFINE_LOCK_WORD_EXPR(STATE_MASK,                uint32_t, kStateMaskShifted)
+DEFINE_LOCK_WORD_EXPR(STATE_MASK_SHIFTED,        uint32_t, kStateMaskShifted)
 DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_SHIFT,  int32_t,  kReadBarrierStateShift)
-DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_MASK,   uint32_t,  kReadBarrierStateMaskShifted)
+DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_MASK,   uint32_t, kReadBarrierStateMaskShifted)
 DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_MASK_TOGGLED, uint32_t, kReadBarrierStateMaskShiftedToggled)
-DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_ONE,       int32_t,  kThinLockCountOne)
+DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_SIZE,      int32_t,  kThinLockCountSize)
+DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_SHIFT,     int32_t,  kThinLockCountShift)
+DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_MASK_SHIFTED, uint32_t, kThinLockCountMaskShifted)
+DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_ONE,       uint32_t, kThinLockCountOne)
+DEFINE_LOCK_WORD_EXPR(THIN_LOCK_OWNER_MASK_SHIFTED, uint32_t, kThinLockOwnerMaskShifted)
 
-DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS, uint32_t, kStateForwardingAddress)
+DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS,  uint32_t, kStateForwardingAddress)
 DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS_OVERFLOW, uint32_t, kStateForwardingAddressOverflow)
 DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS_SHIFT, uint32_t, kForwardingAddressShift)
 
-DEFINE_LOCK_WORD_EXPR(GC_STATE_MASK_SHIFTED,   uint32_t,  kGCStateMaskShifted)
+DEFINE_LOCK_WORD_EXPR(GC_STATE_MASK_SHIFTED,     uint32_t,  kGCStateMaskShifted)
 DEFINE_LOCK_WORD_EXPR(GC_STATE_MASK_SHIFTED_TOGGLED, uint32_t, kGCStateMaskShiftedToggled)
-DEFINE_LOCK_WORD_EXPR(GC_STATE_SHIFT,   int32_t,  kGCStateShift)
+DEFINE_LOCK_WORD_EXPR(GC_STATE_SIZE,             int32_t,  kGCStateSize)
+DEFINE_LOCK_WORD_EXPR(GC_STATE_SHIFT,            int32_t,  kGCStateShift)
 
-DEFINE_LOCK_WORD_EXPR(MARK_BIT_SHIFT, int32_t, kMarkBitStateShift)
-DEFINE_LOCK_WORD_EXPR(MARK_BIT_MASK_SHIFTED, uint32_t, kMarkBitStateMaskShifted)
+DEFINE_LOCK_WORD_EXPR(MARK_BIT_SHIFT,            int32_t,  kMarkBitStateShift)
+DEFINE_LOCK_WORD_EXPR(MARK_BIT_MASK_SHIFTED,     uint32_t, kMarkBitStateMaskShifted)
 
 #undef DEFINE_LOCK_WORD_EXPR
 
diff --git a/tools/dexanalyze/Android.bp b/tools/dexanalyze/Android.bp
index 2754e64..a229d73 100644
--- a/tools/dexanalyze/Android.bp
+++ b/tools/dexanalyze/Android.bp
@@ -37,6 +37,7 @@
     defaults: ["dexanalyze-defaults"],
     shared_libs: [
         "libdexfile",
+        "libartbase",
         "libbase",
     ],
 }
diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc
index a5f647c..46c4852 100644
--- a/tools/dexanalyze/dexanalyze.cc
+++ b/tools/dexanalyze/dexanalyze.cc
@@ -15,6 +15,7 @@
  */
 
 #include <cstdint>
+#include <iostream>
 #include <set>
 #include <sstream>
 
@@ -29,7 +30,19 @@
 namespace art {
 
 class DexAnalyze {
-  static const int kExitCodeUsageError = 1;
+  static constexpr int kExitCodeUsageError = 1;
+  static constexpr int kExitCodeFailedToOpenFile = 2;
+  static constexpr int kExitCodeFailedToOpenDex = 3;
+  static constexpr int kExitCodeFailedToProcessDex = 4;
+
+  static void StdoutLogger(android::base::LogId,
+                           android::base::LogSeverity,
+                           const char*,
+                           const char*,
+                           unsigned int,
+                           const char* message) {
+    std::cout << message << std::endl;
+  }
 
   static int Usage(char** argv) {
     LOG(ERROR)
@@ -53,6 +66,8 @@
           run_all_experiments_ = true;
         } else if (arg == "-count-indices") {
           exp_count_indices_ = true;
+        } else if (arg == "-analyze-strings") {
+          exp_analyze_strings_ = true;
         } else if (arg == "-d") {
           dump_per_input_dex_ = true;
         } else if (!arg.empty() && arg[0] == '-') {
@@ -72,6 +87,7 @@
     bool run_dex_file_verifier_ = true;
     bool dump_per_input_dex_ = false;
     bool exp_count_indices_ = false;
+    bool exp_analyze_strings_ = false;
     bool run_all_experiments_ = false;
     std::vector<std::string> filenames_;
   };
@@ -82,29 +98,36 @@
       if (options->run_all_experiments_ || options->exp_count_indices_) {
         experiments_.emplace_back(new CountDexIndices);
       }
+      if (options->run_all_experiments_ || options->exp_analyze_strings_) {
+        experiments_.emplace_back(new AnalyzeStrings);
+      }
     }
 
     bool ProcessDexFile(const DexFile& dex_file) {
       for (std::unique_ptr<Experiment>& experiment : experiments_) {
         experiment->ProcessDexFile(dex_file);
       }
+      total_size_ += dex_file.Size();
       ++dex_count_;
       return true;
     }
 
     void Dump(std::ostream& os) {
       for (std::unique_ptr<Experiment>& experiment : experiments_) {
-        experiment->Dump(os);
+        experiment->Dump(os, total_size_);
       }
     }
 
     const Options* const options_;
     std::vector<std::unique_ptr<Experiment>> experiments_;
     size_t dex_count_ = 0;
+    uint64_t total_size_ = 0u;
   };
 
  public:
   static int Run(int argc, char** argv) {
+    android::base::SetLogger(StdoutLogger);
+
     Options options;
     int result = options.Parse(argc, argv);
     if (result != 0) {
@@ -115,10 +138,10 @@
     Analysis cumulative(&options);
     for (const std::string& filename : options.filenames_) {
       std::string content;
-      // TODO: once added, use an api to android::base to read a std::vector<uint8_t>.
+      // TODO: once added, use an API to android::base to read a std::vector<uint8_t>.
       if (!android::base::ReadFileToString(filename.c_str(), &content)) {
         LOG(ERROR) << "ReadFileToString failed for " + filename << std::endl;
-        continue;
+        return kExitCodeFailedToOpenFile;
       }
       std::vector<std::unique_ptr<const DexFile>> dex_files;
       const DexFileLoader dex_file_loader;
@@ -130,14 +153,14 @@
                                    &error_msg,
                                    &dex_files)) {
         LOG(ERROR) << "OpenAll failed for " + filename << " with " << error_msg << std::endl;
-        continue;
+        return kExitCodeFailedToOpenDex;
       }
       for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
         if (options.dump_per_input_dex_) {
           Analysis current(&options);
           if (!current.ProcessDexFile(*dex_file)) {
             LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg;
-            continue;
+            return kExitCodeFailedToProcessDex;
           }
           LOG(INFO) << "Analysis for " << dex_file->GetLocation() << std::endl;
           current.Dump(LOG_STREAM(INFO));
@@ -154,7 +177,6 @@
 }  // namespace art
 
 int main(int argc, char** argv) {
-  android::base::SetLogger(android::base::StderrLogger);
   return art::DexAnalyze::Run(argc, argv);
 }
 
diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc
index e1f119d..f9bf45d 100644
--- a/tools/dexanalyze/dexanalyze_experiments.cc
+++ b/tools/dexanalyze/dexanalyze_experiments.cc
@@ -15,12 +15,110 @@
  */
 
 #include "dexanalyze_experiments.h"
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <iostream>
+#include <map>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+#include "dex/class_accessor-inl.h"
 #include "dex/code_item_accessors-inl.h"
 #include "dex/dex_instruction-inl.h"
 #include "dex/standard_dex_file.h"
+#include "dex/utf-inl.h"
 
 namespace art {
 
+std::string Percent(uint64_t value, uint64_t max) {
+  if (max == 0) {
+    ++max;
+  }
+  return android::base::StringPrintf("%" PRId64 "(%.2f%%)",
+                                     value,
+                                     static_cast<double>(value * 100) / static_cast<double>(max));
+}
+
+static size_t PrefixLen(const std::string& a, const std::string& b) {
+  size_t len = 0;
+  for (; len < a.length() && len < b.length() && a[len] == b[len]; ++len) {}
+  return len;
+}
+
+void AnalyzeStrings::ProcessDexFile(const DexFile& dex_file) {
+  std::vector<std::string> strings;
+  for (size_t i = 0; i < dex_file.NumStringIds(); ++i) {
+    uint32_t length = 0;
+    const char* data = dex_file.StringDataAndUtf16LengthByIdx(dex::StringIndex(i), &length);
+    // Analyze if the string has any UTF16 chars.
+    bool have_wide_char = false;
+    const char* ptr = data;
+    for (size_t j = 0; j < length; ++j) {
+      have_wide_char = have_wide_char || GetUtf16FromUtf8(&ptr) >= 0x100;
+    }
+    if (have_wide_char) {
+      wide_string_bytes_ += 2 * length;
+    } else {
+      ascii_string_bytes_ += length;
+    }
+    string_data_bytes_ += ptr - data;
+
+    strings.push_back(data);
+  }
+  // Note that the strings are probably already sorted.
+  std::sort(strings.begin(), strings.end());
+
+  // Tunable parameters.
+  static const size_t kMinPrefixLen = 3;
+  static const size_t kPrefixConstantCost = 5;
+  static const size_t kPrefixIndexCost = 2;
+
+  // Calculate total shared prefix.
+  std::vector<size_t> shared_len;
+  std::set<std::string> prefixes;
+  for (size_t i = 0; i < strings.size(); ++i) {
+    size_t best_len = 0;
+    if (i > 0) {
+      best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1]));
+    }
+    if (i < strings.size() - 1) {
+      best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1]));
+    }
+    std::string prefix;
+    if (best_len >= kMinPrefixLen) {
+      prefix = strings[i].substr(0, best_len);
+      prefixes.insert(prefix);
+      total_prefix_savings_ += prefix.length();
+    }
+    total_prefix_index_cost_ += kPrefixIndexCost;
+  }
+  total_num_prefixes_ += prefixes.size();
+  for (const std::string& s : prefixes) {
+    // 4 bytes for an offset, one for length.
+    total_prefix_dict_ += s.length();
+    total_prefix_table_ += kPrefixConstantCost;
+  }
+}
+
+void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const {
+  os << "Total string data bytes " << Percent(string_data_bytes_, total_size) << "\n";
+  os << "UTF-16 string data bytes " << Percent(wide_string_bytes_, total_size) << "\n";
+  os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n";
+
+  // Prefix based strings.
+  os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n";
+  os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n";
+  os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n";
+  os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n";
+  int64_t net_savings = total_prefix_savings_;
+  net_savings -= total_prefix_dict_;
+  net_savings -= total_prefix_table_;
+  net_savings -= total_prefix_index_cost_;
+  os << "Prefix net savings " << Percent(net_savings, total_size) << "\n";
+  os << "Prefix dictionary elements " << total_num_prefixes_ << "\n";
+}
+
 void CountDexIndices::ProcessDexFile(const DexFile& dex_file) {
   num_string_ids_ += dex_file.NumStringIds();
   num_method_ids_ += dex_file.NumMethodIds();
@@ -29,85 +127,79 @@
   num_class_defs_ += dex_file.NumClassDefs();
   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 uint8_t* class_data = dex_file.GetClassData(class_def);
-    if (class_data == nullptr) {
-      continue;
-    }
-    ClassDataItemIterator it(dex_file, class_data);
-    it.SkipAllFields();
+    ClassAccessor accessor(dex_file, class_def);
     std::set<size_t> unique_method_ids;
     std::set<size_t> unique_string_ids;
-    while (it.HasNextMethod()) {
-      const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
-      if (code_item != nullptr) {
-        CodeItemInstructionAccessor instructions(dex_file, code_item);
-        const uint16_t* code_ptr = instructions.Insns();
-        dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]);
-        for (const DexInstructionPcPair& inst : instructions) {
-          switch (inst->Opcode()) {
-            case Instruction::CONST_STRING: {
-              const dex::StringIndex string_index(inst->VRegB_21c());
-              unique_string_ids.insert(string_index.index_);
-              ++num_string_ids_from_code_;
-              break;
-            }
-            case Instruction::CONST_STRING_JUMBO: {
-              const dex::StringIndex string_index(inst->VRegB_31c());
-              unique_string_ids.insert(string_index.index_);
-              ++num_string_ids_from_code_;
-              break;
-            }
-            // Invoke cases.
-            case Instruction::INVOKE_VIRTUAL:
-            case Instruction::INVOKE_VIRTUAL_RANGE: {
-              bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE);
-              uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
-              if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
-                ++same_class_virtual_;
-              } else {
-                ++other_class_virtual_;
-                unique_method_ids.insert(method_idx);
-              }
-              break;
-            }
-            case Instruction::INVOKE_DIRECT:
-            case Instruction::INVOKE_DIRECT_RANGE: {
-              bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE);
-              uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
-              if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
-                ++same_class_direct_;
-              } else {
-                ++other_class_direct_;
-                unique_method_ids.insert(method_idx);
-              }
-              break;
-            }
-            case Instruction::INVOKE_STATIC:
-            case Instruction::INVOKE_STATIC_RANGE: {
-              bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE);
-              uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
-              if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
-                ++same_class_static_;
-              } else {
-                ++other_class_static_;
-                unique_method_ids.insert(method_idx);
-              }
-              break;
-            }
-            default:
-              break;
+    accessor.VisitMethods([&](const ClassAccessor::Method& method) {
+      const DexFile::CodeItem* code_item = accessor.GetCodeItem(method);
+      if (code_item == nullptr) {
+        return;
+      }
+      CodeItemInstructionAccessor instructions(dex_file, code_item);
+      const uint16_t* code_ptr = instructions.Insns();
+      dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]);
+      for (const DexInstructionPcPair& inst : instructions) {
+        switch (inst->Opcode()) {
+          case Instruction::CONST_STRING: {
+            const dex::StringIndex string_index(inst->VRegB_21c());
+            unique_string_ids.insert(string_index.index_);
+            ++num_string_ids_from_code_;
+            break;
           }
+          case Instruction::CONST_STRING_JUMBO: {
+            const dex::StringIndex string_index(inst->VRegB_31c());
+            unique_string_ids.insert(string_index.index_);
+            ++num_string_ids_from_code_;
+            break;
+          }
+          // Invoke cases.
+          case Instruction::INVOKE_VIRTUAL:
+          case Instruction::INVOKE_VIRTUAL_RANGE: {
+            bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE);
+            uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+            if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
+              ++same_class_virtual_;
+            } else {
+              ++other_class_virtual_;
+              unique_method_ids.insert(method_idx);
+            }
+            break;
+          }
+          case Instruction::INVOKE_DIRECT:
+          case Instruction::INVOKE_DIRECT_RANGE: {
+            bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE);
+            uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+            if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
+              ++same_class_direct_;
+            } else {
+              ++other_class_direct_;
+              unique_method_ids.insert(method_idx);
+            }
+            break;
+          }
+          case Instruction::INVOKE_STATIC:
+          case Instruction::INVOKE_STATIC_RANGE: {
+            bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE);
+            uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+            if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
+              ++same_class_static_;
+            } else {
+              ++other_class_static_;
+              unique_method_ids.insert(method_idx);
+            }
+            break;
+          }
+          default:
+            break;
         }
       }
-      it.Next();
-    }
-    DCHECK(!it.HasNext());
+    });
     total_unique_method_idx_ += unique_method_ids.size();
     total_unique_string_ids_ += unique_string_ids.size();
   }
 }
 
-void CountDexIndices::Dump(std::ostream& os) const {
+void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const {
   os << "Num string ids: " << num_string_ids_ << "\n";
   os << "Num method ids: " << num_method_ids_ << "\n";
   os << "Num field ids: " << num_field_ids_ << "\n";
@@ -127,6 +219,7 @@
   os << "Same class invoke: " << same_class_total << "\n";
   os << "Other class invoke: " << other_class_total << "\n";
   os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
+  os << "Total dex size: " << total_size << "\n";
 }
 
 }  // namespace art
diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h
index 5d0f51b..0fb4d32 100644
--- a/tools/dexanalyze/dexanalyze_experiments.h
+++ b/tools/dexanalyze/dexanalyze_experiments.h
@@ -24,12 +24,31 @@
 
 class DexFile;
 
+std::string Percent(uint64_t value, uint64_t max);
+
 // An experiment a stateful visitor that runs on dex files. Results are cumulative.
 class Experiment {
  public:
   virtual ~Experiment() {}
   virtual void ProcessDexFile(const DexFile& dex_file) = 0;
-  virtual void Dump(std::ostream& os) const = 0;
+  virtual void Dump(std::ostream& os, uint64_t total_size) const = 0;
+};
+
+// Analyze string data and strings accessed from code.
+class AnalyzeStrings : public Experiment {
+ public:
+  void ProcessDexFile(const DexFile& dex_file);
+  void Dump(std::ostream& os, uint64_t total_size) const;
+
+ private:
+  int64_t wide_string_bytes_ = 0u;
+  int64_t ascii_string_bytes_ = 0u;
+  int64_t string_data_bytes_ = 0u;
+  int64_t total_prefix_savings_ = 0u;
+  int64_t total_prefix_dict_ = 0u;
+  int64_t total_prefix_table_ = 0u;
+  int64_t total_prefix_index_cost_ = 0u;
+  int64_t total_num_prefixes_ = 0u;
 };
 
 // Count numbers of dex indices.
@@ -37,7 +56,7 @@
  public:
   void ProcessDexFile(const DexFile& dex_file);
 
-  void Dump(std::ostream& os) const;
+  void Dump(std::ostream& os, uint64_t total_size) const;
 
  private:
   // Total string ids loaded from dex code.
@@ -65,4 +84,3 @@
 }  // namespace art
 
 #endif  // ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_
-
diff --git a/tools/dexanalyze/dexanalyze_test.cc b/tools/dexanalyze/dexanalyze_test.cc
index c9b8f53..96be3f9 100644
--- a/tools/dexanalyze/dexanalyze_test.cc
+++ b/tools/dexanalyze/dexanalyze_test.cc
@@ -36,10 +36,22 @@
   }
 };
 
+TEST_F(DexAnalyzeTest, NoInputFileGiven) {
+  DexAnalyzeExec({ "-a" }, /*expect_success*/ false);
+}
+
+TEST_F(DexAnalyzeTest, CantOpenInput) {
+  DexAnalyzeExec({ "-a", "/non/existent/path" }, /*expect_success*/ false);
+}
+
 TEST_F(DexAnalyzeTest, TestAnalyzeMultidex) {
   DexAnalyzeExec({ "-a", GetTestDexFileName("MultiDex") }, /*expect_success*/ true);
 }
 
+TEST_F(DexAnalyzeTest, TestAnalizeCoreDex) {
+  DexAnalyzeExec({ "-a", GetLibCoreDexFileNames()[0] }, /*expect_success*/ true);
+}
+
 TEST_F(DexAnalyzeTest, TestInvalidArg) {
   DexAnalyzeExec({ "-invalid-option" }, /*expect_success*/ false);
 }
diff --git a/tools/generate-boot-image-profile.sh b/tools/generate-boot-image-profile.sh
index ee53f43..44c64d2 100755
--- a/tools/generate-boot-image-profile.sh
+++ b/tools/generate-boot-image-profile.sh
@@ -48,7 +48,7 @@
 
 # Boot jars have hidden API access flags which do not pass dex file
 # verification. Skip it.
-jar_args=("--skip-apk-verification")
+jar_args=()
 boot_jars=$("$ANDROID_BUILD_TOP"/art/tools/bootjars.sh --target)
 jar_dir=$ANDROID_BUILD_TOP/$(get_build_var TARGET_OUT_JAVA_LIBRARIES)
 for file in $boot_jars; do
diff --git a/tools/hiddenapi/Android.bp b/tools/hiddenapi/Android.bp
index af87d31..3b364b6 100644
--- a/tools/hiddenapi/Android.bp
+++ b/tools/hiddenapi/Android.bp
@@ -40,6 +40,7 @@
     shared_libs: [
         "libart",
         "libdexfile",
+        "libartbase",
     ],
 }
 
@@ -52,6 +53,7 @@
     shared_libs: [
         "libartd",
         "libdexfiled",
+        "libartbased",
     ],
 }
 
diff --git a/tools/public.libraries.buildbot.txt b/tools/public.libraries.buildbot.txt
index de636a8..9b171a2 100644
--- a/tools/public.libraries.buildbot.txt
+++ b/tools/public.libraries.buildbot.txt
@@ -1,5 +1,7 @@
 libart.so
 libartd.so
+libartbase.so
+libartbased.so
 libdexfile.so
 libdexfiled.so
 libbacktrace.so
@@ -8,3 +10,5 @@
 libdl.so
 libm.so
 libnativehelper.so
+libprofile.so
+libprofiled.so
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 21ddcbc..eebc092 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -68,6 +68,8 @@
 mode="target"
 # Use JIT compiling by default.
 use_jit=true
+# Don't use chroot by default.
+use_chroot=false
 variant_cmdline_parameter="--variant=X32"
 dump_command="/bin/true"
 # Timeout of JDWP test in ms.
@@ -110,6 +112,15 @@
     # We don't care about jit with the RI
     use_jit=false
     shift
+  elif [[ "$1" == "--chroot" ]]; then
+    use_chroot=true
+    # Adjust settings for chroot environment.
+    art="/system/bin/art"
+    art_debugee="sh /system/bin/art"
+    vm_command="--vm-command=$art"
+    device_dir="--device-dir=/tmp"
+    # Shift the "--chroot" flag and its argument.
+    shift 2
   elif [[ $1 == --test-timeout-ms ]]; then
     # Remove the --test-timeout-ms from the arguments.
     args=${args/$1}
@@ -191,6 +202,12 @@
   fi
 done
 
+if $use_chroot && [[ $mode == "host" ]]; then
+  # Chroot-based testing is not supported on host.
+  echo "Cannot use --chroot with --mode=host"
+  exit 1
+fi
+
 if [[ $has_gdb = "yes" ]]; then
   if [[ $explicit_debug = "no" ]]; then
     debug="yes"
@@ -216,7 +233,11 @@
   if [[ "$mode" == "host" ]]; then
     dump_command="/bin/kill -3"
   else
-    dump_command="/system/xbin/su root /data/local/tmp/system/bin/debuggerd"
+    # Note that this dumping command won't work when `$android_root`
+    # is different from `/system` (e.g. on ART Buildbot devices) when
+    # the device is running Android N, as the debuggerd protocol
+    # changed in an incompatible way in Android O (see b/32466479).
+    dump_command="$android_root/xbin/su root $android_root/bin/debuggerd"
   fi
   if [[ $has_gdb = "yes" ]]; then
     if [[ $mode == "target" ]]; then
@@ -339,7 +360,9 @@
 if [[ $mode == "host" ]]; then
   pkill -9 -f /bin/dalvikvm
 else
-  adb shell pkill -9 -f /bin/dalvikvm
+  # Tests may run on older Android versions where pkill requires "-l SIGNAL"
+  # rather than "-SIGNAL".
+  adb shell pkill -l 9 -f /bin/dalvikvm
 fi
 echo "Done."
 
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 7f0383d..3537c1b 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -77,7 +77,6 @@
                   "libcore.javax.security"
                   "libcore.javax.sql"
                   "libcore.javax.xml"
-                  "libcore.libcore.icu"
                   "libcore.libcore.io"
                   "libcore.libcore.net"
                   "libcore.libcore.reflect"
@@ -105,10 +104,14 @@
 gcstress=false
 debug=false
 
+# Don't use device mode by default.
+device_mode=false
+# Don't use chroot by default.
+use_chroot=false
+
 while true; do
   if [[ "$1" == "--mode=device" ]]; then
-    vogar_args="$vogar_args --device-dir=/data/local/tmp"
-    vogar_args="$vogar_args --vm-command=$android_root/bin/art"
+    device_mode=true
     vogar_args="$vogar_args --vm-arg -Ximage:/data/art-test/core.art"
     shift
   elif [[ "$1" == "--mode=host" ]]; then
@@ -132,6 +135,10 @@
   elif [[ "$1" == "-Xgc:gcstress" ]]; then
     gcstress=true
     shift
+  elif [[ "$1" == "--chroot" ]]; then
+    use_chroot=true
+    # Shift the "--chroot" flag and its argument.
+    shift 2
   elif [[ "$1" == "" ]]; then
     break
   else
@@ -139,6 +146,23 @@
   fi
 done
 
+if $device_mode; then
+  if $use_chroot; then
+    vogar_args="$vogar_args --device-dir=/tmp"
+    vogar_args="$vogar_args --vm-command=/system/bin/art"
+  else
+    vogar_args="$vogar_args --device-dir=/data/local/tmp"
+    vogar_args="$vogar_args --vm-command=$android_root/bin/art"
+  fi
+else
+  # Host mode.
+  if $use_chroot; then
+    # Chroot-based testing is not supported on host.
+    echo "Cannot use --chroot with --mode=host"
+    exit 1
+  fi
+fi
+
 # Increase the timeout, as vogar cannot set individual test
 # timeout when being asked to run packages, and some tests go above
 # the default timeout.
@@ -154,11 +178,14 @@
 vogar_args="$vogar_args --vm-arg -Xusejit:$use_jit"
 
 # gcstress may lead to timeouts, so we need dedicated expectations files for it.
-if [[ $gcstress ]]; then
+if $gcstress; then
   expectations="$expectations --expectations art/tools/libcore_gcstress_failures.txt"
-  if [[ $debug ]]; then
+  if $debug; then
     expectations="$expectations --expectations art/tools/libcore_gcstress_debug_failures.txt"
   fi
+else
+  # We only run this package when not under gcstress as it can cause timeouts. See b/78228743.
+  working_packages+=("libcore.libcore.icu")
 fi
 
 # Disable network-related libcore tests that are failing on the following
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 5ce7f52..f71d973 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -17,8 +17,7 @@
 green='\033[0;32m'
 nc='\033[0m'
 
-# Setup as root, as the next buildbot step (device cleanup) requires it.
-# This is also required to set the date, if needed.
+# Setup as root, as some actions performed here (e.g. setting the date) requires it.
 adb root
 adb wait-for-device
 
@@ -100,3 +99,58 @@
   processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}')
   for i in $processes; do adb shell kill -9 $i; done
 fi
+
+if [[ -n "$ART_TEST_CHROOT" ]]; then
+  # Prepare the chroot dir.
+  echo -e "${green}Prepare the chroot dir in $ART_TEST_CHROOT${nc}"
+
+  # Check that ART_TEST_CHROOT is correctly defined.
+  [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; }
+
+  # Create chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT"
+
+  # Provide property_contexts file(s) in chroot.
+  # This is required to have Android system properties work from the chroot.
+  # Notes:
+  # - In Android N, only '/property_contexts' is expected.
+  # - In Android O, property_context files are expected under /system and /vendor.
+  # (See bionic/libc/bionic/system_properties.cpp for more information.)
+  property_context_files="/property_contexts \
+    /system/etc/selinux/plat_property_contexts \
+    /vendor/etc/selinux/nonplat_property_context \
+    /plat_property_contexts \
+    /nonplat_property_contexts"
+  for f in $property_context_files; do
+    adb shell test -f "$f" \
+      "&&" mkdir -p "$ART_TEST_CHROOT$(dirname $f)" \
+      "&&" cp -f "$f" "$ART_TEST_CHROOT$f"
+  done
+
+  # Create directories required for ART testing in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/tmp"
+  adb shell mkdir -p "$ART_TEST_CHROOT/data/dalvik-cache"
+  adb shell mkdir -p "$ART_TEST_CHROOT/data/local/tmp"
+
+  # Populate /etc in chroot with required files.
+  adb shell mkdir -p "$ART_TEST_CHROOT/system/etc"
+  adb shell "cd $ART_TEST_CHROOT && ln -s system/etc etc"
+
+  # Provide /proc in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/proc"
+  adb shell mount | grep -q "^proc on $ART_TEST_CHROOT/proc type proc " \
+    || adb shell mount -t proc proc "$ART_TEST_CHROOT/proc"
+
+  # Provide /sys in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/sys"
+  adb shell mount | grep -q "^sysfs on $ART_TEST_CHROOT/sys type sysfs " \
+    || adb shell mount -t sysfs sysfs "$ART_TEST_CHROOT/sys"
+  # Provide /sys/kernel/debug in chroot.
+  adb shell mount | grep -q "^debugfs on $ART_TEST_CHROOT/sys/kernel/debug type debugfs " \
+    || adb shell mount -t debugfs debugfs "$ART_TEST_CHROOT/sys/kernel/debug"
+
+  # Provide /dev in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/dev"
+  adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \
+    || adb shell mount -o bind /dev "$ART_TEST_CHROOT/dev"
+fi
diff --git a/tools/ti-fast/Android.bp b/tools/ti-fast/Android.bp
new file mode 100644
index 0000000..fd867c9
--- /dev/null
+++ b/tools/ti-fast/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2018 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.
+//
+
+// Build variants {target,host} x {debug,ndebug} x {32,64}
+cc_defaults {
+    name: "tifast-defaults",
+    host_supported: true,
+    srcs: ["tifast.cc"],
+    defaults: ["art_defaults"],
+
+    // Note that this tool needs to be built for both 32-bit and 64-bit since it requires
+    // to be same ISA as what it is attached to.
+    compile_multilib: "both",
+
+    shared_libs: [
+        "libbase",
+    ],
+    header_libs: [
+        "libopenjdkjvmti_headers",
+    ],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+    symlink_preferred_arch: true,
+}
+
+art_cc_library {
+    name: "libtifast",
+    defaults: ["tifast-defaults"],
+}
+
+art_cc_library {
+    name: "libtifastd",
+    defaults: [
+        "art_debug_defaults",
+        "tifast-defaults",
+    ],
+}
diff --git a/tools/ti-fast/README.md b/tools/ti-fast/README.md
new file mode 100644
index 0000000..bc46882
--- /dev/null
+++ b/tools/ti-fast/README.md
@@ -0,0 +1,101 @@
+# tifast
+
+tifast is a JVMTI agent designed for profiling the performance impact listening
+to various JVMTI events. It is called tifast since none of the event handlers do
+anything meaning that it can be considered speed-of-light.
+
+# Usage
+### Build
+>    `make libtifast`
+
+The libraries will be built for 32-bit, 64-bit, host and target. Below examples
+assume you want to use the 64-bit version.
+
+### Command Line
+
+The agent is loaded using -agentpath like normal. It takes arguments in the
+following format:
+>     `[log,][EventName1[,EventName2[,...]]]`
+
+* If 'log' is the first argument the event handlers will LOG(INFO) when they are
+  called. This behavior is static. The no-log methods have no branches and just
+  immediately return.
+
+* The event-names are the same names as are used in the jvmtiEventCallbacks
+  struct.
+
+* All required capabilities are automatically gained. No capabilities other than
+  those needed to listen for the events are gained.
+
+* Only events which do not require additional function calls to cause delivery
+  and are sent more than once are supported.
+
+#### Supported events
+
+The following events may be listened for with this agent
+
+* `SingleStep`
+
+* `MethodEntry`
+
+* `MethodExit`
+
+* `NativeMethodBind`
+
+* `Exception`
+
+* `ExceptionCatch`
+
+* `ThreadStart`
+
+* `ThreadEnd`
+
+* `ClassLoad`
+
+* `ClassPrepare`
+
+* `ClassFileLoadHook`
+
+* `CompiledMethodLoad`
+
+* `CompiledMethodUnload`
+
+* `DynamicCodeGenerated`
+
+* `DataDumpRequest`
+
+* `MonitorContendedEnter`
+
+* `MonitorContendedEntered`
+
+* `MonitorWait`
+
+* `MonitorWaited`
+
+* `ResourceExhausted`
+
+* `VMObjectAlloc`
+
+* `GarbageCollectionStart`
+
+* `GarbageCollectionFinish`
+
+All other events cannot be listened for by this agent. Most of these missing
+events either require the use of other functions in order to be called
+(`FramePop`, `ObjectFree`, etc) or are only called once (`VMInit`, `VMDeath`,
+etc).
+
+#### ART
+>    `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so '-agentpath:libtifast.so=MethodEntry' -cp tmp/java/helloworld.dex -Xint helloworld`
+
+* `-Xplugin` and `-agentpath` need to be used, otherwise the agent will fail during init.
+* If using `libartd.so`, make sure to use the debug version of jvmti.
+
+>    `adb shell setenforce 0`
+>
+>    `adb push $ANDROID_PRODUCT_OUT/system/lib64/libtifast.so /data/local/tmp/`
+>
+>    `adb shell am start-activity --attach-agent /data/local/tmp/libtifast.so=MonitorWait,ClassPrepare some.debuggable.apps/.the.app.MainActivity`
+
+#### RI
+>    `java '-agentpath:libtifast.so=MethodEntry' -cp tmp/helloworld/classes helloworld`
diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc
new file mode 100644
index 0000000..428304e
--- /dev/null
+++ b/tools/ti-fast/tifast.cc
@@ -0,0 +1,205 @@
+// Copyright (C) 2018 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 <android-base/logging.h>
+
+#include <atomic>
+#include <iostream>
+#include <istream>
+#include <iomanip>
+#include <jni.h>
+#include <jvmti.h>
+#include <memory>
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace tifast {
+
+#define EVENT(x) JVMTI_EVENT_ ## x
+
+namespace {
+
+static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) {
+  switch (event) {
+#define DO_CASE(name, cap_name) \
+    case EVENT(name):           \
+      caps->cap_name = 1;       \
+      break
+    DO_CASE(SINGLE_STEP, can_generate_single_step_events);
+    DO_CASE(METHOD_ENTRY, can_generate_method_entry_events);
+    DO_CASE(METHOD_EXIT, can_generate_method_exit_events);
+    DO_CASE(NATIVE_METHOD_BIND, can_generate_native_method_bind_events);
+    DO_CASE(EXCEPTION, can_generate_exception_events);
+    DO_CASE(EXCEPTION_CATCH, can_generate_exception_events);
+    DO_CASE(COMPILED_METHOD_LOAD, can_generate_compiled_method_load_events);
+    DO_CASE(COMPILED_METHOD_UNLOAD, can_generate_compiled_method_load_events);
+    DO_CASE(MONITOR_CONTENDED_ENTER, can_generate_monitor_events);
+    DO_CASE(MONITOR_CONTENDED_ENTERED, can_generate_monitor_events);
+    DO_CASE(MONITOR_WAIT, can_generate_monitor_events);
+    DO_CASE(MONITOR_WAITED, can_generate_monitor_events);
+    DO_CASE(VM_OBJECT_ALLOC, can_generate_vm_object_alloc_events);
+    DO_CASE(GARBAGE_COLLECTION_START, can_generate_garbage_collection_events);
+    DO_CASE(GARBAGE_COLLECTION_FINISH, can_generate_garbage_collection_events);
+#undef DO_CASE
+    default: break;
+  }
+}
+
+// Setup for all supported events. Give a macro with fun(name, event_num, args)
+#define FOR_ALL_SUPPORTED_EVENTS(fun) \
+    fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation)) \
+    fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID)) \
+    fun(MethodExit, EVENT(METHOD_EXIT), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \
+    fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, void*, void**)) \
+    fun(Exception, EVENT(EXCEPTION), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject, jmethodID, jlocation)) \
+    fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject)) \
+    fun(ThreadStart, EVENT(THREAD_START), (jvmtiEnv*, JNIEnv*, jthread)) \
+    fun(ThreadEnd, EVENT(THREAD_END), (jvmtiEnv*, JNIEnv*, jthread)) \
+    fun(ClassLoad, EVENT(CLASS_LOAD), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \
+    fun(ClassPrepare, EVENT(CLASS_PREPARE), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \
+    fun(ClassFileLoadHook, EVENT(CLASS_FILE_LOAD_HOOK), (jvmtiEnv*, JNIEnv*, jclass, jobject, const char*, jobject, jint, const unsigned char*, jint*, unsigned char**)) \
+    fun(CompiledMethodLoad, EVENT(COMPILED_METHOD_LOAD), (jvmtiEnv*, jmethodID, jint, const void*, jint, const jvmtiAddrLocationMap*, const void*)) \
+    fun(CompiledMethodUnload, EVENT(COMPILED_METHOD_UNLOAD), (jvmtiEnv*, jmethodID, const void*)) \
+    fun(DynamicCodeGenerated, EVENT(DYNAMIC_CODE_GENERATED), (jvmtiEnv*, const char*, const void*, jint)) \
+    fun(DataDumpRequest, EVENT(DATA_DUMP_REQUEST), (jvmtiEnv*)) \
+    fun(MonitorContendedEnter, EVENT(MONITOR_CONTENDED_ENTER), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \
+    fun(MonitorContendedEntered, EVENT(MONITOR_CONTENDED_ENTERED), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \
+    fun(MonitorWait, EVENT(MONITOR_WAIT), (jvmtiEnv*, JNIEnv*, jthread, jobject, jlong)) \
+    fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv*, JNIEnv*, jthread, jobject, jboolean)) \
+    fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv*, JNIEnv*, jint, const void*, const char*)) \
+    fun(VMObjectAlloc, EVENT(VM_OBJECT_ALLOC), (jvmtiEnv*, JNIEnv*, jthread, jobject, jclass, jlong)) \
+    fun(GarbageCollectionStart, EVENT(GARBAGE_COLLECTION_START), (jvmtiEnv*)) \
+    fun(GarbageCollectionFinish, EVENT(GARBAGE_COLLECTION_FINISH), (jvmtiEnv*))
+
+#define GENERATE_EMPTY_FUNCTION(name, number, args) \
+    static void JNICALL empty ## name  args { }
+FOR_ALL_SUPPORTED_EVENTS(GENERATE_EMPTY_FUNCTION)
+#undef GENERATE_EMPTY_FUNCTION
+
+static jvmtiEventCallbacks kEmptyCallbacks {
+#define CREATE_EMPTY_EVENT_CALLBACKS(name, num, args) \
+    .name = empty ## name,
+  FOR_ALL_SUPPORTED_EVENTS(CREATE_EMPTY_EVENT_CALLBACKS)
+#undef CREATE_EMPTY_EVENT_CALLBACKS
+};
+
+#define GENERATE_LOG_FUNCTION(name, number, args) \
+    static void JNICALL log ## name  args { \
+      LOG(INFO) << "Got event " << #name ; \
+    }
+FOR_ALL_SUPPORTED_EVENTS(GENERATE_LOG_FUNCTION)
+#undef GENERATE_LOG_FUNCTION
+
+static jvmtiEventCallbacks kLogCallbacks {
+#define CREATE_LOG_EVENT_CALLBACK(name, num, args) \
+    .name = log ## name,
+  FOR_ALL_SUPPORTED_EVENTS(CREATE_LOG_EVENT_CALLBACK)
+#undef CREATE_LOG_EVENT_CALLBACK
+};
+
+static jvmtiEvent NameToEvent(const std::string& desired_name) {
+#define CHECK_NAME(name, event, args) \
+  if (desired_name == #name) { \
+    return event; \
+  }
+  FOR_ALL_SUPPORTED_EVENTS(CHECK_NAME);
+  LOG(FATAL) << "Unknown event " << desired_name;
+  __builtin_unreachable();
+#undef CHECK_NAME
+}
+
+#undef FOR_ALL_SUPPORTED_EVENTS
+static std::vector<jvmtiEvent> GetRequestedEventList(const std::string& args) {
+  std::vector<jvmtiEvent> res;
+  std::stringstream args_stream(args);
+  std::string item;
+  while (std::getline(args_stream, item, ',')) {
+    if (item == "") {
+      continue;
+    }
+    res.push_back(NameToEvent(item));
+  }
+  return res;
+}
+
+}  // namespace
+
+static jint AgentStart(JavaVM* vm,
+                       char* options,
+                       void* reserved ATTRIBUTE_UNUSED) {
+  jvmtiEnv* jvmti = nullptr;
+  jvmtiError error = JVMTI_ERROR_NONE;
+  {
+    jint res = 0;
+    res = vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_1);
+
+    if (res != JNI_OK || jvmti == nullptr) {
+      LOG(ERROR) << "Unable to access JVMTI, error code " << res;
+      return JNI_ERR;
+    }
+  }
+  std::string args(options);
+  bool is_log = false;
+  if (args.compare(0, 3, "log") == 0) {
+    is_log = true;
+    args = args.substr(3);
+  }
+
+  std::vector<jvmtiEvent> events = GetRequestedEventList(args);
+
+  jvmtiCapabilities caps{};
+  for (jvmtiEvent e : events) {
+    AddCapsForEvent(e, &caps);
+  }
+  error = jvmti->AddCapabilities(&caps);
+  if (error != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to set caps";
+    return JNI_ERR;
+  }
+
+  if (is_log) {
+    error = jvmti->SetEventCallbacks(&kLogCallbacks, static_cast<jint>(sizeof(kLogCallbacks)));
+  } else {
+    error = jvmti->SetEventCallbacks(&kEmptyCallbacks, static_cast<jint>(sizeof(kEmptyCallbacks)));
+  }
+  if (error != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to set event callbacks.";
+    return JNI_ERR;
+  }
+  for (jvmtiEvent e : events) {
+    error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+                                            e,
+                                            nullptr /* all threads */);
+    if (error != JVMTI_ERROR_NONE) {
+      LOG(ERROR) << "Unable to enable event " << e;
+      return JNI_ERR;
+    }
+  }
+  return JNI_OK;
+}
+
+// Late attachment (e.g. 'am attach-agent').
+extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) {
+  return AgentStart(vm, options, reserved);
+}
+
+// Early attachment
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
+  return AgentStart(jvm, options, reserved);
+}
+
+}  // namespace tifast
+
diff --git a/tools/tracefast-plugin/Android.bp b/tools/tracefast-plugin/Android.bp
new file mode 100644
index 0000000..1d7dd30
--- /dev/null
+++ b/tools/tracefast-plugin/Android.bp
@@ -0,0 +1,108 @@
+//
+// Copyright (C) 2018 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.
+//
+
+// Build variants {target,host} x {debug,ndebug} x {32,64}
+
+cc_defaults {
+    name: "tracefast-defaults",
+    host_supported: true,
+    srcs: ["tracefast.cc"],
+    defaults: ["art_defaults"],
+
+    // Note that this tool needs to be built for both 32-bit and 64-bit since it requires
+    // to be same ISA as what it is attached to.
+    compile_multilib: "both",
+
+    shared_libs: [
+        "libbase",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "libcutils",
+            ],
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+    header_libs: [
+        "libnativehelper_header_only",
+    ],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+    symlink_preferred_arch: true,
+}
+
+cc_defaults {
+    name: "tracefast-interpreter-defaults",
+    defaults: ["tracefast-defaults"],
+    cflags: ["-DTRACEFAST_INTERPRETER=1"],
+}
+
+cc_defaults {
+    name: "tracefast-trampoline-defaults",
+    defaults: ["tracefast-defaults"],
+    cflags: ["-DTRACEFAST_TRAMPOLINE=1"],
+}
+
+art_cc_library {
+    name: "libtracefast-interpreter",
+    defaults: ["tracefast-interpreter-defaults"],
+    shared_libs: [
+        "libart",
+        "libartbase",
+    ],
+}
+
+art_cc_library {
+    name: "libtracefast-interpreterd",
+    defaults: [
+        "art_debug_defaults",
+        "tracefast-interpreter-defaults",
+    ],
+    shared_libs: [
+        "libartd",
+        "libartbased",
+    ],
+}
+
+art_cc_library {
+    name: "libtracefast-trampoline",
+    defaults: ["tracefast-trampoline-defaults"],
+    shared_libs: [
+        "libart",
+        "libartbase",
+    ],
+}
+
+art_cc_library {
+    name: "libtracefast-trampolined",
+    defaults: [
+        "art_debug_defaults",
+        "tracefast-trampoline-defaults",
+    ],
+    shared_libs: [
+        "libartd",
+        "libartbased",
+    ],
+}
diff --git a/tools/tracefast-plugin/tracefast.cc b/tools/tracefast-plugin/tracefast.cc
new file mode 100644
index 0000000..ed6ac3d
--- /dev/null
+++ b/tools/tracefast-plugin/tracefast.cc
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2018 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 "gc/scoped_gc_critical_section.h"
+#include "instrumentation.h"
+#include "runtime.h"
+#include "runtime_callbacks.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+
+namespace tracefast {
+
+#if ((!defined(TRACEFAST_INTERPRETER) && !defined(TRACEFAST_TRAMPOLINE)) || \
+     (defined(TRACEFAST_INTERPRETER) && defined(TRACEFAST_TRAMPOLINE)))
+#error Must set one of TRACEFAST_TRAMPOLINE or TRACEFAST_INTERPRETER during build
+#endif
+
+
+#ifdef TRACEFAST_INTERPRETER
+static constexpr const char* kTracerInstrumentationKey = "tracefast_INTERPRETER";
+static constexpr bool kNeedsInterpreter = true;
+#else  // defined(TRACEFAST_TRAMPOLINE)
+static constexpr const char* kTracerInstrumentationKey = "tracefast_TRAMPOLINE";
+static constexpr bool kNeedsInterpreter = false;
+#endif  // TRACEFAST_INITERPRETER
+
+class Tracer FINAL : public art::instrumentation::InstrumentationListener {
+ public:
+  Tracer() {}
+
+  void MethodEntered(art::Thread* thread ATTRIBUTE_UNUSED,
+                     art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
+                     art::ArtMethod* method ATTRIBUTE_UNUSED,
+                     uint32_t dex_pc ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void MethodExited(art::Thread* thread ATTRIBUTE_UNUSED,
+                    art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
+                    art::ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED,
+                    art::Handle<art::mirror::Object> return_value ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void MethodExited(art::Thread* thread ATTRIBUTE_UNUSED,
+                    art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
+                    art::ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED,
+                    const art::JValue& return_value ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void MethodUnwind(art::Thread* thread ATTRIBUTE_UNUSED,
+                    art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
+                    art::ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void DexPcMoved(art::Thread* thread ATTRIBUTE_UNUSED,
+                  art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
+                  art::ArtMethod* method ATTRIBUTE_UNUSED,
+                  uint32_t new_dex_pc ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void FieldRead(art::Thread* thread ATTRIBUTE_UNUSED,
+                 art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
+                 art::ArtMethod* method ATTRIBUTE_UNUSED,
+                 uint32_t dex_pc ATTRIBUTE_UNUSED,
+                 art::ArtField* field ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void FieldWritten(art::Thread* thread ATTRIBUTE_UNUSED,
+                    art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
+                    art::ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED,
+                    art::ArtField* field ATTRIBUTE_UNUSED,
+                    art::Handle<art::mirror::Object> field_value ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void FieldWritten(art::Thread* thread ATTRIBUTE_UNUSED,
+                    art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
+                    art::ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED,
+                    art::ArtField* field ATTRIBUTE_UNUSED,
+                    const art::JValue& field_value ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void ExceptionThrown(art::Thread* thread ATTRIBUTE_UNUSED,
+                       art::Handle<art::mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void ExceptionHandled(art::Thread* self ATTRIBUTE_UNUSED,
+                        art::Handle<art::mirror::Throwable> throwable ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void Branch(art::Thread* thread ATTRIBUTE_UNUSED,
+              art::ArtMethod* method ATTRIBUTE_UNUSED,
+              uint32_t dex_pc ATTRIBUTE_UNUSED,
+              int32_t dex_pc_offset ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void InvokeVirtualOrInterface(art::Thread* thread ATTRIBUTE_UNUSED,
+                                art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
+                                art::ArtMethod* caller ATTRIBUTE_UNUSED,
+                                uint32_t dex_pc ATTRIBUTE_UNUSED,
+                                art::ArtMethod* callee ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+  void WatchedFramePop(art::Thread* thread ATTRIBUTE_UNUSED,
+                       const art::ShadowFrame& frame ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Tracer);
+};
+
+Tracer gEmptyTracer;
+
+static void StartTracing() REQUIRES(!art::Locks::mutator_lock_,
+                                    !art::Locks::thread_list_lock_,
+                                    !art::Locks::thread_suspend_count_lock_) {
+  art::Thread* self = art::Thread::Current();
+  art::Runtime* runtime = art::Runtime::Current();
+  art::gc::ScopedGCCriticalSection gcs(self,
+                                       art::gc::kGcCauseInstrumentation,
+                                       art::gc::kCollectorTypeInstrumentation);
+  art::ScopedSuspendAll ssa("starting fast tracing");
+  runtime->GetInstrumentation()->AddListener(&gEmptyTracer,
+                                             art::instrumentation::Instrumentation::kMethodEntered |
+                                             art::instrumentation::Instrumentation::kMethodExited |
+                                             art::instrumentation::Instrumentation::kMethodUnwind);
+  runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey, kNeedsInterpreter);
+}
+
+class TraceFastPhaseCB : public art::RuntimePhaseCallback {
+ public:
+  TraceFastPhaseCB() {}
+
+  void NextRuntimePhase(art::RuntimePhaseCallback::RuntimePhase phase)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    if (phase == art::RuntimePhaseCallback::RuntimePhase::kInit) {
+      art::ScopedThreadSuspension sts(art::Thread::Current(),
+                                      art::ThreadState::kWaitingForMethodTracingStart);
+      StartTracing();
+    }
+  }
+};
+TraceFastPhaseCB gPhaseCallback;
+
+// The plugin initialization function.
+extern "C" bool ArtPlugin_Initialize() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  art::Runtime* runtime = art::Runtime::Current();
+  art::ScopedThreadSuspension stsc(art::Thread::Current(),
+                                   art::ThreadState::kWaitingForMethodTracingStart);
+  art::ScopedSuspendAll ssa("Add phase callback");
+  runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gPhaseCallback);
+  return true;
+}
+
+extern "C" bool ArtPlugin_Deinitialize() {
+  // Don't need to bother doing anything.
+  return true;
+}
+
+}  // namespace tracefast
diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp
index 570960c..5186c43 100644
--- a/tools/veridex/Android.bp
+++ b/tools/veridex/Android.bp
@@ -24,7 +24,11 @@
         "veridex.cc",
     ],
     cflags: ["-Wall", "-Werror"],
-    shared_libs: ["libdexfile", "libbase"],
+    shared_libs: [
+        "libdexfile",
+        "libartbase",
+        "libbase",
+    ],
     header_libs: [
         "art_libartbase_headers",
     ],
diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk
index 4183054..51d924a 100644
--- a/tools/veridex/Android.mk
+++ b/tools/veridex/Android.mk
@@ -18,13 +18,13 @@
 
 system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex
 $(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
-$(system_stub_dex): $(TOPDIR)prebuilts/sdk/system_current/android.jar | $(ZIP2ZIP) $(DX)
+$(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(DX)
 	$(transform-classes-d8.jar-to-dex)
 
 
 oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex
 $(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
-$(oahl_stub_dex): $(TOPDIR)prebuilts/sdk/org.apache.http.legacy/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX)
+$(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX)
 	$(transform-classes-d8.jar-to-dex)
 
 .PHONY: appcompat
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
index 736abb7..154c60f 100644
--- a/tools/veridex/flow_analysis.cc
+++ b/tools/veridex/flow_analysis.cc
@@ -112,7 +112,12 @@
       RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls);
 }
 
-const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) {
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls) {
+  current_registers_[dex_register] =
+      RegisterValue(RegisterSource::kConstant, value, DexFileReference(nullptr, 0), cls);
+}
+
+const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) const {
   return current_registers_[dex_register];
 }
 
@@ -131,6 +136,49 @@
   return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls);
 }
 
+int VeriFlowAnalysis::GetBranchFlags(const Instruction& instruction) const {
+  switch (instruction.Opcode()) {
+    #define IF_XX(cond, op) \
+    case Instruction::IF_##cond: { \
+      RegisterValue lhs = GetRegister(instruction.VRegA()); \
+      RegisterValue rhs = GetRegister(instruction.VRegB()); \
+      if (lhs.IsConstant() && rhs.IsConstant()) { \
+        if (lhs.GetConstant() op rhs.GetConstant()) { \
+          return Instruction::kBranch; \
+        } else { \
+          return Instruction::kContinue; \
+        } \
+      } \
+      break; \
+    } \
+    case Instruction::IF_##cond##Z: { \
+      RegisterValue val = GetRegister(instruction.VRegA()); \
+      if (val.IsConstant()) { \
+        if (val.GetConstant() op 0) { \
+          return Instruction::kBranch; \
+        } else { \
+          return Instruction::kContinue; \
+        } \
+      } \
+      break; \
+    }
+
+    IF_XX(EQ, ==);
+    IF_XX(NE, !=);
+    IF_XX(LT, <);
+    IF_XX(LE, <=);
+    IF_XX(GT, >);
+    IF_XX(GE, >=);
+
+    #undef IF_XX
+
+    default:
+      break;
+  }
+
+  return Instruction::FlagsOf(instruction.Opcode());
+}
+
 void VeriFlowAnalysis::AnalyzeCode() {
   std::vector<uint32_t> work_list;
   work_list.push_back(0);
@@ -149,16 +197,17 @@
       ProcessDexInstruction(inst);
       SetVisited(dex_pc);
 
-      int opcode_flags = Instruction::FlagsOf(inst.Opcode());
-      if ((opcode_flags & Instruction::kContinue) != 0) {
-        if ((opcode_flags & Instruction::kBranch) != 0) {
+      int branch_flags = GetBranchFlags(inst);
+
+      if ((branch_flags & Instruction::kContinue) != 0) {
+        if ((branch_flags & Instruction::kBranch) != 0) {
           uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset();
           if (MergeRegisterValues(branch_dex_pc)) {
             work_list.push_back(branch_dex_pc);
           }
         }
         dex_pc += inst.SizeInCodeUnits();
-      } else if ((opcode_flags & Instruction::kBranch) != 0) {
+      } else if ((branch_flags & Instruction::kBranch) != 0) {
         dex_pc += inst.GetTargetOffset();
         DCHECK(IsBranchTarget(dex_pc));
       } else {
@@ -178,12 +227,30 @@
 
 void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) {
   switch (instruction.Opcode()) {
-    case Instruction::CONST_4:
-    case Instruction::CONST_16:
-    case Instruction::CONST:
+    case Instruction::CONST_4: {
+      int32_t register_index = instruction.VRegA();
+      int32_t value = instruction.VRegB_11n();
+      UpdateRegister(register_index, value, VeriClass::integer_);
+      break;
+    }
+    case Instruction::CONST_16: {
+      int32_t register_index = instruction.VRegA();
+      int32_t value = instruction.VRegB_21s();
+      UpdateRegister(register_index, value, VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::CONST: {
+      int32_t register_index = instruction.VRegA();
+      int32_t value = instruction.VRegB_31i();
+      UpdateRegister(register_index, value, VeriClass::integer_);
+      break;
+    }
+
     case Instruction::CONST_HIGH16: {
       int32_t register_index = instruction.VRegA();
-      UpdateRegister(register_index, VeriClass::integer_);
+      int32_t value = instruction.VRegB_21h();
+      UpdateRegister(register_index, value, VeriClass::integer_);
       break;
     }
 
@@ -243,43 +310,7 @@
     case Instruction::INVOKE_STATIC:
     case Instruction::INVOKE_SUPER:
     case Instruction::INVOKE_VIRTUAL: {
-      VeriMethod method = resolver_->GetMethod(instruction.VRegB_35c());
-      uint32_t args[5];
-      instruction.GetVarArgs(args);
-      if (method == VeriClass::forName_) {
-        RegisterValue value = GetRegister(args[0]);
-        last_result_ = RegisterValue(
-            value.GetSource(), value.GetDexFileReference(), VeriClass::class_);
-      } else if (IsGetField(method)) {
-        RegisterValue cls = GetRegister(args[0]);
-        RegisterValue name = GetRegister(args[1]);
-        field_uses_.push_back(std::make_pair(cls, name));
-        last_result_ = GetReturnType(instruction.VRegB_35c());
-      } else if (IsGetMethod(method)) {
-        RegisterValue cls = GetRegister(args[0]);
-        RegisterValue name = GetRegister(args[1]);
-        method_uses_.push_back(std::make_pair(cls, name));
-        last_result_ = GetReturnType(instruction.VRegB_35c());
-      } else if (method == VeriClass::getClass_) {
-        RegisterValue obj = GetRegister(args[0]);
-        const VeriClass* cls = obj.GetType();
-        if (cls != nullptr && cls->GetClassDef() != nullptr) {
-          const DexFile::ClassDef* def = cls->GetClassDef();
-          last_result_ = RegisterValue(
-              RegisterSource::kClass,
-              DexFileReference(&resolver_->GetDexFileOf(*cls), def->class_idx_.index_),
-              VeriClass::class_);
-        } else {
-          last_result_ = RegisterValue(
-              obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_);
-        }
-      } else if (method == VeriClass::loadClass_) {
-        RegisterValue value = GetRegister(args[1]);
-        last_result_ = RegisterValue(
-            value.GetSource(), value.GetDexFileReference(), VeriClass::class_);
-      } else {
-        last_result_ = GetReturnType(instruction.VRegB_35c());
-      }
+      last_result_ = AnalyzeInvoke(instruction, /* is_range */ false);
       break;
     }
 
@@ -288,7 +319,7 @@
     case Instruction::INVOKE_STATIC_RANGE:
     case Instruction::INVOKE_SUPER_RANGE:
     case Instruction::INVOKE_VIRTUAL_RANGE: {
-      last_result_ = GetReturnType(instruction.VRegB_3rc());
+      last_result_ = AnalyzeInvoke(instruction, /* is_range */ true);
       break;
     }
 
@@ -304,6 +335,8 @@
     case Instruction::RETURN: {
       break;
     }
+
+    // If operations will be handled when looking at the control flow.
     #define IF_XX(cond) \
     case Instruction::IF_##cond: break; \
     case Instruction::IF_##cond##Z: break
@@ -315,6 +348,8 @@
     IF_XX(GT);
     IF_XX(GE);
 
+    #undef IF_XX
+
     case Instruction::GOTO:
     case Instruction::GOTO_16:
     case Instruction::GOTO_32: {
@@ -520,6 +555,7 @@
     case Instruction::IPUT_BYTE:
     case Instruction::IPUT_CHAR:
     case Instruction::IPUT_SHORT: {
+      AnalyzeFieldSet(instruction);
       break;
     }
 
@@ -530,7 +566,13 @@
     case Instruction::SGET_BYTE:
     case Instruction::SGET_CHAR:
     case Instruction::SGET_SHORT: {
-      UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+      uint32_t dest_reg = instruction.VRegA_21c();
+      uint16_t field_index = instruction.VRegB_21c();
+      if (VeriClass::sdkInt_ != nullptr && resolver_->GetField(field_index) == VeriClass::sdkInt_) {
+        UpdateRegister(dest_reg, gTargetSdkVersion, VeriClass::integer_);
+      } else {
+        UpdateRegister(dest_reg, GetFieldType(instruction.VRegC_22c()));
+      }
       break;
     }
 
@@ -541,6 +583,7 @@
     case Instruction::SPUT_BYTE:
     case Instruction::SPUT_CHAR:
     case Instruction::SPUT_SHORT: {
+      AnalyzeFieldSet(instruction);
       break;
     }
 
@@ -613,7 +656,112 @@
 
 void VeriFlowAnalysis::Run() {
   FindBranches();
+  uint32_t number_of_registers = code_item_accessor_.RegistersSize();
+  uint32_t number_of_parameters = code_item_accessor_.InsSize();
+  std::vector<RegisterValue>& initial_values = *dex_registers_[0].get();
+  for (uint32_t i = 0; i < number_of_parameters; ++i) {
+    initial_values[number_of_registers - number_of_parameters + i] = RegisterValue(
+      RegisterSource::kParameter,
+      i,
+      DexFileReference(&resolver_->GetDexFile(), method_id_),
+      nullptr);
+  }
   AnalyzeCode();
 }
 
+static uint32_t GetParameterAt(const Instruction& instruction,
+                               bool is_range,
+                               uint32_t* args,
+                               uint32_t index) {
+  return is_range ? instruction.VRegC() + index : args[index];
+}
+
+RegisterValue FlowAnalysisCollector::AnalyzeInvoke(const Instruction& instruction, bool is_range) {
+  uint32_t id = is_range ? instruction.VRegB_3rc() : instruction.VRegB_35c();
+  VeriMethod method = resolver_->GetMethod(id);
+  uint32_t args[5];
+  if (!is_range) {
+    instruction.GetVarArgs(args);
+  }
+
+  if (method == VeriClass::forName_) {
+    // Class.forName. Fetch the first parameter.
+    RegisterValue value = GetRegister(GetParameterAt(instruction, is_range, args, 0));
+    return RegisterValue(
+        value.GetSource(), value.GetDexFileReference(), VeriClass::class_);
+  } else if (IsGetField(method)) {
+    // Class.getField or Class.getDeclaredField. Fetch the first parameter for the class, and the
+    // second parameter for the field name.
+    RegisterValue cls = GetRegister(GetParameterAt(instruction, is_range, args, 0));
+    RegisterValue name = GetRegister(GetParameterAt(instruction, is_range, args, 1));
+    uses_.push_back(ReflectAccessInfo(cls, name, /* is_method */ false));
+    return GetReturnType(id);
+  } else if (IsGetMethod(method)) {
+    // Class.getMethod or Class.getDeclaredMethod. Fetch the first parameter for the class, and the
+    // second parameter for the field name.
+    RegisterValue cls = GetRegister(GetParameterAt(instruction, is_range, args, 0));
+    RegisterValue name = GetRegister(GetParameterAt(instruction, is_range, args, 1));
+    uses_.push_back(ReflectAccessInfo(cls, name, /* is_method */ true));
+    return GetReturnType(id);
+  } else if (method == VeriClass::getClass_) {
+    // Get the type of the first parameter.
+    RegisterValue obj = GetRegister(GetParameterAt(instruction, is_range, args, 0));
+    const VeriClass* cls = obj.GetType();
+    if (cls != nullptr && cls->GetClassDef() != nullptr) {
+      const DexFile::ClassDef* def = cls->GetClassDef();
+      return RegisterValue(
+          RegisterSource::kClass,
+          DexFileReference(&resolver_->GetDexFileOf(*cls), def->class_idx_.index_),
+          VeriClass::class_);
+    } else {
+      return RegisterValue(
+          obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_);
+    }
+  } else if (method == VeriClass::loadClass_) {
+    // ClassLoader.loadClass. Fetch the first parameter.
+    RegisterValue value = GetRegister(GetParameterAt(instruction, is_range, args, 1));
+    return RegisterValue(
+        value.GetSource(), value.GetDexFileReference(), VeriClass::class_);
+  } else {
+    // Return a RegisterValue referencing the method whose type is the return type
+    // of the method.
+    return GetReturnType(id);
+  }
+}
+
+void FlowAnalysisCollector::AnalyzeFieldSet(const Instruction& instruction ATTRIBUTE_UNUSED) {
+  // There are no fields that escape reflection uses.
+}
+
+RegisterValue FlowAnalysisSubstitutor::AnalyzeInvoke(const Instruction& instruction,
+                                                     bool is_range) {
+  uint32_t id = is_range ? instruction.VRegB_3rc() : instruction.VRegB_35c();
+  MethodReference method(&resolver_->GetDexFile(), id);
+  // TODO: doesn't work for multidex
+  // TODO: doesn't work for overriding (but maybe should be done at a higher level);
+  if (accesses_.find(method) == accesses_.end()) {
+    return GetReturnType(id);
+  }
+  uint32_t args[5];
+  if (!is_range) {
+    instruction.GetVarArgs(args);
+  }
+  for (const ReflectAccessInfo& info : accesses_.at(method)) {
+    if (info.cls.IsParameter() || info.name.IsParameter()) {
+      RegisterValue cls = info.cls.IsParameter()
+          ? GetRegister(GetParameterAt(instruction, is_range, args, info.cls.GetParameterIndex()))
+          : info.cls;
+      RegisterValue name = info.name.IsParameter()
+          ? GetRegister(GetParameterAt(instruction, is_range, args, info.name.GetParameterIndex()))
+          : info.name;
+      uses_.push_back(ReflectAccessInfo(cls, name, info.is_method));
+    }
+  }
+  return GetReturnType(id);
+}
+
+void FlowAnalysisSubstitutor::AnalyzeFieldSet(const Instruction& instruction ATTRIBUTE_UNUSED) {
+  // TODO: analyze field sets.
+}
+
 }  // namespace art
diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h
index 80ae5fc..fc09360 100644
--- a/tools/veridex/flow_analysis.h
+++ b/tools/veridex/flow_analysis.h
@@ -21,13 +21,11 @@
 #include "dex/dex_file_reference.h"
 #include "dex/method_reference.h"
 #include "hidden_api.h"
+#include "resolver.h"
 #include "veridex.h"
 
 namespace art {
 
-class VeridexClass;
-class VeridexResolver;
-
 /**
  * The source where a dex register comes from.
  */
@@ -37,6 +35,7 @@
   kMethod,
   kClass,
   kString,
+  kConstant,
   kNone
 };
 
@@ -45,13 +44,34 @@
  */
 class RegisterValue {
  public:
-  RegisterValue() : source_(RegisterSource::kNone), reference_(nullptr, 0), type_(nullptr) {}
+  RegisterValue() : source_(RegisterSource::kNone),
+                    value_(0),
+                    reference_(nullptr, 0),
+                    type_(nullptr) {}
   RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type)
-      : source_(source), reference_(reference), type_(type) {}
+      : source_(source), value_(0), reference_(reference), type_(type) {}
+
+  RegisterValue(RegisterSource source,
+                uint32_t value,
+                DexFileReference reference,
+                const VeriClass* type)
+      : source_(source), value_(value), reference_(reference), type_(type) {}
 
   RegisterSource GetSource() const { return source_; }
   DexFileReference GetDexFileReference() const { return reference_; }
   const VeriClass* GetType() const { return type_; }
+  uint32_t GetParameterIndex() const {
+    CHECK(IsParameter());
+    return value_;
+  }
+  uint32_t GetConstant() const {
+    CHECK(IsConstant());
+    return value_;
+  }
+  bool IsParameter() const { return source_ == RegisterSource::kParameter; }
+  bool IsClass() const { return source_ == RegisterSource::kClass; }
+  bool IsString() const { return source_ == RegisterSource::kString; }
+  bool IsConstant() const { return source_ == RegisterSource::kConstant; }
 
   std::string ToString() const {
     switch (source_) {
@@ -68,6 +88,8 @@
       }
       case RegisterSource::kClass:
         return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index));
+      case RegisterSource::kParameter:
+        return std::string("Parameter of ") + reference_.dex_file->PrettyMethod(reference_.index);
       default:
         return "<unknown>";
     }
@@ -75,6 +97,7 @@
 
  private:
   RegisterSource source_;
+  uint32_t value_;
   DexFileReference reference_;
   const VeriClass* type_;
 };
@@ -85,22 +108,18 @@
 
 class VeriFlowAnalysis {
  public:
-  VeriFlowAnalysis(VeridexResolver* resolver,
-                   const CodeItemDataAccessor& code_item_accessor)
+  VeriFlowAnalysis(VeridexResolver* resolver, const ClassDataItemIterator& it)
       : resolver_(resolver),
-        code_item_accessor_(code_item_accessor),
-        dex_registers_(code_item_accessor.InsnsSizeInCodeUnits()),
-        instruction_infos_(code_item_accessor.InsnsSizeInCodeUnits()) {}
+        method_id_(it.GetMemberIndex()),
+        code_item_accessor_(resolver->GetDexFile(), it.GetMethodCodeItem()),
+        dex_registers_(code_item_accessor_.InsnsSizeInCodeUnits()),
+        instruction_infos_(code_item_accessor_.InsnsSizeInCodeUnits()) {}
 
   void Run();
 
-  const std::vector<std::pair<RegisterValue, RegisterValue>>& GetFieldUses() const {
-    return field_uses_;
-  }
-
-  const std::vector<std::pair<RegisterValue, RegisterValue>>& GetMethodUses() const {
-    return method_uses_;
-  }
+  virtual RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) = 0;
+  virtual void AnalyzeFieldSet(const Instruction& instruction) = 0;
+  virtual ~VeriFlowAnalysis() {}
 
  private:
   // Find all branches in the code.
@@ -124,14 +143,22 @@
       uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id);
   void UpdateRegister(uint32_t dex_register, const RegisterValue& value);
   void UpdateRegister(uint32_t dex_register, const VeriClass* cls);
-  const RegisterValue& GetRegister(uint32_t dex_register);
+  void UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls);
   void ProcessDexInstruction(const Instruction& inst);
   void SetVisited(uint32_t dex_pc);
-  RegisterValue GetReturnType(uint32_t method_index);
   RegisterValue GetFieldType(uint32_t field_index);
 
+  int GetBranchFlags(const Instruction& instruction) const;
+
+ protected:
+  const RegisterValue& GetRegister(uint32_t dex_register) const;
+  RegisterValue GetReturnType(uint32_t method_index);
+
   VeridexResolver* resolver_;
-  const CodeItemDataAccessor& code_item_accessor_;
+
+ private:
+  const uint32_t method_id_;
+  CodeItemDataAccessor code_item_accessor_;
 
   // Vector of register values for all branch targets.
   std::vector<std::unique_ptr<std::vector<RegisterValue>>> dex_registers_;
@@ -144,12 +171,59 @@
 
   // The value of invoke instructions, to be fetched when visiting move-result.
   RegisterValue last_result_;
+};
 
-  // List of reflection field uses found.
-  std::vector<std::pair<RegisterValue, RegisterValue>> field_uses_;
+struct ReflectAccessInfo {
+  RegisterValue cls;
+  RegisterValue name;
+  bool is_method;
 
-  // List of reflection method uses found.
-  std::vector<std::pair<RegisterValue, RegisterValue>> method_uses_;
+  ReflectAccessInfo(RegisterValue c, RegisterValue n, bool m) : cls(c), name(n), is_method(m) {}
+
+  bool IsConcrete() const {
+    // We capture RegisterSource::kString for the class, for example in Class.forName.
+    return (cls.IsClass() || cls.IsString()) && name.IsString();
+  }
+};
+
+// Collects all reflection uses.
+class FlowAnalysisCollector : public VeriFlowAnalysis {
+ public:
+  FlowAnalysisCollector(VeridexResolver* resolver, const ClassDataItemIterator& it)
+      : VeriFlowAnalysis(resolver, it) {}
+
+  const std::vector<ReflectAccessInfo>& GetUses() const {
+    return uses_;
+  }
+
+  RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) OVERRIDE;
+  void AnalyzeFieldSet(const Instruction& instruction) OVERRIDE;
+
+ private:
+  // List of reflection uses found, concrete and abstract.
+  std::vector<ReflectAccessInfo> uses_;
+};
+
+// Substitutes reflection uses by new ones.
+class FlowAnalysisSubstitutor : public VeriFlowAnalysis {
+ public:
+  FlowAnalysisSubstitutor(VeridexResolver* resolver,
+                          const ClassDataItemIterator& it,
+                          const std::map<MethodReference, std::vector<ReflectAccessInfo>>& accesses)
+      : VeriFlowAnalysis(resolver, it), accesses_(accesses) {}
+
+  const std::vector<ReflectAccessInfo>& GetUses() const {
+    return uses_;
+  }
+
+  RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) OVERRIDE;
+  void AnalyzeFieldSet(const Instruction& instruction) OVERRIDE;
+
+ private:
+  // List of reflection uses found, concrete and abstract.
+  std::vector<ReflectAccessInfo> uses_;
+  // The abstract uses we are trying to subsititute.
+  const std::map<MethodReference, std::vector<ReflectAccessInfo>>& accesses_;
 };
 
 }  // namespace art
diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc
index 4ae5769..89754c2 100644
--- a/tools/veridex/precise_hidden_api_finder.cc
+++ b/tools/veridex/precise_hidden_api_finder.cc
@@ -29,7 +29,9 @@
 
 namespace art {
 
-void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+void PreciseHiddenApiFinder::RunInternal(
+    const std::vector<std::unique_ptr<VeridexResolver>>& resolvers,
+    const std::function<void(VeridexResolver*, const ClassDataItemIterator&)>& action) {
   for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
     const DexFile& dex_file = resolver->GetDexFile();
     size_t class_def_count = dex_file.NumClassDefs();
@@ -47,43 +49,67 @@
         if (code_item == nullptr) {
           continue;
         }
-        CodeItemDataAccessor code_item_accessor(dex_file, code_item);
-        VeriFlowAnalysis ana(resolver.get(), code_item_accessor);
-        ana.Run();
-        if (!ana.GetFieldUses().empty()) {
-          field_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetFieldUses();
-        }
-        if (!ana.GetMethodUses().empty()) {
-          method_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetMethodUses();
-        }
+        action(resolver.get(), it);
       }
     }
   }
 }
 
+void PreciseHiddenApiFinder::AddUsesAt(const std::vector<ReflectAccessInfo>& accesses,
+                                       MethodReference ref) {
+  for (const ReflectAccessInfo& info : accesses) {
+    if (info.IsConcrete()) {
+      concrete_uses_[ref].push_back(info);
+    } else {
+      abstract_uses_[ref].push_back(info);
+    }
+  }
+}
+
+void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+  // Collect reflection uses.
+  RunInternal(resolvers, [this] (VeridexResolver* resolver, const ClassDataItemIterator& it) {
+    FlowAnalysisCollector collector(resolver, it);
+    collector.Run();
+    AddUsesAt(collector.GetUses(), MethodReference(&resolver->GetDexFile(), it.GetMemberIndex()));
+  });
+
+  // For non-final reflection uses, do a limited fixed point calculation over the code to try
+  // substituting them with final reflection uses.
+  // We limit the number of times we iterate over the code as one run can be long.
+  static const int kMaximumIterations = 10;
+  uint32_t i = 0;
+  while (!abstract_uses_.empty() && (i++ < kMaximumIterations)) {
+    // Fetch and clear the worklist.
+    std::map<MethodReference, std::vector<ReflectAccessInfo>> current_uses
+        = std::move(abstract_uses_);
+    RunInternal(resolvers,
+                [this, current_uses] (VeridexResolver* resolver, const ClassDataItemIterator& it) {
+      FlowAnalysisSubstitutor substitutor(resolver, it, current_uses);
+      substitutor.Run();
+      AddUsesAt(substitutor.GetUses(),
+                MethodReference(&resolver->GetDexFile(), it.GetMemberIndex()));
+    });
+  }
+}
+
 void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) {
   static const char* kPrefix = "       ";
-  std::map<std::string, std::vector<MethodReference>> uses;
-  for (auto kinds : { field_uses_, method_uses_ }) {
-    for (auto it : kinds) {
-      MethodReference ref = it.first;
-      for (const std::pair<RegisterValue, RegisterValue>& info : it.second) {
-        if ((info.first.GetSource() == RegisterSource::kClass ||
-             info.first.GetSource() == RegisterSource::kString) &&
-            info.second.GetSource() == RegisterSource::kString) {
-          std::string cls(info.first.ToString());
-          std::string name(info.second.ToString());
-          std::string full_name = cls + "->" + name;
-          HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
-          if (api_list != HiddenApiAccessFlags::kWhitelist) {
-            uses[full_name].push_back(ref);
-          }
-        }
+  std::map<std::string, std::vector<MethodReference>> named_uses;
+  for (auto it : concrete_uses_) {
+    MethodReference ref = it.first;
+    for (const ReflectAccessInfo& info : it.second) {
+      std::string cls(info.cls.ToString());
+      std::string name(info.name.ToString());
+      std::string full_name = cls + "->" + name;
+      HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+      if (api_list != HiddenApiAccessFlags::kWhitelist) {
+        named_uses[full_name].push_back(ref);
       }
     }
   }
 
-  for (auto it : uses) {
+  for (auto it : named_uses) {
     ++stats->reflection_count;
     const std::string& full_name = it.first;
     HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h
index 22744a6..1c4d0ae 100644
--- a/tools/veridex/precise_hidden_api_finder.h
+++ b/tools/veridex/precise_hidden_api_finder.h
@@ -45,9 +45,18 @@
   void Dump(std::ostream& os, HiddenApiStats* stats);
 
  private:
+  // Run over all methods of all dex files, and call `action` on each.
+  void RunInternal(
+      const std::vector<std::unique_ptr<VeridexResolver>>& resolvers,
+      const std::function<void(VeridexResolver*, const ClassDataItemIterator&)>& action);
+
+  // Add uses found in method `ref`.
+  void AddUsesAt(const std::vector<ReflectAccessInfo>& accesses, MethodReference ref);
+
   const HiddenApi& hidden_api_;
-  std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> field_uses_;
-  std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> method_uses_;
+
+  std::map<MethodReference, std::vector<ReflectAccessInfo>> concrete_uses_;
+  std::map<MethodReference, std::vector<ReflectAccessInfo>> abstract_uses_;
 };
 
 }  // namespace art
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index dc7ea94..bcd4815 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -25,6 +25,7 @@
 #include "precise_hidden_api_finder.h"
 #include "resolver.h"
 
+#include <cstdlib>
 #include <sstream>
 
 namespace art {
@@ -62,6 +63,7 @@
 VeriMethod VeriClass::getDeclaredMethod_ = nullptr;
 VeriMethod VeriClass::getClass_ = nullptr;
 VeriMethod VeriClass::loadClass_ = nullptr;
+VeriField VeriClass::sdkInt_ = nullptr;
 
 struct VeridexOptions {
   const char* dex_file = nullptr;
@@ -70,6 +72,7 @@
   const char* light_greylist = nullptr;
   const char* dark_greylist = nullptr;
   bool precise = true;
+  int target_sdk_version = 28; /* P */
 };
 
 static const char* Substr(const char* str, int index) {
@@ -91,6 +94,7 @@
   static const char* kDarkGreylistOption = "--dark-greylist=";
   static const char* kLightGreylistOption = "--light-greylist=";
   static const char* kImprecise = "--imprecise";
+  static const char* kTargetSdkVersion = "--target-sdk-version=";
 
   for (int i = 0; i < argc; ++i) {
     if (StartsWith(argv[i], kDexFileOption)) {
@@ -105,6 +109,8 @@
       options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption));
     } else if (strcmp(argv[i], kImprecise) == 0) {
       options->precise = false;
+    } else if (StartsWith(argv[i], kTargetSdkVersion)) {
+      options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion)));
     }
   }
 }
@@ -124,6 +130,7 @@
   static int Run(int argc, char** argv) {
     VeridexOptions options;
     ParseArgs(&options, argc, argv);
+    gTargetSdkVersion = options.target_sdk_version;
 
     std::vector<std::string> boot_content;
     std::vector<std::string> app_content;
@@ -200,6 +207,11 @@
     VeriClass::loadClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
         *VeriClass::class_loader_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
 
+    VeriClass* version = type_map["Landroid/os/Build$VERSION;"];
+    if (version != nullptr) {
+      VeriClass::sdkInt_ = boot_resolvers[0]->LookupFieldIn(*version, "SDK_INT", "I");
+    }
+
     std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
     Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
 
diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h
index 9c0a158..31ddbf4 100644
--- a/tools/veridex/veridex.h
+++ b/tools/veridex/veridex.h
@@ -24,6 +24,8 @@
 
 namespace art {
 
+static int gTargetSdkVersion = 1000;  // Will be initialized after parsing options.
+
 /**
  * Abstraction for fields defined in dex files. Currently, that's a pointer into their
  * `encoded_field` description.
@@ -86,6 +88,8 @@
   static VeriMethod getClass_;
   static VeriMethod loadClass_;
 
+  static VeriField sdkInt_;
+
  private:
   Primitive::Type kind_;
   uint8_t dimensions_;