Snap for 6533464 from 03a322dcc15561931ed188f4d69f3ef577e26560 to sdk-release

Change-Id: I8e3a9036a2a25ccb827d586c4dc10e41a6ed6c9e
diff --git a/Android.mk b/Android.mk
index d4de2e5..9d76b2d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -497,117 +497,65 @@
   bin/dalvikvm32 \
   bin/dalvikvm64 \
   bin/dalvikvm \
-  bin/dex2oat \
-  bin/dex2oatd \
+  bin/dex2oat32 \
+  bin/dex2oat64 \
   bin/dexdump \
 
 PRIVATE_ART_APEX_DEPENDENCY_LIBS := \
-  lib/libadbconnectiond.so \
   lib/libadbconnection.so \
-  lib/libandroidicu.so \
   lib/libandroidio.so \
-  lib/libartbased.so \
   lib/libartbase.so \
   lib/libart-compiler.so \
-  lib/libartd-compiler.so \
-  lib/libartd-dexlayout.so \
-  lib/libartd-disassembler.so \
   lib/libart-dexlayout.so \
   lib/libart-disassembler.so \
-  lib/libartd.so \
   lib/libartpalette.so \
   lib/libart.so \
-  lib/libbacktrace.so \
-  lib/libbase.so \
-  lib/libcrypto.so \
-  lib/libdexfiled_external.so \
-  lib/libdexfiled.so \
   lib/libdexfile_external.so \
   lib/libdexfile.so \
-  lib/libdexfile_support.so \
   lib/libdt_fd_forward.so \
   lib/libdt_socket.so \
   lib/libexpat.so \
-  lib/libicui18n.so \
-  lib/libicu_jni.so \
-  lib/libicuuc.so \
   lib/libjavacore.so \
   lib/libjdwp.so \
-  lib/liblzma.so \
   lib/libmeminfo.so \
   lib/libnativebridge.so \
   lib/libnativehelper.so \
   lib/libnativeloader.so \
   lib/libnpt.so \
-  lib/libopenjdkd.so \
-  lib/libopenjdkjvmd.so \
   lib/libopenjdkjvm.so \
-  lib/libopenjdkjvmtid.so \
   lib/libopenjdkjvmti.so \
   lib/libopenjdk.so \
   lib/libpac.so \
   lib/libprocinfo.so \
-  lib/libprofiled.so \
   lib/libprofile.so \
-  lib/libsigchain.so \
-  lib/libunwindstack.so \
-  lib/libvixld.so \
   lib/libvixl.so \
-  lib/libziparchive.so \
-  lib/libz.so \
-  lib64/libadbconnectiond.so \
   lib64/libadbconnection.so \
-  lib64/libandroidicu.so \
   lib64/libandroidio.so \
-  lib64/libartbased.so \
   lib64/libartbase.so \
   lib64/libart-compiler.so \
-  lib64/libartd-compiler.so \
-  lib64/libartd-dexlayout.so \
-  lib64/libartd-disassembler.so \
   lib64/libart-dexlayout.so \
   lib64/libart-disassembler.so \
-  lib64/libartd.so \
   lib64/libartpalette.so \
   lib64/libart.so \
-  lib64/libbacktrace.so \
-  lib64/libbase.so \
-  lib64/libcrypto.so \
-  lib64/libdexfiled_external.so \
-  lib64/libdexfiled.so \
   lib64/libdexfile_external.so \
   lib64/libdexfile.so \
-  lib64/libdexfile_support.so \
   lib64/libdt_fd_forward.so \
   lib64/libdt_socket.so \
   lib64/libexpat.so \
-  lib64/libicui18n.so \
-  lib64/libicu_jni.so \
-  lib64/libicuuc.so \
   lib64/libjavacore.so \
   lib64/libjdwp.so \
-  lib64/liblzma.so \
   lib64/libmeminfo.so \
   lib64/libnativebridge.so \
   lib64/libnativehelper.so \
   lib64/libnativeloader.so \
   lib64/libnpt.so \
-  lib64/libopenjdkd.so \
-  lib64/libopenjdkjvmd.so \
   lib64/libopenjdkjvm.so \
-  lib64/libopenjdkjvmtid.so \
   lib64/libopenjdkjvmti.so \
   lib64/libopenjdk.so \
   lib64/libpac.so \
   lib64/libprocinfo.so \
-  lib64/libprofiled.so \
   lib64/libprofile.so \
-  lib64/libsigchain.so \
-  lib64/libunwindstack.so \
-  lib64/libvixld.so \
   lib64/libvixl.so \
-  lib64/libziparchive.so \
-  lib64/libz.so \
 
 PRIVATE_CONSCRYPT_APEX_DEPENDENCY_LIBS := \
   lib/libcrypto.so \
@@ -617,6 +565,45 @@
   lib64/libjavacrypto.so \
   lib64/libssl.so \
 
+PRIVATE_I18N_APEX_DEPENDENCY_LIBS := \
+  lib/libandroidicu.so \
+  lib/libicui18n.so \
+  lib/libicu_jni.so \
+  lib/libicuuc.so \
+  lib64/libandroidicu.so \
+  lib64/libicui18n.so \
+  lib64/libicu_jni.so \
+  lib64/libicuuc.so \
+
+# Extracts files from an APEX into a location. The APEX can be either a .apex
+# file in $(TARGET_OUT)/apex, or a directory in the same location. Files are
+# extracted to $(TARGET_OUT) with the same relative paths as under the APEX
+# root.
+# $(1): APEX base name
+# $(2): List of files to extract, with paths relative to the APEX root
+#
+# "cp -d" below doesn't work on Darwin, but this is only used for Golem builds
+# and won't run on mac anyway.
+define extract-from-apex
+  apex_root=$(TARGET_OUT)/apex && \
+  apex_file=$$apex_root/$(1).apex && \
+  apex_dir=$$apex_root/$(1) && \
+  if [ -f $$apex_file ]; then \
+    rm -rf $$apex_dir && \
+    mkdir -p $$apex_dir && \
+    debugfs=$(HOST_OUT)/bin/debugfs_static && \
+    $(HOST_OUT)/bin/deapexer --debugfs_path $$debugfs extract $$apex_file $$apex_dir; \
+  fi && \
+  for f in $(2); do \
+    sf=$$apex_dir/$$f && \
+    df=$(TARGET_OUT)/$$f && \
+    if [ -f $$sf -o -h $$sf ]; then \
+      mkdir -p $$(dirname $$df) && \
+      cp -fd $$sf $$df; \
+    fi || exit 1; \
+  done
+endef
+
 # Generate copies of Bionic bootstrap artifacts and ART APEX
 # libraries in the `system` (TARGET_OUT) directory. This is dangerous
 # as these files could inadvertently stay in this directory and be
@@ -628,15 +615,7 @@
 # - Bionic bootstrap libraries, copied from
 #   `$(TARGET_OUT)/lib(64)/bootstrap` (the `/system/lib(64)/bootstrap`
 #   directory to be sync'd to the target);
-# - Programs and libraries from the ART APEX; if the product
-#   to build uses flattened APEXes, these libraries are copied from
-#   `$(TARGET_OUT)/apex/com.android.art.debug` (the flattened
-#   (Debug) ART APEX directory to be sync'd to the target);
-#   otherwise, they are copied from
-#   `$(TARGET_OUT)/../apex/com.android.art.debug` (the local
-#   directory under the build tree containing the (Debug) ART APEX
-#   artifacts, which is not sync'd to the target).
-# - Libraries from the Conscrypt APEX may be loaded during golem runs.
+# - Programs and libraries from various APEXes.
 #
 # This target is only used by Golem now.
 #
@@ -647,36 +626,25 @@
 # TODO(b/129332183): Remove this when Golem has full support for the
 # ART APEX.
 .PHONY: standalone-apex-files
-standalone-apex-files: libc.bootstrap \
+standalone-apex-files: deapexer \
+                       libc.bootstrap \
                        libdl.bootstrap \
                        libdl_android.bootstrap \
                        libm.bootstrap \
                        linker \
-                       $(DEBUG_ART_APEX) \
-                       $(CONSCRYPT_APEX)
+                       $(RELEASE_ART_APEX) \
+                       $(CONSCRYPT_APEX) \
+                       $(I18N_APEX)
 	for f in $(PRIVATE_BIONIC_FILES); do \
 	  tf=$(TARGET_OUT)/$$f; \
 	  if [ -f $$tf ]; then cp -f $$tf $$(echo $$tf | sed 's,bootstrap/,,'); fi; \
 	done
-	if [ "x$(TARGET_FLATTEN_APEX)" = xtrue ]; then \
-          apex_orig_dir=$(TARGET_OUT)/apex; \
-	else \
-          apex_orig_dir=""; \
-	fi; \
-	art_apex_orig_dir=$$apex_orig_dir/$(DEBUG_ART_APEX); \
-	for f in $(PRIVATE_ART_APEX_DEPENDENCY_LIBS) $(PRIVATE_ART_APEX_DEPENDENCY_FILES); do \
-	  tf="$$art_apex_orig_dir/$$f"; \
-	  df="$(TARGET_OUT)/$$f"; \
-	  if [ -f $$tf ]; then \
-            if [ -h $$df ]; then rm $$df; fi; \
-            cp -fd $$tf $$df; \
-          fi; \
-	done; \
-	conscrypt_apex_orig_dir=$$apex_orig_dir/$(CONSCRYPT_APEX); \
-	for f in $(PRIVATE_CONSCRYPT_APEX_DEPENDENCY_LIBS); do \
-	  tf="$$conscrypt_apex_orig_dir/$$f"; \
-	  if [ -f $$tf ]; then cp -f $$tf $(TARGET_OUT)/$$f; fi; \
-	done; \
+	$(call extract-from-apex,$(RELEASE_ART_APEX),\
+	  $(PRIVATE_ART_APEX_DEPENDENCY_LIBS) $(PRIVATE_ART_APEX_DEPENDENCY_FILES))
+	$(call extract-from-apex,$(CONSCRYPT_APEX),\
+	  $(PRIVATE_CONSCRYPT_APEX_DEPENDENCY_LIBS))
+	$(call extract-from-apex,$(I18N_APEX),\
+	  $(PRIVATE_I18N_APEX_DEPENDENCY_LIBS))
 
 ########################################################################
 # Phony target for only building what go/lem requires for pushing ART on /data.
@@ -718,11 +686,10 @@
 # ART APEX (and TZ Data APEX).
 
 ART_TARGET_SHARED_LIBRARY_BENCHMARK := $(TARGET_OUT_SHARED_LIBRARIES)/libartbenchmark.so
-build-art-target-golem: dex2oat dalvikvm linker libstdc++ \
+build-art-target-golem: $(RELEASE_ART_APEX) com.android.runtime $(CONSCRYPT_APEX) \
                         $(TARGET_OUT_EXECUTABLES)/art \
+                        $(TARGET_OUT_EXECUTABLES)/dex2oat_wrapper \
                         $(TARGET_OUT)/etc/public.libraries.txt \
-                        $(ART_TARGET_DEX_DEPENDENCIES) \
-                        $(ART_DEBUG_TARGET_SHARED_LIBRARY_DEPENDENCIES) \
                         $(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \
                         $(TARGET_CORE_IMG_OUT_BASE).art \
                         $(TARGET_CORE_IMG_OUT_BASE)-interpreter.art \
@@ -738,6 +705,8 @@
 	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
+	# The 'art' script will look for a 'com.android.art' directory.
+	ln -sf com.android.art.release $(TARGET_OUT)/apex/com.android.art
 
 ########################################################################
 # Phony target for building what go/lem requires on host.
@@ -745,12 +714,14 @@
 # Also include libartbenchmark, we always include it when running golem.
 ART_HOST_SHARED_LIBRARY_BENCHMARK := $(ART_HOST_OUT_SHARED_LIBRARIES)/libartbenchmark.so
 build-art-host-golem: build-art-host \
-                      $(ART_HOST_SHARED_LIBRARY_BENCHMARK)
+                      $(ART_HOST_SHARED_LIBRARY_BENCHMARK) \
+                      $(HOST_OUT_EXECUTABLES)/dex2oat_wrapper
 
 ########################################################################
 # Phony target for building what go/lem requires for syncing /system to target.
 .PHONY: build-art-unbundled-golem
-build-art-unbundled-golem: art-runtime linker oatdump $(ART_APEX_JARS) conscrypt crash_dump
+art_apex_jars := $(foreach pair,$(ART_APEX_JARS), $(call word-colon,2,$(pair)))
+build-art-unbundled-golem: art-runtime linker oatdump $(art_apex_jars) conscrypt crash_dump
 
 ########################################################################
 # Rules for building all dependencies for tests.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..6d8601b
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: RESTRICTED
+}
diff --git a/benchmark/Android.bp b/benchmark/Android.bp
index 3995ca2..826f944 100644
--- a/benchmark/Android.bp
+++ b/benchmark/Android.bp
@@ -25,15 +25,17 @@
         "micro-native/micro_native.cc",
         "scoped-primitive-array/scoped_primitive_array.cc",
     ],
-    shared_libs: [
-        "libart",
-        "libbacktrace",
-        "libbase",
-        "libnativehelper",
-    ],
     cflags: [
         "-Wno-frame-larger-than=",
     ],
+    header_libs: [
+        "libnativehelper_header_only",
+    ],
+    // TODO(ngeoffray): find a way to link against the libraries in the apex.
+    shared_libs: [
+        "libart",
+        "libbase",
+    ],
 }
 
 art_cc_library {
diff --git a/benchmark/scoped-primitive-array/scoped_primitive_array.cc b/benchmark/scoped-primitive-array/scoped_primitive_array.cc
index 005cae4..459e8b1 100644
--- a/benchmark/scoped-primitive-array/scoped_primitive_array.cc
+++ b/benchmark/scoped-primitive-array/scoped_primitive_array.cc
@@ -15,7 +15,7 @@
  */
 
 #include "jni.h"
-#include "nativehelper/ScopedPrimitiveArray.h"
+#include "nativehelper/scoped_primitive_array.h"
 
 extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureByteArray(
     JNIEnv* env, jclass, int reps, jbyteArray arr) {
diff --git a/build/Android.bp b/build/Android.bp
index 3bbeebd..6ac0f5b 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -251,7 +251,7 @@
 
 // A version of conscrypt only for enabling the "-hostdex" version to test ART on host.
 java_library {
-    // We need our own name to not class with the conscrypt library.
+    // We need our own name to not clash with the conscrypt library.
     name: "conscrypt-host",
     installable: true,
     hostdex: true,
@@ -268,3 +268,23 @@
         },
     },
 }
+
+// A version of core-icu4j only for enabling the "-hostdex" version to test ART on host.
+java_library {
+    // We need our own name to not clash with the core-icu4j library.
+    name: "core-icu4j-host",
+    installable: true,
+    hostdex: true,
+    static_libs: ["core-icu4j"],
+
+    // Tests and build files rely on this file to be installed as "core-icu4j-hostdex",
+    // therefore set a stem. Without it, the file would be installed as
+    // "core-icu4j-host-hostdex".
+    stem: "core-icu4j",
+    sdk_version: "core_platform",
+    target: {
+        hostdex: {
+            required: ["libicu_jni"],
+        },
+    },
+}
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index 1d67037..762a3a4 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -74,7 +74,7 @@
 TARGET_CORE_IMG_LOCATION := $(ART_TARGET_TEST_OUT)/core.art
 
 # Modules to compile for core.art.
-CORE_IMG_JARS := core-oj core-libart core-icu4j okhttp bouncycastle apache-xml
+CORE_IMG_JARS := core-oj core-libart okhttp bouncycastle apache-xml
 HOST_CORE_IMG_JARS   := $(addsuffix -hostdex,$(CORE_IMG_JARS))
 TARGET_CORE_IMG_JARS := $(CORE_IMG_JARS)
 HOST_CORE_IMG_DEX_LOCATIONS   := $(foreach jar,$(HOST_CORE_IMG_JARS),  $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar)
@@ -93,19 +93,23 @@
 HOST_BOOT_IMAGE_JARS += $(HOST_OUT)/apex/com.android.conscrypt/javalib/conscrypt.jar
 $(HOST_OUT)/apex/com.android.conscrypt/javalib/conscrypt.jar : $(HOST_OUT_JAVA_LIBRARIES)/conscrypt-hostdex.jar
 	$(copy-file-to-target)
+HOST_BOOT_IMAGE_JARS += $(HOST_OUT)/apex/com.android.i18n/javalib/core-icu4j.jar
+$(HOST_OUT)/apex/com.android.i18n/javalib/core-icu4j.jar : $(HOST_OUT_JAVA_LIBRARIES)/core-icu4j-hostdex.jar
+	$(copy-file-to-target)
 
 HOST_CORE_IMG_OUTS += $(HOST_BOOT_IMAGE_JARS) $(HOST_BOOT_IMAGE) $(2ND_HOST_BOOT_IMAGE)
 
-HOST_TEST_CORE_JARS   := $(addsuffix -hostdex,$(CORE_IMG_JARS) conscrypt)
+HOST_TEST_CORE_JARS   := $(addsuffix -hostdex,$(CORE_IMG_JARS) core-icu4j conscrypt)
 ART_HOST_DEX_DEPENDENCIES := $(foreach jar,$(HOST_TEST_CORE_JARS),$(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar)
-ART_TARGET_DEX_DEPENDENCIES := com.android.art.testing com.android.conscrypt
+ART_TARGET_DEX_DEPENDENCIES := com.android.art.testing com.android.conscrypt com.android.i18n
 
-ART_CORE_SHARED_LIBRARIES := libicu_jni libjavacore libopenjdk libopenjdkjvm libopenjdkjvmti
+ART_CORE_SHARED_LIBRARIES := libjavacore libopenjdk libopenjdkjvm libopenjdkjvmti
 ART_CORE_SHARED_DEBUG_LIBRARIES := libopenjdkd libopenjdkjvmd libopenjdkjvmtid
-ART_HOST_SHARED_LIBRARY_DEPENDENCIES := $(foreach lib,$(ART_CORE_SHARED_LIBRARIES), $(ART_HOST_OUT_SHARED_LIBRARIES)/$(lib)$(ART_HOST_SHLIB_EXTENSION))
+ART_HOST_CORE_SHARED_LIBRARIES := $(ART_CORE_SHARED_LIBRARIES) libandroidicu-host libicuuc-host libicui18n-host libicu_jni
+ART_HOST_SHARED_LIBRARY_DEPENDENCIES := $(foreach lib,$(ART_HOST_CORE_SHARED_LIBRARIES), $(ART_HOST_OUT_SHARED_LIBRARIES)/$(lib)$(ART_HOST_SHLIB_EXTENSION))
 ART_HOST_SHARED_LIBRARY_DEBUG_DEPENDENCIES := $(foreach lib,$(ART_CORE_SHARED_DEBUG_LIBRARIES), $(ART_HOST_OUT_SHARED_LIBRARIES)/$(lib)$(ART_HOST_SHLIB_EXTENSION))
 ifdef HOST_2ND_ARCH
-ART_HOST_SHARED_LIBRARY_DEPENDENCIES += $(foreach lib,$(ART_CORE_SHARED_LIBRARIES), $(2ND_HOST_OUT_SHARED_LIBRARIES)/$(lib).so)
+ART_HOST_SHARED_LIBRARY_DEPENDENCIES += $(foreach lib,$(ART_HOST_CORE_SHARED_LIBRARIES), $(2ND_HOST_OUT_SHARED_LIBRARIES)/$(lib).so)
 ART_HOST_SHARED_LIBRARY_DEBUG_DEPENDENCIES += $(foreach lib,$(ART_CORE_SHARED_DEBUG_LIBRARIES), $(2ND_HOST_OUT_SHARED_LIBRARIES)/$(lib).so)
 endif
 
@@ -156,5 +160,7 @@
 
 # Conscrypt APEX
 CONSCRYPT_APEX := com.android.conscrypt
+# i18n APEX
+I18N_APEX := com.android.i18n
 
 endif # ART_ANDROID_COMMON_PATH_MK
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index f5c9892..2397a09 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -14,6 +14,9 @@
 # limitations under the License.
 #
 
+# Build rules are excluded from Mac, since we can not run ART tests there in the first place.
+ifneq ($(HOST_OS),darwin)
+
 # The path for which all the dex files are relative, not actually the current directory.
 LOCAL_PATH := art/test
 
@@ -254,6 +257,12 @@
 ART_GTEST_oatdump_test_DEX_DEPS := ProfileTestMultiDex
 ART_GTEST_reg_type_test_DEX_DEPS := Interfaces
 
+# Deprecated core.art dependencies.
+HOST_CORE_IMAGE_DEFAULT_32 :=
+HOST_CORE_IMAGE_DEFAULT_64 :=
+TARGET_CORE_IMAGE_DEFAULT_32 :=
+TARGET_CORE_IMAGE_DEFAULT_64 :=
+
 # The elf writer test has dependencies on core.oat.
 ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
 ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32)
@@ -266,156 +275,6 @@
 ART_GTEST_transaction_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
 ART_GTEST_transaction_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32)
 
-ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_optimizing_64) \
-  $(HOST_CORE_IMAGE_optimizing_32) \
-  $(HOST_CORE_IMAGE_interpreter_64) \
-  $(HOST_CORE_IMAGE_interpreter_32)
-ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_optimizing_64) \
-  $(TARGET_CORE_IMAGE_optimizing_32) \
-  $(TARGET_CORE_IMAGE_interpreter_64) \
-  $(TARGET_CORE_IMAGE_interpreter_32)
-
-ART_GTEST_oat_file_test_HOST_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
-  $(HOST_OUT_EXECUTABLES)/dex2oatd
-ART_GTEST_oat_file_test_TARGET_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
-  dex2oatd.com.android.art.debug
-
-ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
-ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
-
-ART_GTEST_dexoptanalyzer_test_HOST_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
-  $(HOST_OUT_EXECUTABLES)/dexoptanalyzerd
-ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
-  $(TESTING_ART_APEX)  # For dexoptanalyzerd.
-
-ART_GTEST_image_space_test_HOST_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
-ART_GTEST_image_space_test_TARGET_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
-
-ART_GTEST_dex2oat_test_HOST_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
-  $(HOST_OUT_EXECUTABLES)/dex2oatd
-ART_GTEST_dex2oat_test_TARGET_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
-  $(TESTING_ART_APEX)  # For dex2oatd.
-
-ART_GTEST_dex2oat_image_test_HOST_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
-  $(HOST_OUT_EXECUTABLES)/dex2oatd
-ART_GTEST_dex2oat_image_test_TARGET_DEPS := \
-  $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
-  $(TESTING_ART_APEX)  # For dex2oatd.
-
-ART_GTEST_module_exclusion_test_HOST_DEPS := \
-  $(ART_GTEST_dex2oat_image_test_HOST_DEPS)
-ART_GTEST_module_exclusion_test_TARGET_DEPS := \
-  $(ART_GTEST_dex2oat_image_test_TARGET_DEPS)
-
-# TODO: document why this is needed.
-ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
-
-# The dexdiag test requires the dexdiag utility.
-ART_GTEST_dexdiag_test_HOST_DEPS := $(HOST_OUT_EXECUTABLES)/dexdiag
-ART_GTEST_dexdiag_test_TARGET_DEPS := $(TESTING_ART_APEX)  # For dexdiag.
-
-# The dexdump test requires an image and the dexdump utility.
-# TODO: rename into dexdump when migration completes
-ART_GTEST_dexdump_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_DEFAULT_64) \
-  $(HOST_CORE_IMAGE_DEFAULT_32) \
-  $(HOST_OUT_EXECUTABLES)/dexdump
-ART_GTEST_dexdump_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_DEFAULT_64) \
-  $(TARGET_CORE_IMAGE_DEFAULT_32) \
-  dexdump.com.android.art.debug
-
-# 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) \
-  $(HOST_OUT_EXECUTABLES)/dexanalyze
-ART_GTEST_dexanalyze_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_DEFAULT_64) \
-  $(TARGET_CORE_IMAGE_DEFAULT_32) \
-  dexanalyze.com.android.art.debug
-
-# The dexlayout test requires an image and the dexlayout utility.
-# TODO: rename into dexdump when migration completes
-ART_GTEST_dexlayout_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_DEFAULT_64) \
-  $(HOST_CORE_IMAGE_DEFAULT_32) \
-  $(HOST_OUT_EXECUTABLES)/dexlayoutd \
-  $(HOST_OUT_EXECUTABLES)/dexdump
-ART_GTEST_dexlayout_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_DEFAULT_64) \
-  $(TARGET_CORE_IMAGE_DEFAULT_32) \
-  dexlayoutd.com.android.art.debug \
-  dexdump.com.android.art.debug
-
-# The dexlist test requires an image and the dexlist utility.
-ART_GTEST_dexlist_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_DEFAULT_64) \
-  $(HOST_CORE_IMAGE_DEFAULT_32) \
-  $(HOST_OUT_EXECUTABLES)/dexlist
-ART_GTEST_dexlist_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_DEFAULT_64) \
-  $(TARGET_CORE_IMAGE_DEFAULT_32) \
-  $(TESTING_ART_APEX)   # For dexlist.
-
-# The imgdiag test has dependencies on core.oat since it needs to load it during the test.
-# For the host, also add the installed tool (in the base size, that should suffice). For the
-# target, just the module is fine, the sync will happen late enough.
-ART_GTEST_imgdiag_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_DEFAULT_64) \
-  $(HOST_CORE_IMAGE_DEFAULT_32) \
-  $(HOST_OUT_EXECUTABLES)/imgdiagd
-ART_GTEST_imgdiag_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_DEFAULT_64) \
-  $(TARGET_CORE_IMAGE_DEFAULT_32) \
-  imgdiagd.com.android.art.debug
-
-# Dex analyze test requires dexanalyze.
-ART_GTEST_dexanalyze_test_HOST_DEPS := \
-  $(HOST_OUT_EXECUTABLES)/dexanalyze
-ART_GTEST_dexanalyze_test_TARGET_DEPS := \
-  dexanalyze.com.android.art.debug
-
-# Oatdump test requires an image and oatfile to dump.
-ART_GTEST_oatdump_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_DEFAULT_64) \
-  $(HOST_CORE_IMAGE_DEFAULT_32) \
-  $(HOST_OUT_EXECUTABLES)/oatdumpd \
-  $(HOST_OUT_EXECUTABLES)/oatdumpds \
-  $(HOST_OUT_EXECUTABLES)/dexdump \
-  $(HOST_OUT_EXECUTABLES)/dex2oatd \
-  $(HOST_OUT_EXECUTABLES)/dex2oatds
-ART_GTEST_oatdump_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_DEFAULT_64) \
-  $(TARGET_CORE_IMAGE_DEFAULT_32) \
-  $(TESTING_ART_APEX)    # For oatdumpd, dexdump, dex2oatd.
-ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS)
-ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS)
-ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS)
-ART_GTEST_oatdump_app_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS)
-
-# Profile assistant tests requires profman utility.
-ART_GTEST_profile_assistant_test_HOST_DEPS := $(HOST_OUT_EXECUTABLES)/profmand
-ART_GTEST_profile_assistant_test_TARGET_DEPS := $(TESTING_ART_APEX)  # For profmand.
-
-ART_GTEST_hiddenapi_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_DEFAULT_64) \
-  $(HOST_CORE_IMAGE_DEFAULT_32) \
-  $(HOST_OUT_EXECUTABLES)/hiddenapid
-
 # The path for which all the source files are relative, not actually the current directory.
 LOCAL_PATH := art
 
@@ -501,6 +360,7 @@
   gtest_exe := $(2)
   # Dependencies for all host gtests.
   gtest_deps := $$(ART_HOST_DEX_DEPENDENCIES) \
+    $$(ART_TEST_HOST_GTEST_DEPENDENCIES) \
     $$(HOST_BOOT_IMAGE_JARS) \
     $$($(3)ART_HOST_OUT_SHARED_LIBRARIES)/libicu_jni$$(ART_HOST_SHLIB_EXTENSION) \
     $$($(3)ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$$(ART_HOST_SHLIB_EXTENSION) \
@@ -532,14 +392,14 @@
     gtest_deps += $$($(3)HOST_BOOT_IMAGE)
   endif
 
-  ART_TEST_HOST_GTEST_DEPENDENCIES += $$(gtest_deps)
-
 .PHONY: $$(gtest_rule)
 $$(gtest_rule): $$(gtest_output)
 
 # Re-run the tests, even if nothing changed. Until the build system has a dedicated "no cache"
 # option, claim to write a file that is never produced.
 $$(gtest_output): .KATI_IMPLICIT_OUTPUTS := $$(gtest_output)-nocache
+# Limit concurrent runs. Each test itself is already highly parallel (and thus memory hungry).
+$$(gtest_output): .KATI_NINJA_POOL := highmem_pool
 $$(gtest_output): NAME := $$(gtest_rule)
 ifeq (,$(SANITIZE_HOST))
 $$(gtest_output): $$(gtest_exe) $$(gtest_deps)
@@ -579,6 +439,27 @@
   gtest_suffix :=
 endef  # define-art-gtest-rule-host
 
+# Global list of all dependencies. All tests depend on all tools.
+# Removal of test_per_src broke the naming convention for dependencies,
+# so the fine-grained dependencies no longer work for now.
+# TODO: Move all dependency tracking to the blueprint file.
+ART_GTEST_ALL_DEX_DEPS := \
+  EmptyUncompressed \
+  EmptyUncompressedAligned \
+  LinkageTest \
+  MainStripped \
+  MainUncompressedAligned \
+  MultiDexUncompressedAligned \
+  VerifierDeps \
+  VerifierDepsMulti  \
+  VerifySoftFailDuringClinit \
+  $(foreach dir,$(GTEST_DEX_DIRECTORIES),$(dir))
+ART_TEST_HOST_GTEST_DEPENDENCIES += \
+  $(foreach file,$(ART_GTEST_ALL_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX))
+ART_TEST_TARGET_GTEST_DEPENDENCIES += \
+  $(TESTING_ART_APEX) \
+  $(foreach file,$(ART_GTEST_ALL_DEX_DEPS),$(ART_TEST_TARGET_GTEST_$(file)_DEX))
+
 # Add the additional dependencies for the specified test
 # $(1): test name
 define add-art-gtest-dependencies
@@ -628,9 +509,9 @@
 ifeq ($(ART_BUILD_TARGET),true)
   $(foreach name,$(ART_TARGET_GTEST_NAMES), $(eval $(call add-art-gtest-dependencies,$(name),)))
   ART_TEST_TARGET_GTEST_DEPENDENCIES += \
-    libicu_jni.com.android.art.debug \
-    libjavacore.com.android.art.debug \
-    libopenjdkd.com.android.art.debug \
+    libicu_jni.com.android.i18n \
+    libjavacore.com.android.art.testing \
+    libopenjdkd.com.android.art.testing \
     com.android.art.testing \
     com.android.conscrypt
 endif
@@ -773,3 +654,5 @@
 ART_TEST_TARGET_GTEST_VerifySoftFailDuringClinit_DEX :=
 GTEST_DEX_DIRECTORIES :=
 LOCAL_PATH :=
+
+endif # ifneq ($(HOST_OS),darwin)
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 638436a..cc12a5a 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -79,6 +79,7 @@
 $$(core_image_name): PRIVATE_CORE_IMAGE_LOCATION := $$(core_image_location)
 $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
 $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
+$$(core_image_name): .KATI_IMPLICIT_OUTPUTS := $$(core_oat_name)
 $$(core_image_name): $$(HOST_CORE_IMG_DEX_LOCATIONS) $$(core_dex2oat_dependency)
 	@echo "host dex2oat: $$@"
 	@mkdir -p $$(dir $$@)
@@ -99,8 +100,6 @@
 	  --no-inline-from=core-oj-hostdex.jar \
 	  $$(PRIVATE_CORE_COMPILE_OPTIONS)
 
-$$(core_oat_name): $$(core_image_name)
-
   # Clean up locally used variables.
   core_dex2oat_dependency :=
   core_compile_options :=
@@ -171,6 +170,7 @@
 $$(core_image_name): PRIVATE_CORE_IMAGE_LOCATION := $$(core_image_location)
 $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
 $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
+$$(core_image_name): .KATI_IMPLICIT_OUTPUTS := $$(core_oat_name)
 $$(core_image_name): $$(TARGET_CORE_IMG_DEX_FILES) $$(core_dex2oat_dependency)
 	@echo "target dex2oat: $$@"
 	@mkdir -p $$(dir $$@)
@@ -191,8 +191,6 @@
 	  --runtime-arg -XX:SlowDebug=true \
 	  $$(PRIVATE_CORE_COMPILE_OPTIONS)
 
-$$(core_oat_name): $$(core_image_name)
-
   # Clean up locally used variables.
   core_dex2oat_dependency :=
   core_compile_options :=
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 706fa96..45a8401 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -141,7 +141,6 @@
 libcore_java_libs = [
     "core-oj",
     "core-libart",
-    "core-icu4j",
     "okhttp",
     "bouncycastle",
     "apache-xml",
@@ -160,14 +159,10 @@
 // the ART APEX.
 libcore_native_shared_libs = [
     // External API (having APEX stubs).
-    "libandroidicu",
     "libandroidio",
     // TODO(b/124476339): Clean up the following libraries once "required"
     // dependencies work with APEX libraries.
     "libexpat",
-    "libicui18n",
-    "libicuuc",
-    "libicu_jni",
     "libjavacore",
     "libopenjdk",
 ]
@@ -175,12 +170,6 @@
     "libopenjdkd",
 ]
 
-libcore_native_device_only_shared_libs = [
-    // TODO(b/122876336): Remove libpac.so once it's migrated to Webview.
-    // libpac is used by frameworks, not by ART host.
-    "libpac",
-]
-
 // Temporary library includes for b/123591866 as all libraries are moved into the main art-apex.
 art_runtime_libraries_zipapex = [
     "libnativebridge",
@@ -208,7 +197,6 @@
     java_libs: libcore_java_libs,
     native_shared_libs: art_runtime_base_native_shared_libs +
         art_runtime_base_native_device_only_shared_libs +
-        libcore_native_device_only_shared_libs +
         libcore_native_shared_libs,
     multilib: {
         both: {
@@ -228,6 +216,14 @@
         "art_apex_boot_integrity",
         "com.android.i18n",
     ],
+    // ART APEXes depend on bouncycastle which is disabled for PDK builds.
+    // Since the dependency is disabled, ART APEXes can't be built either.
+    // Disable the APEXes too. See b/157267166.
+    product_variables: {
+        pdk: {
+            enabled: false,
+        },
+    },
 }
 
 // Default values shared by (device) Debug and Testing ART APEXes.
@@ -357,6 +353,14 @@
             },
         },
     },
+    // ART APEXes depend on bouncycastle which is disabled for PDK builds.
+    // Since the dependency is disabled, ART APEXes can't be built either.
+    // Disable the APEXes too. See b/157267166.
+    product_variables: {
+        pdk: {
+            enabled: false,
+        },
+    },
 }
 
 python_binary_host {
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index 6bccdf5..92505b4 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -542,7 +542,6 @@
     # Check java libraries for Managed Core Library.
     self._checker.check_java_library('apache-xml')
     self._checker.check_java_library('bouncycastle')
-    self._checker.check_java_library('core-icu4j')
     self._checker.check_java_library('core-libart')
     self._checker.check_java_library('core-oj')
     self._checker.check_java_library('okhttp')
@@ -586,7 +585,6 @@
     self._checker.check_dexpreopt('boot')
     self._checker.check_dexpreopt('boot-apache-xml')
     self._checker.check_dexpreopt('boot-bouncycastle')
-    self._checker.check_dexpreopt('boot-core-icu4j')
     self._checker.check_dexpreopt('boot-core-libart')
     self._checker.check_dexpreopt('boot-okhttp')
     if isEnvTrue('EMMA_INSTRUMENT_FRAMEWORK'):
@@ -615,17 +613,11 @@
     self._checker.check_native_library('libperfetto_hprof')
 
     # Check exported native libraries for Managed Core Library.
-    self._checker.check_native_library('libandroidicu')
     self._checker.check_native_library('libandroidio')
 
     # Check internal native library dependencies.
     self._checker.check_native_library('libcrypto')
     self._checker.check_native_library('libexpat')
-    self._checker.check_native_library('libicui18n')
-    self._checker.check_native_library('libicuuc')
-    self._checker.check_native_library('libicu_jni')
-    self._checker.check_native_library('libpac')
-    self._checker.check_native_library('libz')
 
     # TODO(b/139046641): Fix proper 2nd arch checks. For now, just ignore these
     # directories.
@@ -653,9 +645,6 @@
 
     # Check internal libraries for Managed Core Library.
     self._checker.check_native_library('libexpat-host')
-    self._checker.check_native_library('libicui18n-host')
-    self._checker.check_native_library('libicuuc-host')
-    self._checker.check_native_library('libicu_jni')
     self._checker.check_native_library('libz-host')
 
 
@@ -738,246 +727,27 @@
     return 'Testing (Target) Checker'
 
   def run(self):
-    # Check cmdline tests.
-    self._checker.check_optional_art_test_executable('cmdline_parser_test')
-
-    # Check compiler tests.
-    self._checker.check_art_test_executable('atomic_dex_ref_map_test')
-    self._checker.check_art_test_executable('bounds_check_elimination_test')
-    self._checker.check_art_test_executable('codegen_test')
-    self._checker.check_art_test_executable('compiled_method_storage_test')
-    self._checker.check_art_test_executable('data_type_test')
-    self._checker.check_art_test_executable('dedupe_set_test')
-    self._checker.check_art_test_executable('dominator_test')
-    self._checker.check_art_test_executable('dwarf_test')
-    self._checker.check_art_test_executable('exception_test')
-    self._checker.check_art_test_executable('find_loops_test')
-    self._checker.check_art_test_executable('graph_checker_test')
-    self._checker.check_art_test_executable('graph_test')
-    self._checker.check_art_test_executable('gvn_test')
-    self._checker.check_art_test_executable('induction_var_analysis_test')
-    self._checker.check_art_test_executable('induction_var_range_test')
-    self._checker.check_art_test_executable('jni_cfi_test')
-    self._checker.check_art_test_executable('jni_compiler_test')
-    self._checker.check_art_test_executable('licm_test')
-    self._checker.check_art_test_executable('linker_patch_test')
-    self._checker.check_art_test_executable('live_interval_test')
-    self._checker.check_art_test_executable('load_store_analysis_test')
-    self._checker.check_art_test_executable('load_store_elimination_test')
-    self._checker.check_art_test_executable('loop_optimization_test')
-    self._checker.check_art_test_executable('nodes_test')
-    self._checker.check_art_test_executable('nodes_vector_test')
-    self._checker.check_art_test_executable('optimizing_cfi_test')
-    self._checker.check_art_test_executable('output_stream_test')
-    self._checker.check_art_test_executable('parallel_move_test')
-    self._checker.check_art_test_executable('pretty_printer_test')
-    self._checker.check_art_test_executable('reference_type_propagation_test')
-    self._checker.check_art_test_executable('scheduler_test')
-    self._checker.check_art_test_executable('select_generator_test')
-    self._checker.check_art_test_executable('side_effects_test')
-    self._checker.check_art_test_executable('src_map_elem_test')
-    self._checker.check_art_test_executable('ssa_liveness_analysis_test')
-    self._checker.check_art_test_executable('ssa_test')
-    self._checker.check_art_test_executable('stack_map_test')
-    self._checker.check_art_test_executable('superblock_cloner_test')
-    self._checker.check_art_test_executable('suspend_check_test')
-    self._checker.check_art_test_executable('swap_space_test')
-    # These tests depend on a specific code generator and are conditionally included.
-    self._checker.check_optional_art_test_executable('constant_folding_test')
-    self._checker.check_optional_art_test_executable('dead_code_elimination_test')
-    self._checker.check_optional_art_test_executable('linearize_test')
-    self._checker.check_optional_art_test_executable('live_ranges_test')
-    self._checker.check_optional_art_test_executable('liveness_test')
-    self._checker.check_optional_art_test_executable('managed_register_arm64_test')
-    self._checker.check_optional_art_test_executable('managed_register_arm_test')
-    self._checker.check_optional_art_test_executable('managed_register_x86_64_test')
-    self._checker.check_optional_art_test_executable('managed_register_x86_test')
-    self._checker.check_optional_art_test_executable('register_allocator_test')
-
-    # Check dex2oat tests.
-    self._checker.check_art_test_executable('compiler_driver_test')
-    self._checker.check_art_test_executable('dex2oat_image_test')
-    self._checker.check_art_test_executable('dex2oat_test')
-    self._checker.check_art_test_executable('dex_to_dex_decompiler_test')
-    self._checker.check_art_test_executable('elf_writer_test')
-    self._checker.check_art_test_executable('image_test')
-    self._checker.check_art_test_executable('image_write_read_test')
-    self._checker.check_art_test_executable('index_bss_mapping_encoder_test')
-    self._checker.check_art_test_executable('multi_oat_relative_patcher_test')
-    self._checker.check_art_test_executable('oat_writer_test')
-    self._checker.check_art_test_executable('verifier_deps_test')
-    # These tests depend on a specific code generator and are conditionally included.
-    self._checker.check_optional_art_test_executable('relative_patcher_arm64_test')
-    self._checker.check_optional_art_test_executable('relative_patcher_thumb2_test')
-    self._checker.check_optional_art_test_executable('relative_patcher_x86_64_test')
-    self._checker.check_optional_art_test_executable('relative_patcher_x86_test')
-
-    # Check dexanalyze tests.
-    self._checker.check_optional_art_test_executable('dexanalyze_test')
-
-    # Check dexdiag tests.
-    self._checker.check_optional_art_test_executable('dexdiag_test')
-
-    # Check dexdump tests.
-    self._checker.check_art_test_executable('dexdump_test')
-
-    # Check dexlayout tests.
-    self._checker.check_optional_art_test_executable('dexlayout_test')
-
-    # Check dexlist tests.
-    self._checker.check_art_test_executable('dexlist_test')
-
-    # Check dexoptanalyzer tests.
-    self._checker.check_art_test_executable('dexoptanalyzer_test')
-
-    # Check imgdiag tests.
-    self._checker.check_art_test_executable('imgdiag_test')
-
-    # Check libartbase tests.
-    self._checker.check_art_test_executable('arena_allocator_test')
-    self._checker.check_art_test_executable('bit_field_test')
-    self._checker.check_art_test_executable('bit_memory_region_test')
-    self._checker.check_art_test_executable('bit_string_test')
-    self._checker.check_art_test_executable('bit_struct_test')
-    self._checker.check_art_test_executable('bit_table_test')
-    self._checker.check_art_test_executable('bit_utils_test')
-    self._checker.check_art_test_executable('bit_vector_test')
-    self._checker.check_art_test_executable('fd_file_test')
-    self._checker.check_art_test_executable('file_utils_test')
-    self._checker.check_art_test_executable('hash_set_test')
-    self._checker.check_art_test_executable('hex_dump_test')
-    self._checker.check_art_test_executable('histogram_test')
-    self._checker.check_art_test_executable('indenter_test')
-    self._checker.check_art_test_executable('instruction_set_test')
-    self._checker.check_art_test_executable('intrusive_forward_list_test')
-    self._checker.check_art_test_executable('leb128_test')
-    self._checker.check_art_test_executable('logging_test')
-    self._checker.check_art_test_executable('mem_map_test')
-    self._checker.check_art_test_executable('membarrier_test')
-    self._checker.check_art_test_executable('memfd_test')
-    self._checker.check_art_test_executable('memory_region_test')
-    self._checker.check_art_test_executable('safe_copy_test')
-    self._checker.check_art_test_executable('scoped_flock_test')
-    self._checker.check_art_test_executable('time_utils_test')
-    self._checker.check_art_test_executable('transform_array_ref_test')
-    self._checker.check_art_test_executable('transform_iterator_test')
-    self._checker.check_art_test_executable('utils_test')
-    self._checker.check_art_test_executable('variant_map_test')
-    self._checker.check_art_test_executable('zip_archive_test')
-
-    # Check libartpalette tests.
-    self._checker.check_art_test_executable('palette_test')
-
-    # Check libdexfile tests.
-    self._checker.check_art_test_executable('art_dex_file_loader_test')
+    # Check ART test binaries.
+    self._checker.check_art_test_executable('art_cmdline_tests')
+    self._checker.check_art_test_executable('art_compiler_tests')
+    self._checker.check_art_test_executable('art_dex2oat_tests')
+    self._checker.check_art_test_executable('art_dexanalyze_tests')
+    self._checker.check_art_test_executable('art_dexdiag_tests')
+    self._checker.check_art_test_executable('art_dexdump_tests')
+    self._checker.check_art_test_executable('art_dexlayout_tests')
+    self._checker.check_art_test_executable('art_dexlist_tests')
+    self._checker.check_art_test_executable('art_dexoptanalyzer_tests')
+    self._checker.check_art_test_executable('art_imgdiag_tests')
+    self._checker.check_art_test_executable('art_libartbase_tests')
+    self._checker.check_art_test_executable('art_libartpalette_tests')
     self._checker.check_art_test_executable('art_libdexfile_support_tests')
-    self._checker.check_art_test_executable('class_accessor_test')
-    self._checker.check_art_test_executable('code_item_accessors_test')
-    self._checker.check_art_test_executable('compact_dex_file_test')
-    self._checker.check_art_test_executable('compact_offset_table_test')
-    self._checker.check_art_test_executable('descriptors_names_test')
-    self._checker.check_art_test_executable('dex_file_loader_test')
-    self._checker.check_art_test_executable('dex_file_verifier_test')
-    self._checker.check_art_test_executable('dex_instruction_test')
-    self._checker.check_art_test_executable('primitive_test')
-    self._checker.check_art_test_executable('string_reference_test')
-    self._checker.check_art_test_executable('test_dex_file_builder_test')
-    self._checker.check_art_test_executable('type_lookup_table_test')
-    self._checker.check_art_test_executable('utf_test')
-
-    # Check libprofile tests.
-    self._checker.check_optional_art_test_executable('profile_boot_info_test')
-    self._checker.check_optional_art_test_executable('profile_compilation_info_test')
-
-    # Check oatdump tests.
-    self._checker.check_art_test_executable('oatdump_app_test')
-    self._checker.check_art_test_executable('oatdump_image_test')
-    self._checker.check_art_test_executable('oatdump_test')
-
-    # Check profman tests.
-    self._checker.check_art_test_executable('profile_assistant_test')
-
-    # Check runtime compiler tests.
-    self._checker.check_art_test_executable('module_exclusion_test')
-    self._checker.check_art_test_executable('reflection_test')
-
-    # Check runtime tests.
-    self._checker.check_art_test_executable('arch_test')
-    self._checker.check_art_test_executable('barrier_test')
-    self._checker.check_art_test_executable('card_table_test')
-    self._checker.check_art_test_executable('cha_test')
-    self._checker.check_art_test_executable('class_linker_test')
-    self._checker.check_art_test_executable('class_loader_context_test')
-    self._checker.check_art_test_executable('class_table_test')
-    self._checker.check_art_test_executable('compiler_filter_test')
-    self._checker.check_art_test_executable('dex_cache_test')
-    self._checker.check_art_test_executable('dlmalloc_space_random_test')
-    self._checker.check_art_test_executable('dlmalloc_space_static_test')
-    self._checker.check_art_test_executable('entrypoints_order_test')
-    self._checker.check_art_test_executable('exec_utils_test')
-    self._checker.check_art_test_executable('gtest_test')
-    self._checker.check_art_test_executable('handle_scope_test')
-    self._checker.check_art_test_executable('heap_test')
-    self._checker.check_art_test_executable('heap_verification_test')
-    self._checker.check_art_test_executable('hidden_api_test')
-    self._checker.check_art_test_executable('image_space_test')
-    self._checker.check_art_test_executable('immune_spaces_test')
-    self._checker.check_art_test_executable('imtable_test')
-    self._checker.check_art_test_executable('indirect_reference_table_test')
-    self._checker.check_art_test_executable('instruction_set_features_arm64_test')
-    self._checker.check_art_test_executable('instruction_set_features_arm_test')
-    self._checker.check_art_test_executable('instruction_set_features_test')
-    self._checker.check_art_test_executable('instruction_set_features_x86_64_test')
-    self._checker.check_art_test_executable('instruction_set_features_x86_test')
-    self._checker.check_art_test_executable('instrumentation_test')
-    self._checker.check_art_test_executable('intern_table_test')
-    self._checker.check_art_test_executable('java_vm_ext_test')
-    self._checker.check_art_test_executable('jit_memory_region_test')
-    self._checker.check_art_test_executable('jni_internal_test')
-    self._checker.check_art_test_executable('large_object_space_test')
-    self._checker.check_art_test_executable('math_entrypoints_test')
-    self._checker.check_art_test_executable('memcmp16_test')
-    self._checker.check_art_test_executable('method_handles_test')
-    self._checker.check_art_test_executable('method_type_test')
-    self._checker.check_art_test_executable('method_verifier_test')
-    self._checker.check_art_test_executable('mod_union_table_test')
-    self._checker.check_art_test_executable('monitor_pool_test')
-    self._checker.check_art_test_executable('monitor_test')
-    self._checker.check_art_test_executable('mutex_test')
-    self._checker.check_art_test_executable('oat_file_assistant_test')
-    self._checker.check_art_test_executable('oat_file_test')
-    self._checker.check_art_test_executable('object_test')
-    self._checker.check_art_test_executable('parsed_options_test')
-    self._checker.check_art_test_executable('prebuilt_tools_test')
-    self._checker.check_art_test_executable('profiling_info_test')
-    self._checker.check_art_test_executable('profile_saver_test')
-    self._checker.check_art_test_executable('proxy_test')
-    self._checker.check_art_test_executable('quick_trampoline_entrypoints_test')
-    self._checker.check_art_test_executable('reference_queue_test')
-    self._checker.check_art_test_executable('reference_table_test')
-    self._checker.check_art_test_executable('reg_type_test')
-    self._checker.check_art_test_executable('rosalloc_space_random_test')
-    self._checker.check_art_test_executable('rosalloc_space_static_test')
-    self._checker.check_art_test_executable('runtime_callbacks_test')
-    self._checker.check_art_test_executable('runtime_test')
-    self._checker.check_art_test_executable('safe_math_test')
-    self._checker.check_art_test_executable('space_bitmap_test')
-    self._checker.check_art_test_executable('space_create_test')
-    self._checker.check_art_test_executable('stub_test')
-    self._checker.check_art_test_executable('subtype_check_info_test')
-    self._checker.check_art_test_executable('subtype_check_test')
-    self._checker.check_art_test_executable('system_weak_test')
-    self._checker.check_art_test_executable('task_processor_test')
-    self._checker.check_art_test_executable('thread_pool_test')
-    self._checker.check_art_test_executable('timing_logger_test')
-    self._checker.check_art_test_executable('transaction_test')
-    self._checker.check_art_test_executable('two_runtimes_test')
-    self._checker.check_art_test_executable('unstarted_runtime_test')
-    self._checker.check_art_test_executable('var_handle_test')
-    self._checker.check_art_test_executable('vdex_file_test')
-
-    # Check sigchainlib tests.
-    self._checker.check_art_test_executable('sigchain_test')
+    self._checker.check_art_test_executable('art_libdexfile_tests')
+    self._checker.check_art_test_executable('art_libprofile_tests')
+    self._checker.check_art_test_executable('art_oatdump_tests')
+    self._checker.check_art_test_executable('art_profman_tests')
+    self._checker.check_art_test_executable('art_runtime_compiler_tests')
+    self._checker.check_art_test_executable('art_runtime_tests')
+    self._checker.check_art_test_executable('art_sigchain_tests')
 
     # Check ART test (internal) libraries.
     self._checker.check_native_library('libart-gtest')
diff --git a/build/apex/manifest-art.json b/build/apex/manifest-art.json
index b1d4e99..dc4e48a 100644
--- a/build/apex/manifest-art.json
+++ b/build/apex/manifest-art.json
@@ -1,11 +1,11 @@
 {
   "name": "com.android.art",
   "version": 1,
-  "provideNativeLibs": [
-    "libicui18n.so",
-    "libicuuc.so"
-  ],
   "requireNativeLibs": [
+    "libandroidicu.so",
+    "libicuuc.so",
+    "libicui18n.so",
+    "libicu_jni.so",
     "libneuralnetworks.so"
   ]
 }
diff --git a/build/sdk/Android.bp b/build/sdk/Android.bp
index a9048d0..ce1ecd7 100644
--- a/build/sdk/Android.bp
+++ b/build/sdk/Android.bp
@@ -17,20 +17,54 @@
     name: "art-module-sdk",
     host_supported: true,
     native_header_libs: [
+        "jni_headers",
         "libnativehelper_header_only",
     ],
+    native_shared_libs: [
+        "libandroidio",
+    ],
     target: {
         android: {
+            java_header_libs: [
+                // Needed by any module that builds against any non-numeric
+                // sdk_version other than "none".
+                //
+                // This is actually only used for compiling Java 8 and kotlin.
+                // Java 9 uses system modules which encapsulates this
+                // internally.
+                "core-lambda-stubs",
+
+                // Needed by any module that builds against any non-numeric
+                // sdk_version other than "none" or "core_platform".
+                //
+                // This is actually only used for compiling Java 8 and kotlin.
+                // Java 9 uses system modules which encapsulates this
+                // internally.
+                "core.current.stubs",
+
+                // Needed by any module that builds against an sdk_version of
+                // "core_platform".
+                //
+                // This is actually only used for compiling Java 8 and kotlin.
+                // Java 9 uses system modules which encapsulates this
+                // internally.
+                "core.platform.api.stubs",
+            ],
+
             java_system_modules: [
                 "art-module-public-api-stubs-system-modules",
                 "art-module-intra-core-api-stubs-system-modules",
                 "art-module-platform-api-stubs-system-modules",
+                "core-current-stubs-system-modules",
                 "core-platform-api-stubs-system-modules",
             ],
             native_static_libs: [
                 "libartimagevalues",
             ],
         },
+        darwin: {
+            enabled: false,
+        },
     },
 }
 
@@ -45,34 +79,61 @@
             // property is fixed to "both" in the sdk/module_exports
             // implementation and cannot be overridden any other way.
             compile_multilib: "64",
+
+            java_libs: [
+                "timezone-host",
+            ],
+            native_binaries: [
+                "hiddenapi",
+                "dex2oat",
+                "dex2oatd",
+            ],
+            native_shared_libs: [
+                // The following three libraries are internal implementation libraries
+                // that are needed by hiddenapi.
+                // TODO(http://b/155372760): Remove dependencies.
+                "libartbase",
+                "libartpalette",
+                "libdexfile",
+            ],
         },
         darwin: {
             enabled: false,
         },
     },
-    java_libs: [
-        "timezone-host",
-    ],
-    native_binaries: [
-        "hiddenapi",
-        "dex2oat",
-        "dex2oatd",
-    ],
 }
 
 // Exported tests and supporting libraries
 module_exports {
     name: "art-module-test-exports",
+    host_supported: true,
     java_libs: [
-        "core-compat-test-rules",
-        "core-test-rules",
-        "core-tests-support",
-        "okhttp-tests-nojarjar",
+        // The following bouncycastle modules are required by:
+        // * conscrypt-benchmarks (all)
+        // * conscrypt-tests (all)
+        // * signapk (all except ocsp)
+        "bouncycastle-unbundled",
+        "bouncycastle-bcpkix-unbundled",
+        "bouncycastle-ocsp-unbundled",
     ],
-    java_tests: [
-        "libcore-crypto-tests",
-    ],
-    native_shared_libs: [
-        "libjavacoretests",
-    ],
+    target: {
+        android: {
+            java_libs: [
+                "core-compat-test-rules",
+                "core-compat-test-rules",
+                "core-test-rules",
+                "core-tests-support",
+                "okhttp-tests-nojarjar",
+            ],
+            java_tests: [
+                "libcore-crypto-tests",
+            ],
+            native_shared_libs: [
+                "libjavacoretests",
+            ],
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
 }
diff --git a/cmdline/Android.bp b/cmdline/Android.bp
index b46e987..3eac0ed 100644
--- a/cmdline/Android.bp
+++ b/cmdline/Android.bp
@@ -19,6 +19,11 @@
     name: "art_cmdlineparser_headers",
     host_supported: true,
     export_include_dirs: ["."],
+
+    apex_available: [
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 art_cc_test {
diff --git a/compiler/Android.bp b/compiler/Android.bp
index cbfff89..c503eab 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -22,6 +22,12 @@
 art_cc_defaults {
     name: "libart-compiler-defaults",
     defaults: ["art_defaults"],
+    visibility: [
+        // Visibility for prebuilt binaries from the prebuilt of this module.
+        // TODO(b/155921753): Restrict this when prebuilts are in their proper
+        // locations.
+        "//prebuilts:__subpackages__",
+    ],
     host_supported: true,
     srcs: [
         "compiled_method.cc",
@@ -112,7 +118,8 @@
             srcs: [
                 "jni/quick/arm64/calling_convention_arm64.cc",
                 "optimizing/code_generator_arm64.cc",
-                "optimizing/code_generator_vector_arm64.cc",
+                "optimizing/code_generator_vector_arm64_neon.cc",
+                "optimizing/code_generator_vector_arm64_sve.cc",
                 "optimizing/scheduler_arm64.cc",
                 "optimizing/instruction_simplifier_arm64.cc",
                 "optimizing/intrinsics_arm64.cc",
@@ -367,8 +374,9 @@
         "linker/linker_patch_test.cc",
         "linker/output_stream_test.cc",
         "optimizing/bounds_check_elimination_test.cc",
-        "optimizing/superblock_cloner_test.cc",
+        "optimizing/constant_folding_test.cc",
         "optimizing/data_type_test.cc",
+        "optimizing/dead_code_elimination_test.cc",
         "optimizing/dominator_test.cc",
         "optimizing/find_loops_test.cc",
         "optimizing/graph_checker_test.cc",
@@ -377,7 +385,10 @@
         "optimizing/induction_var_analysis_test.cc",
         "optimizing/induction_var_range_test.cc",
         "optimizing/licm_test.cc",
+        "optimizing/linearize_test.cc",
         "optimizing/live_interval_test.cc",
+        "optimizing/live_ranges_test.cc",
+        "optimizing/liveness_test.cc",
         "optimizing/loop_optimization_test.cc",
         "optimizing/nodes_test.cc",
         "optimizing/nodes_vector_test.cc",
@@ -389,6 +400,7 @@
         "optimizing/ssa_liveness_analysis_test.cc",
         "optimizing/ssa_test.cc",
         "optimizing/stack_map_test.cc",
+        "optimizing/superblock_cloner_test.cc",
         "optimizing/suspend_check_test.cc",
         "utils/atomic_dex_ref_map_test.cc",
         "utils/dedupe_set_test.cc",
@@ -417,14 +429,8 @@
             srcs: [
                 "utils/x86/managed_register_x86_test.cc",
 
-                // These tests are testing architecture-independent
-                // functionality, but happen to use x86 codegen as part of the
-                // test.
-                "optimizing/constant_folding_test.cc",
-                "optimizing/dead_code_elimination_test.cc",
-                "optimizing/linearize_test.cc",
-                "optimizing/live_ranges_test.cc",
-                "optimizing/liveness_test.cc",
+                // This test is testing architecture-independent functionality,
+                // but happens to use x86 codegen as part of the test.
                 "optimizing/register_allocator_test.cc",
             ],
         },
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 18f00e2..4b6a557 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -44,6 +44,17 @@
 
 namespace art {
 
+std::unique_ptr<CompilerOptions> CommonCompilerTest::CreateCompilerOptions(
+    InstructionSet instruction_set, const std::string& variant) {
+  std::unique_ptr<CompilerOptions> compiler_options = std::make_unique<CompilerOptions>();
+  compiler_options->instruction_set_ = instruction_set;
+  std::string error_msg;
+  compiler_options->instruction_set_features_ =
+      InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg);
+  CHECK(compiler_options->instruction_set_features_ != nullptr) << error_msg;
+  return compiler_options;
+}
+
 CommonCompilerTest::CommonCompilerTest() {}
 CommonCompilerTest::~CommonCompilerTest() {}
 
@@ -96,7 +107,9 @@
   uintptr_t base = RoundDown(data, kPageSize);
   uintptr_t limit = RoundUp(data + code_length, kPageSize);
   uintptr_t len = limit - base;
-  int result = mprotect(reinterpret_cast<void*>(base), len, PROT_READ | PROT_WRITE | PROT_EXEC);
+  // Remove hwasan tag.  This is done in kernel in newer versions.  This supports older kernels.
+  void* base_ptr = HWASanUntag(reinterpret_cast<void*>(base));
+  int result = mprotect(base_ptr, len, PROT_READ | PROT_WRITE | PROT_EXEC);
   CHECK_EQ(result, 0);
 
   CHECK(FlushCpuCaches(reinterpret_cast<void*>(base), reinterpret_cast<void*>(base + len)));
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 4f4e49a..703e5f8 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -44,6 +44,9 @@
 
 class CommonCompilerTest : public CommonRuntimeTest {
  public:
+  static std::unique_ptr<CompilerOptions> CreateCompilerOptions(InstructionSet instruction_set,
+                                                                const std::string& variant);
+
   CommonCompilerTest();
   ~CommonCompilerTest();
 
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 0550075..a5805f9 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -50,6 +50,7 @@
       dex_files_for_oat_file_(),
       image_classes_(),
       verification_results_(nullptr),
+      compiler_type_(CompilerType::kAotCompiler),
       image_type_(ImageType::kNone),
       compile_art_test_(false),
       baseline_(false),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 6cdb821..7723707 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -69,6 +69,13 @@
   static const size_t kDefaultInlineMaxCodeUnits = 32;
   static constexpr size_t kUnsetInlineMaxCodeUnits = -1;
 
+  enum class CompilerType : uint8_t {
+    kAotCompiler,             // AOT compiler.
+    kJitCompiler,             // Normal JIT compiler.
+    kSharedCodeJitCompiler,   // Zygote JIT producing code in the shared region area, putting
+                              // restrictions on, for example, how literals are being generated.
+  };
+
   enum class ImageType : uint8_t {
     kNone,                    // JIT or AOT app compilation producing only an oat file but no image.
     kBootImage,               // Creating boot image.
@@ -191,6 +198,19 @@
     return implicit_so_checks_;
   }
 
+  bool IsAotCompiler() const {
+    return compiler_type_ == CompilerType::kAotCompiler;
+  }
+
+  bool IsJitCompiler() const {
+    return compiler_type_ == CompilerType::kJitCompiler ||
+           compiler_type_ == CompilerType::kSharedCodeJitCompiler;
+  }
+
+  bool IsJitCompilerForSharedCode() const {
+    return compiler_type_ == CompilerType::kSharedCodeJitCompiler;
+  }
+
   bool GetImplicitSuspendChecks() const {
     return implicit_suspend_checks_;
   }
@@ -394,6 +414,7 @@
   // Results of AOT verification.
   const VerificationResults* verification_results_;
 
+  CompilerType compiler_type_;
   ImageType image_type_;
   bool compile_art_test_;
   bool baseline_;
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index 633e124..7d56da0 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -213,7 +213,7 @@
   // Set up thread to appear as if we called out of method_g_ at given pc dex.
   thread->SetTopOfStack(reinterpret_cast<ArtMethod**>(&fake_stack[0]));
 
-  jobject internal = thread->CreateInternalStackTrace<false>(soa);
+  jobject internal = thread->CreateInternalStackTrace(soa);
   ASSERT_TRUE(internal != nullptr);
   jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
   ASSERT_TRUE(ste_array != nullptr);
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 31dbec4..632a4fb 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -55,6 +55,10 @@
       UNREACHABLE();
     }
   }
+  // Set to appropriate JIT compiler type.
+  compiler_options_->compiler_type_ = runtime->IsZygote()
+      ? CompilerOptions::CompilerType::kSharedCodeJitCompiler
+      : CompilerOptions::CompilerType::kJitCompiler;
   // JIT is never PIC, no matter what the runtime compiler options specify.
   compiler_options_->SetNonPic();
 
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 405c9ec..2db1390 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -1178,7 +1178,7 @@
     ScopedObjectAccess soa(env);
 
     // Build stack trace
-    jobject internal = Thread::Current()->CreateInternalStackTrace<false>(soa);
+    jobject internal = Thread::Current()->CreateInternalStackTrace(soa);
     jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
     ObjPtr<mirror::ObjectArray<mirror::StackTraceElement>> trace_array =
         soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>>(ste_array);
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 64aa1b9..33dbf4e 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -43,8 +43,7 @@
                              const DexCompilationUnit* outer_compilation_unit,
                              CodeGenerator* code_generator,
                              OptimizingCompilerStats* compiler_stats,
-                             ArrayRef<const uint8_t> interpreter_metadata,
-                             VariableSizedHandleScope* handles)
+                             ArrayRef<const uint8_t> interpreter_metadata)
     : graph_(graph),
       dex_file_(&graph->GetDexFile()),
       code_item_accessor_(accessor),
@@ -53,13 +52,11 @@
       code_generator_(code_generator),
       compilation_stats_(compiler_stats),
       interpreter_metadata_(interpreter_metadata),
-      handles_(handles),
       return_type_(DataType::FromShorty(dex_compilation_unit_->GetShorty()[0])) {}
 
 HGraphBuilder::HGraphBuilder(HGraph* graph,
                              const DexCompilationUnit* dex_compilation_unit,
                              const CodeItemDebugInfoAccessor& accessor,
-                             VariableSizedHandleScope* handles,
                              DataType::Type return_type)
     : graph_(graph),
       dex_file_(&graph->GetDexFile()),
@@ -68,7 +65,6 @@
       outer_compilation_unit_(nullptr),
       code_generator_(nullptr),
       compilation_stats_(nullptr),
-      handles_(handles),
       return_type_(return_type) {}
 
 bool HGraphBuilder::SkipCompilation(size_t number_of_branches) {
@@ -119,7 +115,6 @@
   SsaBuilder ssa_builder(graph_,
                          dex_compilation_unit_->GetClassLoader(),
                          dex_compilation_unit_->GetDexCache(),
-                         handles_,
                          &local_allocator);
   HInstructionBuilder instruction_builder(graph_,
                                           &block_builder,
@@ -132,7 +127,6 @@
                                           code_generator_,
                                           interpreter_metadata_,
                                           compilation_stats_,
-                                          handles_,
                                           &local_allocator);
 
   // 1) Create basic blocks and link them together. Basic blocks are left
@@ -190,7 +184,6 @@
   SsaBuilder ssa_builder(graph_,
                          dex_compilation_unit_->GetClassLoader(),
                          dex_compilation_unit_->GetDexCache(),
-                         handles_,
                          &local_allocator);
   HInstructionBuilder instruction_builder(graph_,
                                           &block_builder,
@@ -203,7 +196,6 @@
                                           code_generator_,
                                           interpreter_metadata_,
                                           compilation_stats_,
-                                          handles_,
                                           &local_allocator);
 
   // 1) Create basic blocks for the intrinsic and link them together.
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 6152740..8b76dd9 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -39,14 +39,12 @@
                 const DexCompilationUnit* outer_compilation_unit,
                 CodeGenerator* code_generator,
                 OptimizingCompilerStats* compiler_stats,
-                ArrayRef<const uint8_t> interpreter_metadata,
-                VariableSizedHandleScope* handles);
+                ArrayRef<const uint8_t> interpreter_metadata);
 
   // Only for unit testing.
   HGraphBuilder(HGraph* graph,
                 const DexCompilationUnit* dex_compilation_unit,
                 const CodeItemDebugInfoAccessor& accessor,
-                VariableSizedHandleScope* handles,
                 DataType::Type return_type = DataType::Type::kInt32);
 
   GraphAnalysisResult BuildGraph();
@@ -72,7 +70,6 @@
 
   OptimizingCompilerStats* const compilation_stats_;
   const ArrayRef<const uint8_t> interpreter_metadata_;
-  VariableSizedHandleScope* const handles_;
   const DataType::Type return_type_;
 
   DISALLOW_COPY_AND_ASSIGN(HGraphBuilder);
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 84bf491..ff2be47 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -223,6 +223,9 @@
   virtual const Assembler& GetAssembler() const = 0;
   virtual size_t GetWordSize() const = 0;
 
+  // Returns whether the target supports predicated SIMD instructions.
+  virtual bool SupportsPredicatedSIMD() const { return false; }
+
   // Get FP register width in bytes for spilling/restoring in the slow paths.
   //
   // Note: In SIMD graphs this should return SIMD register width as all FP and SIMD registers
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 001fcb1..fd5f689 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -75,7 +75,6 @@
 using helpers::OutputCPURegister;
 using helpers::OutputFPRegister;
 using helpers::OutputRegister;
-using helpers::QRegisterFrom;
 using helpers::RegisterFrom;
 using helpers::StackOperandFrom;
 using helpers::VIXLRegCodeFromART;
@@ -177,6 +176,7 @@
 
   CPURegList core_list = CPURegList(CPURegister::kRegister, kXRegSize, core_spills);
   const unsigned v_reg_size_in_bits = codegen->GetSlowPathFPWidth() * 8;
+  DCHECK_LE(codegen->GetSIMDRegisterWidth(), kQRegSizeInBytes);
   CPURegList fp_list = CPURegList(CPURegister::kVRegister, v_reg_size_in_bits, fp_spills);
 
   MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler();
@@ -426,10 +426,10 @@
     LocationSummary* locations = instruction_->GetLocations();
     CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
     __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);  // Only saves live 128-bit regs for SIMD.
+    SaveLiveRegisters(codegen, locations);  // Only saves live vector regs for SIMD.
     arm64_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickTestSuspend, void, void>();
-    RestoreLiveRegisters(codegen, locations);  // Only restores live 128-bit regs for SIMD.
+    RestoreLiveRegisters(codegen, locations);  // Only restores live vector regs for SIMD.
     if (successor_ == nullptr) {
       __ B(GetReturnLabel());
     } else {
@@ -883,8 +883,10 @@
                     stats),
       block_labels_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       jump_tables_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
-      location_builder_(graph, this),
-      instruction_visitor_(graph, this),
+      location_builder_neon_(graph, this),
+      instruction_visitor_neon_(graph, this),
+      location_builder_sve_(graph, this),
+      instruction_visitor_sve_(graph, this),
       move_resolver_(graph->GetAllocator(), this),
       assembler_(graph->GetAllocator(),
                  compiler_options.GetInstructionSetFeatures()->AsArm64InstructionSetFeatures()),
@@ -909,6 +911,19 @@
                                          graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
   // Save the link register (containing the return address) to mimic Quick.
   AddAllocatedRegister(LocationFrom(lr));
+
+  bool use_sve = ShouldUseSVE();
+  if (use_sve) {
+    location_builder_ = &location_builder_sve_;
+    instruction_visitor_ = &instruction_visitor_sve_;
+  } else {
+    location_builder_ = &location_builder_neon_;
+    instruction_visitor_ = &instruction_visitor_neon_;
+  }
+}
+
+bool CodeGeneratorARM64::ShouldUseSVE() const {
+  return kArm64AllowSVE && GetInstructionSetFeatures().HasSVE();
 }
 
 #define __ GetVIXLAssembler()->
@@ -923,7 +938,7 @@
   EmitJumpTables();
 
   // Emit JIT baker read barrier slow paths.
-  DCHECK(Runtime::Current()->UseJitCompilation() || jit_baker_read_barrier_slow_paths_.empty());
+  DCHECK(GetCompilerOptions().IsJitCompiler() || jit_baker_read_barrier_slow_paths_.empty());
   for (auto& entry : jit_baker_read_barrier_slow_paths_) {
     uint32_t encoded_data = entry.first;
     vixl::aarch64::Label* slow_path_entry = &entry.second.label;
@@ -1038,9 +1053,9 @@
     scratch = LocationFrom(vixl_temps_.AcquireX());
   } else {
     DCHECK_EQ(kind, Location::kFpuRegister);
-    scratch = LocationFrom(codegen_->GetGraph()->HasSIMD()
-        ? vixl_temps_.AcquireVRegisterOfSize(kQRegSize)
-        : vixl_temps_.AcquireD());
+    scratch = codegen_->GetGraph()->HasSIMD()
+        ? codegen_->GetInstructionCodeGeneratorArm64()->AllocateSIMDScratchLocation(&vixl_temps_)
+        : LocationFrom(vixl_temps_.AcquireD());
   }
   AddScratchLocation(scratch);
   return scratch;
@@ -1051,7 +1066,11 @@
     vixl_temps_.Release(XRegisterFrom(loc));
   } else {
     DCHECK(loc.IsFpuRegister());
-    vixl_temps_.Release(codegen_->GetGraph()->HasSIMD() ? QRegisterFrom(loc) : DRegisterFrom(loc));
+    if (codegen_->GetGraph()->HasSIMD()) {
+      codegen_->GetInstructionCodeGeneratorArm64()->FreeSIMDScratchLocation(loc, &vixl_temps_);
+    } else {
+      vixl_temps_.Release(DRegisterFrom(loc));
+    }
   }
   RemoveScratchLocation(loc);
 }
@@ -1434,7 +1453,7 @@
       DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
       __ Ldr(dst, StackOperandFrom(source));
     } else if (source.IsSIMDStackSlot()) {
-      __ Ldr(QRegisterFrom(destination), StackOperandFrom(source));
+      GetInstructionCodeGeneratorArm64()->LoadSIMDRegFromStack(destination, source);
     } else if (source.IsConstant()) {
       DCHECK(CoherentConstantAndType(source, dst_type));
       MoveConstant(dst, source.GetConstant());
@@ -1458,30 +1477,14 @@
       } else {
         DCHECK(destination.IsFpuRegister());
         if (GetGraph()->HasSIMD()) {
-          __ Mov(QRegisterFrom(destination), QRegisterFrom(source));
+          GetInstructionCodeGeneratorArm64()->MoveSIMDRegToSIMDReg(destination, source);
         } else {
           __ Fmov(VRegister(dst), FPRegisterFrom(source, dst_type));
         }
       }
     }
   } else if (destination.IsSIMDStackSlot()) {
-    if (source.IsFpuRegister()) {
-      __ Str(QRegisterFrom(source), StackOperandFrom(destination));
-    } else {
-      DCHECK(source.IsSIMDStackSlot());
-      UseScratchRegisterScope temps(GetVIXLAssembler());
-      if (GetVIXLAssembler()->GetScratchVRegisterList()->IsEmpty()) {
-        Register temp = temps.AcquireX();
-        __ Ldr(temp, MemOperand(sp, source.GetStackIndex()));
-        __ Str(temp, MemOperand(sp, destination.GetStackIndex()));
-        __ Ldr(temp, MemOperand(sp, source.GetStackIndex() + kArm64WordSize));
-        __ Str(temp, MemOperand(sp, destination.GetStackIndex() + kArm64WordSize));
-      } else {
-        VRegister temp = temps.AcquireVRegisterOfSize(kQRegSize);
-        __ Ldr(temp, StackOperandFrom(source));
-        __ Str(temp, StackOperandFrom(destination));
-      }
-    }
+    GetInstructionCodeGeneratorArm64()->MoveToSIMDStackSlot(destination, source);
   } else {  // The destination is not a register. It must be a stack slot.
     DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
     if (source.IsRegister() || source.IsFpuRegister()) {
@@ -1779,7 +1782,7 @@
   // Reduce code size for AOT by using shared trampolines for slow path runtime calls across the
   // entire oat file. This adds an extra branch and we do not want to slow down the main path.
   // For JIT, thunk sharing is per-method, so the gains would be smaller or even negative.
-  if (slow_path == nullptr || Runtime::Current()->UseJitCompilation()) {
+  if (slow_path == nullptr || GetCompilerOptions().IsJitCompiler()) {
     __ Ldr(lr, MemOperand(tr, entrypoint_offset.Int32Value()));
     // Ensure the pc position is recorded immediately after the `blr` instruction.
     ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize);
@@ -3032,8 +3035,48 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
+// Return true if the magic number was modified by subtracting 2^32. So dividend needs to be added.
+static inline bool NeedToAddDividend(int64_t magic_number, int64_t divisor) {
+  return divisor > 0 && magic_number < 0;
+}
+
+// Return true if the magic number was modified by adding 2^32. So dividend needs to be subtracted.
+static inline bool NeedToSubDividend(int64_t magic_number, int64_t divisor) {
+  return divisor < 0 && magic_number > 0;
+}
+
+// Generate code which increments the value in register 'in' by 1 if the value is negative.
+// It is done with 'add out, in, in, lsr #31 or #63'.
+// If the value is a result of an operation setting the N flag, CINC MI can be used
+// instead of ADD. 'use_cond_inc' controls this.
+void InstructionCodeGeneratorARM64::GenerateIncrementNegativeByOne(
+    Register out,
+    Register in,
+    bool use_cond_inc) {
+  if (use_cond_inc) {
+    __ Cinc(out, in, mi);
+  } else {
+    __ Add(out, in, Operand(in, LSR, in.GetSizeInBits() - 1));
+  }
+}
+
+// Helper to generate code producing the result of HRem with a constant divisor.
+void InstructionCodeGeneratorARM64::GenerateResultRemWithAnyConstant(
+    Register out,
+    Register dividend,
+    Register quotient,
+    int64_t divisor,
+    UseScratchRegisterScope* temps_scope) {
+  // TODO: Strength reduction for msub.
+  Register temp_imm = temps_scope->AcquireSameSizeAs(out);
+  __ Mov(temp_imm, divisor);
+  __ Msub(out, quotient, temp_imm, dividend);
+}
+
+void InstructionCodeGeneratorARM64::GenerateInt64DivRemWithAnyConstant(
+    HBinaryOperation* instruction) {
   DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK(instruction->GetResultType() == DataType::Type::kInt64);
 
   LocationSummary* locations = instruction->GetLocations();
   Location second = locations->InAt(1);
@@ -3043,44 +3086,107 @@
   Register dividend = InputRegisterAt(instruction, 0);
   int64_t imm = Int64FromConstant(second.GetConstant());
 
-  DataType::Type type = instruction->GetResultType();
-  DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
-
   int64_t magic;
   int shift;
-  CalculateMagicAndShiftForDivRem(
-      imm, /* is_long= */ type == DataType::Type::kInt64, &magic, &shift);
+  CalculateMagicAndShiftForDivRem(imm, /* is_long= */ true, &magic, &shift);
 
   UseScratchRegisterScope temps(GetVIXLAssembler());
   Register temp = temps.AcquireSameSizeAs(out);
 
   // temp = get_high(dividend * magic)
   __ Mov(temp, magic);
-  if (type == DataType::Type::kInt64) {
-    __ Smulh(temp, dividend, temp);
-  } else {
-    __ Smull(temp.X(), dividend, temp);
-    __ Lsr(temp.X(), temp.X(), 32);
-  }
+  __ Smulh(temp, dividend, temp);
 
-  if (imm > 0 && magic < 0) {
-    __ Add(temp, temp, dividend);
-  } else if (imm < 0 && magic > 0) {
-    __ Sub(temp, temp, dividend);
+  // The multiplication result might need some corrections to be finalized.
+  // The last correction is to increment by 1, if the result is negative.
+  // Currently it is done with 'add result, temp_result, temp_result, lsr #31 or #63'.
+  // Such ADD usually has latency 2, e.g. on Cortex-A55.
+  // However if one of the corrections is ADD or SUB, the sign can be detected
+  // with ADDS/SUBS. They set the N flag if the result is negative.
+  // This allows to use CINC MI which has latency 1.
+  bool use_cond_inc = false;
+
+  // As magic_number can be modified to fit into 32 bits, check whether the correction is needed.
+  if (NeedToAddDividend(magic, imm)) {
+    __ Adds(temp, temp, dividend);
+    use_cond_inc = true;
+  } else if (NeedToSubDividend(magic, imm)) {
+    __ Subs(temp, temp, dividend);
+    use_cond_inc = true;
   }
 
   if (shift != 0) {
     __ Asr(temp, temp, shift);
   }
 
-  if (instruction->IsDiv()) {
-    __ Sub(out, temp, Operand(temp, ASR, type == DataType::Type::kInt64 ? 63 : 31));
+  if (instruction->IsRem()) {
+    GenerateIncrementNegativeByOne(temp, temp, use_cond_inc);
+    GenerateResultRemWithAnyConstant(out, dividend, temp, imm, &temps);
   } else {
-    __ Sub(temp, temp, Operand(temp, ASR, type == DataType::Type::kInt64 ? 63 : 31));
-    // TODO: Strength reduction for msub.
-    Register temp_imm = temps.AcquireSameSizeAs(out);
-    __ Mov(temp_imm, imm);
-    __ Msub(out, temp, temp_imm, dividend);
+    GenerateIncrementNegativeByOne(out, temp, use_cond_inc);
+  }
+}
+
+void InstructionCodeGeneratorARM64::GenerateInt32DivRemWithAnyConstant(
+    HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+  DCHECK(second.IsConstant());
+
+  Register out = OutputRegister(instruction);
+  Register dividend = InputRegisterAt(instruction, 0);
+  int64_t imm = Int64FromConstant(second.GetConstant());
+
+  int64_t magic;
+  int shift;
+  CalculateMagicAndShiftForDivRem(imm, /* is_long= */ false, &magic, &shift);
+  UseScratchRegisterScope temps(GetVIXLAssembler());
+  Register temp = temps.AcquireSameSizeAs(out);
+
+  // temp = get_high(dividend * magic)
+  __ Mov(temp, magic);
+  __ Smull(temp.X(), dividend, temp);
+
+  // The multiplication result might need some corrections to be finalized.
+  // The last correction is to increment by 1, if the result is negative.
+  // Currently it is done with 'add result, temp_result, temp_result, lsr #31 or #63'.
+  // Such ADD usually has latency 2, e.g. on Cortex-A55.
+  // However if one of the corrections is ADD or SUB, the sign can be detected
+  // with ADDS/SUBS. They set the N flag if the result is negative.
+  // This allows to use CINC MI which has latency 1.
+  bool use_cond_inc = false;
+
+  // ADD/SUB correction is performed in the high 32 bits
+  // as high 32 bits are ignored because type are kInt32.
+  if (NeedToAddDividend(magic, imm)) {
+    __ Adds(temp.X(), temp.X(), Operand(dividend.X(), LSL, 32));
+    use_cond_inc = true;
+  } else if (NeedToSubDividend(magic, imm)) {
+    __ Subs(temp.X(), temp.X(), Operand(dividend.X(), LSL, 32));
+    use_cond_inc = true;
+  }
+
+  // Extract the result from the high 32 bits and apply the final right shift.
+  DCHECK_LT(shift, 32);
+  __ Asr(temp.X(), temp.X(), 32 + shift);
+
+  if (instruction->IsRem()) {
+    GenerateIncrementNegativeByOne(temp, temp, use_cond_inc);
+    GenerateResultRemWithAnyConstant(out, dividend, temp, imm, &temps);
+  } else {
+    GenerateIncrementNegativeByOne(out, temp, use_cond_inc);
+  }
+}
+
+void InstructionCodeGeneratorARM64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  if (instruction->GetResultType() == DataType::Type::kInt64) {
+    GenerateInt64DivRemWithAnyConstant(instruction);
+  } else {
+    GenerateInt32DivRemWithAnyConstant(instruction);
   }
 }
 
@@ -4423,7 +4529,7 @@
 
 void CodeGeneratorARM64::EmitEntrypointThunkCall(ThreadOffset64 entrypoint_offset) {
   DCHECK(!__ AllowMacroInstructions());  // In ExactAssemblyScope.
-  DCHECK(!Runtime::Current()->UseJitCompilation());
+  DCHECK(!GetCompilerOptions().IsJitCompiler());
   call_entrypoint_patches_.emplace_back(/*dex_file*/ nullptr, entrypoint_offset.Uint32Value());
   vixl::aarch64::Label* bl_label = &call_entrypoint_patches_.back().label;
   __ bind(bl_label);
@@ -4432,7 +4538,7 @@
 
 void CodeGeneratorARM64::EmitBakerReadBarrierCbnz(uint32_t custom_data) {
   DCHECK(!__ AllowMacroInstructions());  // In ExactAssemblyScope.
-  if (Runtime::Current()->UseJitCompilation()) {
+  if (GetCompilerOptions().IsJitCompiler()) {
     auto it = jit_baker_read_barrier_slow_paths_.FindOrAdd(custom_data);
     vixl::aarch64::Label* slow_path_entry = &it->second.label;
     __ cbnz(mr, slow_path_entry);
@@ -4523,7 +4629,7 @@
     vixl::aarch64::Label* ldr_label = NewBootImageRelRoPatch(boot_image_reference, adrp_label);
     EmitLdrOffsetPlaceholder(ldr_label, reg.W(), reg.X());
   } else {
-    DCHECK(Runtime::Current()->UseJitCompilation());
+    DCHECK(GetCompilerOptions().IsJitCompiler());
     gc::Heap* heap = Runtime::Current()->GetHeap();
     DCHECK(!heap->GetBootImageSpaces().empty());
     const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference;
@@ -4735,11 +4841,11 @@
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
+      DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kJitBootImageAddress:
     case HLoadClass::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
+      DCHECK(GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kRuntimeCall:
       break;
@@ -4954,11 +5060,11 @@
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
+      DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadString::LoadKind::kJitBootImageAddress:
     case HLoadString::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
+      DCHECK(GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadString::LoadKind::kRuntimeCall:
       break;
@@ -6372,6 +6478,39 @@
   }
 }
 
+MemOperand InstructionCodeGeneratorARM64::VecNeonAddress(
+    HVecMemoryOperation* instruction,
+    UseScratchRegisterScope* temps_scope,
+    size_t size,
+    bool is_string_char_at,
+    /*out*/ Register* scratch) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register base = InputRegisterAt(instruction, 0);
+
+  if (instruction->InputAt(1)->IsIntermediateAddressIndex()) {
+    DCHECK(!is_string_char_at);
+    return MemOperand(base.X(), InputRegisterAt(instruction, 1).X());
+  }
+
+  Location index = locations->InAt(1);
+  uint32_t offset = is_string_char_at
+      ? mirror::String::ValueOffset().Uint32Value()
+      : mirror::Array::DataOffset(size).Uint32Value();
+  size_t shift = ComponentSizeShiftWidth(size);
+
+  // HIntermediateAddress optimization is only applied for scalar ArrayGet and ArraySet.
+  DCHECK(!instruction->InputAt(0)->IsIntermediateAddress());
+
+  if (index.IsConstant()) {
+    offset += Int64FromLocation(index) << shift;
+    return HeapOperand(base, offset);
+  } else {
+    *scratch = temps_scope->AcquireSameSizeAs(base);
+    __ Add(*scratch, base, Operand(WRegisterFrom(index), LSL, shift));
+    return HeapOperand(*scratch, offset);
+  }
+}
+
 #undef __
 #undef QUICK_ENTRY_POINT
 
@@ -6538,10 +6677,8 @@
   }
 
   // For JIT, the slow path is considered part of the compiled method,
-  // so JIT should pass null as `debug_name`. Tests may not have a runtime.
-  DCHECK(Runtime::Current() == nullptr ||
-         !Runtime::Current()->UseJitCompilation() ||
-         debug_name == nullptr);
+  // so JIT should pass null as `debug_name`.
+  DCHECK(!GetCompilerOptions().IsJitCompiler() || debug_name == nullptr);
   if (debug_name != nullptr && GetCompilerOptions().GenerateAnyDebugInfo()) {
     std::ostringstream oss;
     oss << "BakerReadBarrierThunk";
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 47a0194..8349732 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -53,6 +53,9 @@
 static constexpr int kMaxMacroInstructionSizeInBytes = 15 * vixl::aarch64::kInstructionSize;
 static constexpr int kInvokeCodeMarginSizeInBytes = 6 * kMaxMacroInstructionSizeInBytes;
 
+// SVE is currently not enabled.
+static constexpr bool kArm64AllowSVE = false;
+
 static const vixl::aarch64::Register kParameterCoreRegisters[] = {
   vixl::aarch64::x1,
   vixl::aarch64::x2,
@@ -262,7 +265,7 @@
 #define DECLARE_VISIT_INSTRUCTION(name, super) \
   void Visit##name(H##name* instr) override;
 
-  FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_SCALAR_COMMON(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
 
@@ -276,7 +279,15 @@
   Arm64Assembler* GetAssembler() const { return assembler_; }
   vixl::aarch64::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
 
- private:
+  // SIMD helpers.
+  virtual Location AllocateSIMDScratchLocation(vixl::aarch64::UseScratchRegisterScope* scope) = 0;
+  virtual void FreeSIMDScratchLocation(Location loc,
+                                       vixl::aarch64::UseScratchRegisterScope* scope)  = 0;
+  virtual void LoadSIMDRegFromStack(Location destination, Location source) = 0;
+  virtual void MoveSIMDRegToSIMDReg(Location destination, Location source) = 0;
+  virtual void MoveToSIMDStackSlot(Location destination, Location source) = 0;
+
+ protected:
   void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,
                                         vixl::aarch64::Register class_reg);
   void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
@@ -331,6 +342,16 @@
                              vixl::aarch64::Label* false_target);
   void DivRemOneOrMinusOne(HBinaryOperation* instruction);
   void DivRemByPowerOfTwo(HBinaryOperation* instruction);
+  void GenerateIncrementNegativeByOne(vixl::aarch64::Register out,
+                                      vixl::aarch64::Register in, bool use_cond_inc);
+  void GenerateResultRemWithAnyConstant(vixl::aarch64::Register out,
+                                        vixl::aarch64::Register dividend,
+                                        vixl::aarch64::Register quotient,
+                                        int64_t divisor,
+                                        // This function may acquire a scratch register.
+                                        vixl::aarch64::UseScratchRegisterScope* temps_scope);
+  void GenerateInt64DivRemWithAnyConstant(HBinaryOperation* instruction);
+  void GenerateInt32DivRemWithAnyConstant(HBinaryOperation* instruction);
   void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
   void GenerateIntDiv(HDiv* instruction);
   void GenerateIntDivForConstDenom(HDiv *instruction);
@@ -340,7 +361,11 @@
   void GenerateIntRemForPower2Denom(HRem *instruction);
   void HandleGoto(HInstruction* got, HBasicBlock* successor);
 
-  vixl::aarch64::MemOperand VecAddress(
+  // Helper to set up locations for vector memory operations. Returns the memory operand and,
+  // if used, sets the output parameter scratch to a temporary register used in this operand,
+  // so that the client can release it right after the memory operand use.
+  // Neon version.
+  vixl::aarch64::MemOperand VecNeonAddress(
       HVecMemoryOperation* instruction,
       // This function may acquire a scratch register.
       vixl::aarch64::UseScratchRegisterScope* temps_scope,
@@ -362,7 +387,7 @@
 #define DECLARE_VISIT_INSTRUCTION(name, super) \
   void Visit##name(H##name* instr) override;
 
-  FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_SCALAR_COMMON(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
 
@@ -373,7 +398,7 @@
                << " (id " << instruction->GetId() << ")";
   }
 
- private:
+ protected:
   void HandleBinaryOp(HBinaryOperation* instr);
   void HandleFieldSet(HInstruction* instruction);
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
@@ -387,6 +412,72 @@
   DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARM64);
 };
 
+class InstructionCodeGeneratorARM64Neon : public InstructionCodeGeneratorARM64 {
+ public:
+  InstructionCodeGeneratorARM64Neon(HGraph* graph, CodeGeneratorARM64* codegen) :
+      InstructionCodeGeneratorARM64(graph, codegen) {}
+
+#define DECLARE_VISIT_INSTRUCTION(name, super) \
+  void Visit##name(H##name* instr) override;
+
+  FOR_EACH_CONCRETE_INSTRUCTION_VECTOR_COMMON(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+
+  Location AllocateSIMDScratchLocation(vixl::aarch64::UseScratchRegisterScope* scope) override;
+  void FreeSIMDScratchLocation(Location loc,
+                               vixl::aarch64::UseScratchRegisterScope* scope) override;
+  void LoadSIMDRegFromStack(Location destination, Location source) override;
+  void MoveSIMDRegToSIMDReg(Location destination, Location source) override;
+  void MoveToSIMDStackSlot(Location destination, Location source) override;
+};
+
+class LocationsBuilderARM64Neon : public LocationsBuilderARM64 {
+ public:
+  LocationsBuilderARM64Neon(HGraph* graph, CodeGeneratorARM64* codegen) :
+      LocationsBuilderARM64(graph, codegen) {}
+
+#define DECLARE_VISIT_INSTRUCTION(name, super) \
+  void Visit##name(H##name* instr) override;
+
+  FOR_EACH_CONCRETE_INSTRUCTION_VECTOR_COMMON(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+};
+
+class InstructionCodeGeneratorARM64Sve : public InstructionCodeGeneratorARM64 {
+ public:
+  InstructionCodeGeneratorARM64Sve(HGraph* graph, CodeGeneratorARM64* codegen) :
+      InstructionCodeGeneratorARM64(graph, codegen) {}
+
+#define DECLARE_VISIT_INSTRUCTION(name, super) \
+  void Visit##name(H##name* instr) override;
+
+  FOR_EACH_CONCRETE_INSTRUCTION_VECTOR_COMMON(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+
+  Location AllocateSIMDScratchLocation(vixl::aarch64::UseScratchRegisterScope* scope) override;
+  void FreeSIMDScratchLocation(Location loc,
+                               vixl::aarch64::UseScratchRegisterScope* scope) override;
+  void LoadSIMDRegFromStack(Location destination, Location source) override;
+  void MoveSIMDRegToSIMDReg(Location destination, Location source) override;
+  void MoveToSIMDStackSlot(Location destination, Location source) override;
+};
+
+class LocationsBuilderARM64Sve : public LocationsBuilderARM64 {
+ public:
+  LocationsBuilderARM64Sve(HGraph* graph, CodeGeneratorARM64* codegen) :
+      LocationsBuilderARM64(graph, codegen) {}
+
+#define DECLARE_VISIT_INSTRUCTION(name, super) \
+  void Visit##name(H##name* instr) override;
+
+  FOR_EACH_CONCRETE_INSTRUCTION_VECTOR_COMMON(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+};
+
 class ParallelMoveResolverARM64 : public ParallelMoveResolverNoSwap {
  public:
   ParallelMoveResolverARM64(ArenaAllocator* allocator, CodeGeneratorARM64* codegen)
@@ -435,6 +526,8 @@
     return kArm64WordSize;
   }
 
+  bool SupportsPredicatedSIMD() const override { return ShouldUseSVE(); }
+
   size_t GetSlowPathFPWidth() const override {
     return GetGraph()->HasSIMD()
         ? GetSIMDRegisterWidth()
@@ -455,8 +548,11 @@
     return block_entry_label->GetLocation();
   }
 
-  HGraphVisitor* GetLocationBuilder() override { return &location_builder_; }
-  HGraphVisitor* GetInstructionVisitor() override { return &instruction_visitor_; }
+  HGraphVisitor* GetLocationBuilder() override { return location_builder_; }
+  InstructionCodeGeneratorARM64* GetInstructionCodeGeneratorArm64() {
+    return instruction_visitor_;
+  }
+  HGraphVisitor* GetInstructionVisitor() override { return GetInstructionCodeGeneratorArm64(); }
   Arm64Assembler* GetAssembler() override { return &assembler_; }
   const Arm64Assembler& GetAssembler() const override { return assembler_; }
   vixl::aarch64::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
@@ -899,14 +995,22 @@
   static void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos,
                                           ArenaVector<linker::LinkerPatch>* linker_patches);
 
+  // Returns whether SVE features are supported and should be used.
+  bool ShouldUseSVE() const;
+
   // Labels for each block that will be compiled.
   // We use a deque so that the `vixl::aarch64::Label` objects do not move in memory.
   ArenaDeque<vixl::aarch64::Label> block_labels_;  // Indexed by block id.
   vixl::aarch64::Label frame_entry_label_;
   ArenaVector<std::unique_ptr<JumpTableARM64>> jump_tables_;
 
-  LocationsBuilderARM64 location_builder_;
-  InstructionCodeGeneratorARM64 instruction_visitor_;
+  LocationsBuilderARM64Neon location_builder_neon_;
+  InstructionCodeGeneratorARM64Neon instruction_visitor_neon_;
+  LocationsBuilderARM64Sve location_builder_sve_;
+  InstructionCodeGeneratorARM64Sve instruction_visitor_sve_;
+
+  LocationsBuilderARM64* location_builder_;
+  InstructionCodeGeneratorARM64* instruction_visitor_;
   ParallelMoveResolverARM64 move_resolver_;
   Arm64Assembler assembler_;
 
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 3a2cf40..1d8fd6c 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -1931,7 +1931,7 @@
   FixJumpTables();
 
   // Emit JIT baker read barrier slow paths.
-  DCHECK(Runtime::Current()->UseJitCompilation() || jit_baker_read_barrier_slow_paths_.empty());
+  DCHECK(GetCompilerOptions().IsJitCompiler() || jit_baker_read_barrier_slow_paths_.empty());
   for (auto& entry : jit_baker_read_barrier_slow_paths_) {
     uint32_t encoded_data = entry.first;
     vixl::aarch32::Label* slow_path_entry = &entry.second.label;
@@ -2511,7 +2511,7 @@
   // Reduce code size for AOT by using shared trampolines for slow path runtime calls across the
   // entire oat file. This adds an extra branch and we do not want to slow down the main path.
   // For JIT, thunk sharing is per-method, so the gains would be smaller or even negative.
-  if (slow_path == nullptr || Runtime::Current()->UseJitCompilation()) {
+  if (slow_path == nullptr || GetCompilerOptions().IsJitCompiler()) {
     __ Ldr(lr, MemOperand(tr, entrypoint_offset.Int32Value()));
     // Ensure the pc position is recorded immediately after the `blx` instruction.
     // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
@@ -7123,11 +7123,11 @@
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
+      DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kJitBootImageAddress:
     case HLoadClass::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
+      DCHECK(GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kRuntimeCall:
       break;
@@ -7388,11 +7388,11 @@
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
+      DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadString::LoadKind::kJitBootImageAddress:
     case HLoadString::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
+      DCHECK(GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadString::LoadKind::kRuntimeCall:
       break;
@@ -9070,7 +9070,7 @@
 
 void CodeGeneratorARMVIXL::EmitEntrypointThunkCall(ThreadOffset32 entrypoint_offset) {
   DCHECK(!__ AllowMacroInstructions());  // In ExactAssemblyScope.
-  DCHECK(!Runtime::Current()->UseJitCompilation());
+  DCHECK(!GetCompilerOptions().IsJitCompiler());
   call_entrypoint_patches_.emplace_back(/*dex_file*/ nullptr, entrypoint_offset.Uint32Value());
   vixl::aarch32::Label* bl_label = &call_entrypoint_patches_.back().label;
   __ bind(bl_label);
@@ -9081,7 +9081,7 @@
 
 void CodeGeneratorARMVIXL::EmitBakerReadBarrierBne(uint32_t custom_data) {
   DCHECK(!__ AllowMacroInstructions());  // In ExactAssemblyScope.
-  if (Runtime::Current()->UseJitCompilation()) {
+  if (GetCompilerOptions().IsJitCompiler()) {
     auto it = jit_baker_read_barrier_slow_paths_.FindOrAdd(custom_data);
     vixl::aarch32::Label* slow_path_entry = &it->second.label;
     __ b(ne, EncodingSize(Wide), slow_path_entry);
@@ -9134,7 +9134,7 @@
     EmitMovwMovtPlaceholder(labels, reg);
     __ Ldr(reg, MemOperand(reg, /* offset= */ 0));
   } else {
-    DCHECK(Runtime::Current()->UseJitCompilation());
+    DCHECK(GetCompilerOptions().IsJitCompiler());
     gc::Heap* heap = Runtime::Current()->GetHeap();
     DCHECK(!heap->GetBootImageSpaces().empty());
     uintptr_t address =
@@ -9710,10 +9710,8 @@
   }
 
   // For JIT, the slow path is considered part of the compiled method,
-  // so JIT should pass null as `debug_name`. Tests may not have a runtime.
-  DCHECK(Runtime::Current() == nullptr ||
-         !Runtime::Current()->UseJitCompilation() ||
-         debug_name == nullptr);
+  // so JIT should pass null as `debug_name`.
+  DCHECK(!GetCompilerOptions().IsJitCompiler() || debug_name == nullptr);
   if (debug_name != nullptr && GetCompilerOptions().GenerateAnyDebugInfo()) {
     std::ostringstream oss;
     oss << "BakerReadBarrierThunk";
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64_neon.cc
similarity index 87%
rename from compiler/optimizing/code_generator_vector_arm64.cc
rename to compiler/optimizing/code_generator_vector_arm64_neon.cc
index df95c88..78720c3 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64_neon.cc
@@ -31,9 +31,11 @@
 using helpers::HeapOperand;
 using helpers::InputRegisterAt;
 using helpers::Int64FromLocation;
+using helpers::LocationFrom;
 using helpers::OutputRegister;
+using helpers::QRegisterFrom;
+using helpers::StackOperandFrom;
 using helpers::VRegisterFrom;
-using helpers::WRegisterFrom;
 using helpers::XRegisterFrom;
 
 #define __ GetVIXLAssembler()->
@@ -47,7 +49,7 @@
   return kArm64EmitDotProdInstructions && codegen_->GetInstructionSetFeatures().HasDotProd();
 }
 
-void LocationsBuilderARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+void LocationsBuilderARM64Neon::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
   HInstruction* input = instruction->InputAt(0);
   switch (instruction->GetPackedType()) {
@@ -78,7 +80,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Location src_loc = locations->InAt(0);
   VRegister dst = VRegisterFrom(locations->Out());
@@ -140,7 +142,7 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+void LocationsBuilderARM64Neon::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
   switch (instruction->GetPackedType()) {
     case DataType::Type::kBool:
@@ -164,7 +166,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   switch (instruction->GetPackedType()) {
@@ -215,11 +217,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecReduce(HVecReduce* instruction) {
+void LocationsBuilderARM64Neon::VisitVecReduce(HVecReduce* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecReduce(HVecReduce* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = DRegisterFrom(locations->Out());
@@ -255,11 +257,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecCnv(HVecCnv* instruction) {
+void LocationsBuilderARM64Neon::VisitVecCnv(HVecCnv* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecCnv(HVecCnv* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecCnv(HVecCnv* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -273,11 +275,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecNeg(HVecNeg* instruction) {
+void LocationsBuilderARM64Neon::VisitVecNeg(HVecNeg* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecNeg(HVecNeg* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecNeg(HVecNeg* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -314,11 +316,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecAbs(HVecAbs* instruction) {
+void LocationsBuilderARM64Neon::VisitVecAbs(HVecAbs* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecAbs(HVecAbs* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecAbs(HVecAbs* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -353,11 +355,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecNot(HVecNot* instruction) {
+void LocationsBuilderARM64Neon::VisitVecNot(HVecNot* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecNot(HVecNot* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecNot(HVecNot* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -404,11 +406,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecAdd(HVecAdd* instruction) {
+void LocationsBuilderARM64Neon::VisitVecAdd(HVecAdd* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecAdd(HVecAdd* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecAdd(HVecAdd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -446,11 +448,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
+void LocationsBuilderARM64Neon::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -478,11 +480,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+void LocationsBuilderARM64Neon::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -518,11 +520,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSub(HVecSub* instruction) {
+void LocationsBuilderARM64Neon::VisitVecSub(HVecSub* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSub(HVecSub* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecSub(HVecSub* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -560,11 +562,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSaturationSub(HVecSaturationSub* instruction) {
+void LocationsBuilderARM64Neon::VisitVecSaturationSub(HVecSaturationSub* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSaturationSub(HVecSaturationSub* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecSaturationSub(HVecSaturationSub* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -592,11 +594,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecMul(HVecMul* instruction) {
+void LocationsBuilderARM64Neon::VisitVecMul(HVecMul* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecMul(HVecMul* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecMul(HVecMul* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -630,11 +632,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecDiv(HVecDiv* instruction) {
+void LocationsBuilderARM64Neon::VisitVecDiv(HVecDiv* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecDiv(HVecDiv* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecDiv(HVecDiv* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -654,11 +656,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecMin(HVecMin* instruction) {
+void LocationsBuilderARM64Neon::VisitVecMin(HVecMin* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecMin(HVecMin* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecMin(HVecMin* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -702,11 +704,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecMax(HVecMax* instruction) {
+void LocationsBuilderARM64Neon::VisitVecMax(HVecMax* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecMax(HVecMax* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecMax(HVecMax* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -750,12 +752,12 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecAnd(HVecAnd* instruction) {
+void LocationsBuilderARM64Neon::VisitVecAnd(HVecAnd* instruction) {
   // TODO: Allow constants supported by BIC (vector, immediate).
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecAnd(HVecAnd* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecAnd(HVecAnd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -778,20 +780,20 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecAndNot(HVecAndNot* instruction) {
+void LocationsBuilderARM64Neon::VisitVecAndNot(HVecAndNot* instruction) {
   LOG(FATAL) << "Unsupported SIMD instruction " << instruction->GetId();
 }
 
-void InstructionCodeGeneratorARM64::VisitVecAndNot(HVecAndNot* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecAndNot(HVecAndNot* instruction) {
   // TODO: Use BIC (vector, register).
   LOG(FATAL) << "Unsupported SIMD instruction " << instruction->GetId();
 }
 
-void LocationsBuilderARM64::VisitVecOr(HVecOr* instruction) {
+void LocationsBuilderARM64Neon::VisitVecOr(HVecOr* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecOr(HVecOr* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecOr(HVecOr* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -814,11 +816,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecXor(HVecXor* instruction) {
+void LocationsBuilderARM64Neon::VisitVecXor(HVecXor* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecXor(HVecXor* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecXor(HVecXor* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -861,11 +863,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecShl(HVecShl* instruction) {
+void LocationsBuilderARM64Neon::VisitVecShl(HVecShl* instruction) {
   CreateVecShiftLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecShl(HVecShl* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecShl(HVecShl* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -895,11 +897,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecShr(HVecShr* instruction) {
+void LocationsBuilderARM64Neon::VisitVecShr(HVecShr* instruction) {
   CreateVecShiftLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecShr(HVecShr* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecShr(HVecShr* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -929,11 +931,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecUShr(HVecUShr* instruction) {
+void LocationsBuilderARM64Neon::VisitVecUShr(HVecUShr* instruction) {
   CreateVecShiftLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecUShr(HVecUShr* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecUShr(HVecUShr* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -963,7 +965,7 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+void LocationsBuilderARM64Neon::VisitVecSetScalars(HVecSetScalars* instruction) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
 
   DCHECK_EQ(1u, instruction->InputCount());  // only one input currently implemented
@@ -995,7 +997,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecSetScalars(HVecSetScalars* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister dst = VRegisterFrom(locations->Out());
 
@@ -1057,14 +1059,14 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
+void LocationsBuilderARM64Neon::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
   CreateVecAccumLocations(GetGraph()->GetAllocator(), instruction);
 }
 
 // Some early revisions of the Cortex-A53 have an erratum (835769) whereby it is possible for a
 // 64-bit scalar multiply-accumulate instruction in AArch64 state to generate an incorrect result.
 // However vector MultiplyAccumulate instruction is not affected.
-void InstructionCodeGeneratorARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister acc = VRegisterFrom(locations->InAt(0));
   VRegister left = VRegisterFrom(locations->InAt(1));
@@ -1105,7 +1107,7 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
+void LocationsBuilderARM64Neon::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
   CreateVecAccumLocations(GetGraph()->GetAllocator(), instruction);
   // Some conversions require temporary registers.
   LocationSummary* locations = instruction->GetLocations();
@@ -1147,7 +1149,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister acc = VRegisterFrom(locations->InAt(0));
   VRegister left = VRegisterFrom(locations->InAt(1));
@@ -1287,7 +1289,7 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecDotProd(HVecDotProd* instruction) {
+void LocationsBuilderARM64Neon::VisitVecDotProd(HVecDotProd* instruction) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
   DCHECK(instruction->GetPackedType() == DataType::Type::kInt32);
   locations->SetInAt(0, Location::RequiresFpuRegister());
@@ -1302,7 +1304,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecDotProd(HVecDotProd* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecDotProd(HVecDotProd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   DCHECK(locations->InAt(0).Equals(locations->Out()));
   VRegister acc = VRegisterFrom(locations->InAt(0));
@@ -1392,47 +1394,11 @@
   }
 }
 
-// Helper to set up locations for vector memory operations. Returns the memory operand and,
-// if used, sets the output parameter scratch to a temporary register used in this operand,
-// so that the client can release it right after the memory operand use.
-MemOperand InstructionCodeGeneratorARM64::VecAddress(
-    HVecMemoryOperation* instruction,
-    UseScratchRegisterScope* temps_scope,
-    size_t size,
-    bool is_string_char_at,
-    /*out*/ Register* scratch) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register base = InputRegisterAt(instruction, 0);
-
-  if (instruction->InputAt(1)->IsIntermediateAddressIndex()) {
-    DCHECK(!is_string_char_at);
-    return MemOperand(base.X(), InputRegisterAt(instruction, 1).X());
-  }
-
-  Location index = locations->InAt(1);
-  uint32_t offset = is_string_char_at
-      ? mirror::String::ValueOffset().Uint32Value()
-      : mirror::Array::DataOffset(size).Uint32Value();
-  size_t shift = ComponentSizeShiftWidth(size);
-
-  // HIntermediateAddress optimization is only applied for scalar ArrayGet and ArraySet.
-  DCHECK(!instruction->InputAt(0)->IsIntermediateAddress());
-
-  if (index.IsConstant()) {
-    offset += Int64FromLocation(index) << shift;
-    return HeapOperand(base, offset);
-  } else {
-    *scratch = temps_scope->AcquireSameSizeAs(base);
-    __ Add(*scratch, base, Operand(WRegisterFrom(index), LSL, shift));
-    return HeapOperand(*scratch, offset);
-  }
-}
-
-void LocationsBuilderARM64::VisitVecLoad(HVecLoad* instruction) {
+void LocationsBuilderARM64Neon::VisitVecLoad(HVecLoad* instruction) {
   CreateVecMemLocations(GetGraph()->GetAllocator(), instruction, /*is_load*/ true);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecLoad(HVecLoad* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecLoad(HVecLoad* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   size_t size = DataType::Size(instruction->GetPackedType());
   VRegister reg = VRegisterFrom(locations->Out());
@@ -1456,7 +1422,7 @@
         temps.Release(length);  // no longer needed
         // Zero extend 8 compressed bytes into 8 chars.
         __ Ldr(DRegisterFrom(locations->Out()).V8B(),
-               VecAddress(instruction, &temps, 1, /*is_string_char_at*/ true, &scratch));
+               VecNeonAddress(instruction, &temps, 1, /*is_string_char_at*/ true, &scratch));
         __ Uxtl(reg.V8H(), reg.V8B());
         __ B(&done);
         if (scratch.IsValid()) {
@@ -1464,7 +1430,8 @@
         }
         // Load 8 direct uncompressed chars.
         __ Bind(&uncompressed_load);
-        __ Ldr(reg, VecAddress(instruction, &temps, size, /*is_string_char_at*/ true, &scratch));
+        __ Ldr(reg,
+               VecNeonAddress(instruction, &temps, size, /*is_string_char_at*/ true, &scratch));
         __ Bind(&done);
         return;
       }
@@ -1478,7 +1445,8 @@
     case DataType::Type::kFloat64:
       DCHECK_LE(2u, instruction->GetVectorLength());
       DCHECK_LE(instruction->GetVectorLength(), 16u);
-      __ Ldr(reg, VecAddress(instruction, &temps, size, instruction->IsStringCharAt(), &scratch));
+      __ Ldr(reg,
+             VecNeonAddress(instruction, &temps, size, instruction->IsStringCharAt(), &scratch));
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType();
@@ -1486,11 +1454,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecStore(HVecStore* instruction) {
+void LocationsBuilderARM64Neon::VisitVecStore(HVecStore* instruction) {
   CreateVecMemLocations(GetGraph()->GetAllocator(), instruction, /*is_load*/ false);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecStore(HVecStore* instruction) {
+void InstructionCodeGeneratorARM64Neon::VisitVecStore(HVecStore* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   size_t size = DataType::Size(instruction->GetPackedType());
   VRegister reg = VRegisterFrom(locations->InAt(2));
@@ -1509,7 +1477,8 @@
     case DataType::Type::kFloat64:
       DCHECK_LE(2u, instruction->GetVectorLength());
       DCHECK_LE(instruction->GetVectorLength(), 16u);
-      __ Str(reg, VecAddress(instruction, &temps, size, /*is_string_char_at*/ false, &scratch));
+      __ Str(reg,
+             VecNeonAddress(instruction, &temps, size, /*is_string_char_at*/ false, &scratch));
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType();
@@ -1517,6 +1486,54 @@
   }
 }
 
+Location InstructionCodeGeneratorARM64Neon::AllocateSIMDScratchLocation(
+    vixl::aarch64::UseScratchRegisterScope* scope) {
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+  return LocationFrom(scope->AcquireVRegisterOfSize(kQRegSize));
+}
+
+void InstructionCodeGeneratorARM64Neon::FreeSIMDScratchLocation(Location loc,
+    vixl::aarch64::UseScratchRegisterScope* scope) {
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+  scope->Release(QRegisterFrom(loc));
+}
+
+void InstructionCodeGeneratorARM64Neon::LoadSIMDRegFromStack(Location destination,
+                                                             Location source) {
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+  __ Ldr(QRegisterFrom(destination), StackOperandFrom(source));
+}
+
+void InstructionCodeGeneratorARM64Neon::MoveSIMDRegToSIMDReg(Location destination,
+                                                             Location source) {
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+  __ Mov(QRegisterFrom(destination), QRegisterFrom(source));
+}
+
+void InstructionCodeGeneratorARM64Neon::MoveToSIMDStackSlot(Location destination,
+                                                            Location source) {
+  DCHECK(destination.IsSIMDStackSlot());
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+
+  if (source.IsFpuRegister()) {
+    __ Str(QRegisterFrom(source), StackOperandFrom(destination));
+  } else {
+    DCHECK(source.IsSIMDStackSlot());
+    UseScratchRegisterScope temps(GetVIXLAssembler());
+    if (GetVIXLAssembler()->GetScratchVRegisterList()->IsEmpty()) {
+      Register temp = temps.AcquireX();
+      __ Ldr(temp, MemOperand(sp, source.GetStackIndex()));
+      __ Str(temp, MemOperand(sp, destination.GetStackIndex()));
+      __ Ldr(temp, MemOperand(sp, source.GetStackIndex() + kArm64WordSize));
+      __ Str(temp, MemOperand(sp, destination.GetStackIndex() + kArm64WordSize));
+    } else {
+      VRegister temp = temps.AcquireVRegisterOfSize(kQRegSize);
+      __ Ldr(temp, StackOperandFrom(source));
+      __ Str(temp, StackOperandFrom(destination));
+    }
+  }
+}
+
 #undef __
 
 }  // namespace arm64
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64_sve.cc
similarity index 87%
copy from compiler/optimizing/code_generator_vector_arm64.cc
copy to compiler/optimizing/code_generator_vector_arm64_sve.cc
index df95c88..5460ff28 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64_sve.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,9 +31,11 @@
 using helpers::HeapOperand;
 using helpers::InputRegisterAt;
 using helpers::Int64FromLocation;
+using helpers::LocationFrom;
 using helpers::OutputRegister;
+using helpers::QRegisterFrom;
+using helpers::StackOperandFrom;
 using helpers::VRegisterFrom;
-using helpers::WRegisterFrom;
 using helpers::XRegisterFrom;
 
 #define __ GetVIXLAssembler()->
@@ -47,7 +49,7 @@
   return kArm64EmitDotProdInstructions && codegen_->GetInstructionSetFeatures().HasDotProd();
 }
 
-void LocationsBuilderARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+void LocationsBuilderARM64Sve::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
   HInstruction* input = instruction->InputAt(0);
   switch (instruction->GetPackedType()) {
@@ -78,7 +80,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Location src_loc = locations->InAt(0);
   VRegister dst = VRegisterFrom(locations->Out());
@@ -140,7 +142,7 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+void LocationsBuilderARM64Sve::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
   switch (instruction->GetPackedType()) {
     case DataType::Type::kBool:
@@ -164,7 +166,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   switch (instruction->GetPackedType()) {
@@ -215,11 +217,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecReduce(HVecReduce* instruction) {
+void LocationsBuilderARM64Sve::VisitVecReduce(HVecReduce* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecReduce(HVecReduce* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = DRegisterFrom(locations->Out());
@@ -255,11 +257,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecCnv(HVecCnv* instruction) {
+void LocationsBuilderARM64Sve::VisitVecCnv(HVecCnv* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecCnv(HVecCnv* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecCnv(HVecCnv* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -273,11 +275,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecNeg(HVecNeg* instruction) {
+void LocationsBuilderARM64Sve::VisitVecNeg(HVecNeg* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecNeg(HVecNeg* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecNeg(HVecNeg* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -314,11 +316,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecAbs(HVecAbs* instruction) {
+void LocationsBuilderARM64Sve::VisitVecAbs(HVecAbs* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecAbs(HVecAbs* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecAbs(HVecAbs* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -353,11 +355,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecNot(HVecNot* instruction) {
+void LocationsBuilderARM64Sve::VisitVecNot(HVecNot* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecNot(HVecNot* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecNot(HVecNot* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister src = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -404,11 +406,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecAdd(HVecAdd* instruction) {
+void LocationsBuilderARM64Sve::VisitVecAdd(HVecAdd* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecAdd(HVecAdd* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecAdd(HVecAdd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -446,11 +448,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
+void LocationsBuilderARM64Sve::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -478,11 +480,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+void LocationsBuilderARM64Sve::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -518,11 +520,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSub(HVecSub* instruction) {
+void LocationsBuilderARM64Sve::VisitVecSub(HVecSub* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSub(HVecSub* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecSub(HVecSub* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -560,11 +562,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSaturationSub(HVecSaturationSub* instruction) {
+void LocationsBuilderARM64Sve::VisitVecSaturationSub(HVecSaturationSub* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSaturationSub(HVecSaturationSub* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecSaturationSub(HVecSaturationSub* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -592,11 +594,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecMul(HVecMul* instruction) {
+void LocationsBuilderARM64Sve::VisitVecMul(HVecMul* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecMul(HVecMul* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecMul(HVecMul* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -630,11 +632,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecDiv(HVecDiv* instruction) {
+void LocationsBuilderARM64Sve::VisitVecDiv(HVecDiv* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecDiv(HVecDiv* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecDiv(HVecDiv* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -654,11 +656,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecMin(HVecMin* instruction) {
+void LocationsBuilderARM64Sve::VisitVecMin(HVecMin* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecMin(HVecMin* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecMin(HVecMin* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -702,11 +704,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecMax(HVecMax* instruction) {
+void LocationsBuilderARM64Sve::VisitVecMax(HVecMax* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecMax(HVecMax* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecMax(HVecMax* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -750,12 +752,12 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecAnd(HVecAnd* instruction) {
+void LocationsBuilderARM64Sve::VisitVecAnd(HVecAnd* instruction) {
   // TODO: Allow constants supported by BIC (vector, immediate).
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecAnd(HVecAnd* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecAnd(HVecAnd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -778,20 +780,20 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecAndNot(HVecAndNot* instruction) {
+void LocationsBuilderARM64Sve::VisitVecAndNot(HVecAndNot* instruction) {
   LOG(FATAL) << "Unsupported SIMD instruction " << instruction->GetId();
 }
 
-void InstructionCodeGeneratorARM64::VisitVecAndNot(HVecAndNot* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecAndNot(HVecAndNot* instruction) {
   // TODO: Use BIC (vector, register).
   LOG(FATAL) << "Unsupported SIMD instruction " << instruction->GetId();
 }
 
-void LocationsBuilderARM64::VisitVecOr(HVecOr* instruction) {
+void LocationsBuilderARM64Sve::VisitVecOr(HVecOr* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecOr(HVecOr* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecOr(HVecOr* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -814,11 +816,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecXor(HVecXor* instruction) {
+void LocationsBuilderARM64Sve::VisitVecXor(HVecXor* instruction) {
   CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecXor(HVecXor* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecXor(HVecXor* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister rhs = VRegisterFrom(locations->InAt(1));
@@ -861,11 +863,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecShl(HVecShl* instruction) {
+void LocationsBuilderARM64Sve::VisitVecShl(HVecShl* instruction) {
   CreateVecShiftLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecShl(HVecShl* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecShl(HVecShl* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -895,11 +897,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecShr(HVecShr* instruction) {
+void LocationsBuilderARM64Sve::VisitVecShr(HVecShr* instruction) {
   CreateVecShiftLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecShr(HVecShr* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecShr(HVecShr* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -929,11 +931,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecUShr(HVecUShr* instruction) {
+void LocationsBuilderARM64Sve::VisitVecUShr(HVecUShr* instruction) {
   CreateVecShiftLocations(GetGraph()->GetAllocator(), instruction);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecUShr(HVecUShr* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecUShr(HVecUShr* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister lhs = VRegisterFrom(locations->InAt(0));
   VRegister dst = VRegisterFrom(locations->Out());
@@ -963,7 +965,7 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+void LocationsBuilderARM64Sve::VisitVecSetScalars(HVecSetScalars* instruction) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
 
   DCHECK_EQ(1u, instruction->InputCount());  // only one input currently implemented
@@ -995,7 +997,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecSetScalars(HVecSetScalars* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister dst = VRegisterFrom(locations->Out());
 
@@ -1057,14 +1059,14 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
+void LocationsBuilderARM64Sve::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
   CreateVecAccumLocations(GetGraph()->GetAllocator(), instruction);
 }
 
 // Some early revisions of the Cortex-A53 have an erratum (835769) whereby it is possible for a
 // 64-bit scalar multiply-accumulate instruction in AArch64 state to generate an incorrect result.
 // However vector MultiplyAccumulate instruction is not affected.
-void InstructionCodeGeneratorARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister acc = VRegisterFrom(locations->InAt(0));
   VRegister left = VRegisterFrom(locations->InAt(1));
@@ -1105,7 +1107,7 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
+void LocationsBuilderARM64Sve::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
   CreateVecAccumLocations(GetGraph()->GetAllocator(), instruction);
   // Some conversions require temporary registers.
   LocationSummary* locations = instruction->GetLocations();
@@ -1147,7 +1149,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   VRegister acc = VRegisterFrom(locations->InAt(0));
   VRegister left = VRegisterFrom(locations->InAt(1));
@@ -1287,7 +1289,7 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecDotProd(HVecDotProd* instruction) {
+void LocationsBuilderARM64Sve::VisitVecDotProd(HVecDotProd* instruction) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
   DCHECK(instruction->GetPackedType() == DataType::Type::kInt32);
   locations->SetInAt(0, Location::RequiresFpuRegister());
@@ -1302,7 +1304,7 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecDotProd(HVecDotProd* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecDotProd(HVecDotProd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   DCHECK(locations->InAt(0).Equals(locations->Out()));
   VRegister acc = VRegisterFrom(locations->InAt(0));
@@ -1392,47 +1394,11 @@
   }
 }
 
-// Helper to set up locations for vector memory operations. Returns the memory operand and,
-// if used, sets the output parameter scratch to a temporary register used in this operand,
-// so that the client can release it right after the memory operand use.
-MemOperand InstructionCodeGeneratorARM64::VecAddress(
-    HVecMemoryOperation* instruction,
-    UseScratchRegisterScope* temps_scope,
-    size_t size,
-    bool is_string_char_at,
-    /*out*/ Register* scratch) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register base = InputRegisterAt(instruction, 0);
-
-  if (instruction->InputAt(1)->IsIntermediateAddressIndex()) {
-    DCHECK(!is_string_char_at);
-    return MemOperand(base.X(), InputRegisterAt(instruction, 1).X());
-  }
-
-  Location index = locations->InAt(1);
-  uint32_t offset = is_string_char_at
-      ? mirror::String::ValueOffset().Uint32Value()
-      : mirror::Array::DataOffset(size).Uint32Value();
-  size_t shift = ComponentSizeShiftWidth(size);
-
-  // HIntermediateAddress optimization is only applied for scalar ArrayGet and ArraySet.
-  DCHECK(!instruction->InputAt(0)->IsIntermediateAddress());
-
-  if (index.IsConstant()) {
-    offset += Int64FromLocation(index) << shift;
-    return HeapOperand(base, offset);
-  } else {
-    *scratch = temps_scope->AcquireSameSizeAs(base);
-    __ Add(*scratch, base, Operand(WRegisterFrom(index), LSL, shift));
-    return HeapOperand(*scratch, offset);
-  }
-}
-
-void LocationsBuilderARM64::VisitVecLoad(HVecLoad* instruction) {
+void LocationsBuilderARM64Sve::VisitVecLoad(HVecLoad* instruction) {
   CreateVecMemLocations(GetGraph()->GetAllocator(), instruction, /*is_load*/ true);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecLoad(HVecLoad* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecLoad(HVecLoad* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   size_t size = DataType::Size(instruction->GetPackedType());
   VRegister reg = VRegisterFrom(locations->Out());
@@ -1456,7 +1422,7 @@
         temps.Release(length);  // no longer needed
         // Zero extend 8 compressed bytes into 8 chars.
         __ Ldr(DRegisterFrom(locations->Out()).V8B(),
-               VecAddress(instruction, &temps, 1, /*is_string_char_at*/ true, &scratch));
+               VecNeonAddress(instruction, &temps, 1, /*is_string_char_at*/ true, &scratch));
         __ Uxtl(reg.V8H(), reg.V8B());
         __ B(&done);
         if (scratch.IsValid()) {
@@ -1464,7 +1430,8 @@
         }
         // Load 8 direct uncompressed chars.
         __ Bind(&uncompressed_load);
-        __ Ldr(reg, VecAddress(instruction, &temps, size, /*is_string_char_at*/ true, &scratch));
+        __ Ldr(reg,
+               VecNeonAddress(instruction, &temps, size, /*is_string_char_at*/ true, &scratch));
         __ Bind(&done);
         return;
       }
@@ -1478,7 +1445,8 @@
     case DataType::Type::kFloat64:
       DCHECK_LE(2u, instruction->GetVectorLength());
       DCHECK_LE(instruction->GetVectorLength(), 16u);
-      __ Ldr(reg, VecAddress(instruction, &temps, size, instruction->IsStringCharAt(), &scratch));
+      __ Ldr(reg,
+             VecNeonAddress(instruction, &temps, size, instruction->IsStringCharAt(), &scratch));
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType();
@@ -1486,11 +1454,11 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecStore(HVecStore* instruction) {
+void LocationsBuilderARM64Sve::VisitVecStore(HVecStore* instruction) {
   CreateVecMemLocations(GetGraph()->GetAllocator(), instruction, /*is_load*/ false);
 }
 
-void InstructionCodeGeneratorARM64::VisitVecStore(HVecStore* instruction) {
+void InstructionCodeGeneratorARM64Sve::VisitVecStore(HVecStore* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   size_t size = DataType::Size(instruction->GetPackedType());
   VRegister reg = VRegisterFrom(locations->InAt(2));
@@ -1509,7 +1477,8 @@
     case DataType::Type::kFloat64:
       DCHECK_LE(2u, instruction->GetVectorLength());
       DCHECK_LE(instruction->GetVectorLength(), 16u);
-      __ Str(reg, VecAddress(instruction, &temps, size, /*is_string_char_at*/ false, &scratch));
+      __ Str(reg,
+             VecNeonAddress(instruction, &temps, size, /*is_string_char_at*/ false, &scratch));
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType();
@@ -1517,6 +1486,54 @@
   }
 }
 
+Location InstructionCodeGeneratorARM64Sve::AllocateSIMDScratchLocation(
+    vixl::aarch64::UseScratchRegisterScope* scope) {
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+  return LocationFrom(scope->AcquireVRegisterOfSize(kQRegSize));
+}
+
+void InstructionCodeGeneratorARM64Sve::FreeSIMDScratchLocation(Location loc,
+    vixl::aarch64::UseScratchRegisterScope* scope) {
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+  scope->Release(QRegisterFrom(loc));
+}
+
+void InstructionCodeGeneratorARM64Sve::LoadSIMDRegFromStack(Location destination,
+                                                            Location source) {
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+  __ Ldr(QRegisterFrom(destination), StackOperandFrom(source));
+}
+
+void InstructionCodeGeneratorARM64Sve::MoveSIMDRegToSIMDReg(Location destination,
+                                                            Location source) {
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+  __ Mov(QRegisterFrom(destination), QRegisterFrom(source));
+}
+
+void InstructionCodeGeneratorARM64Sve::MoveToSIMDStackSlot(Location destination,
+                                                           Location source) {
+  DCHECK(destination.IsSIMDStackSlot());
+  DCHECK_EQ(codegen_->GetSIMDRegisterWidth(), kQRegSizeInBytes);
+
+  if (source.IsFpuRegister()) {
+    __ Str(QRegisterFrom(source), StackOperandFrom(destination));
+  } else {
+    DCHECK(source.IsSIMDStackSlot());
+    UseScratchRegisterScope temps(GetVIXLAssembler());
+    if (GetVIXLAssembler()->GetScratchVRegisterList()->IsEmpty()) {
+      Register temp = temps.AcquireX();
+      __ Ldr(temp, MemOperand(sp, source.GetStackIndex()));
+      __ Str(temp, MemOperand(sp, destination.GetStackIndex()));
+      __ Ldr(temp, MemOperand(sp, source.GetStackIndex() + kArm64WordSize));
+      __ Str(temp, MemOperand(sp, destination.GetStackIndex() + kArm64WordSize));
+    } else {
+      VRegister temp = temps.AcquireVRegisterOfSize(kQRegSize);
+      __ Ldr(temp, StackOperandFrom(source));
+      __ Str(temp, StackOperandFrom(destination));
+    }
+  }
+}
+
 #undef __
 
 }  // namespace arm64
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index ed1a536..e9ef21a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5144,7 +5144,7 @@
     __ movl(reg, Address(method_address_reg, CodeGeneratorX86::kDummy32BitOffset));
     RecordBootImageRelRoPatch(method_address, boot_image_reference);
   } else {
-    DCHECK(Runtime::Current()->UseJitCompilation());
+    DCHECK(GetCompilerOptions().IsJitCompiler());
     gc::Heap* heap = Runtime::Current()->GetHeap();
     DCHECK(!heap->GetBootImageSpaces().empty());
     const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference;
@@ -6630,11 +6630,11 @@
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
+      DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kJitBootImageAddress:
     case HLoadClass::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
+      DCHECK(GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kRuntimeCall:
       break;
@@ -6867,11 +6867,11 @@
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
+      DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadString::LoadKind::kJitBootImageAddress:
     case HLoadString::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
+      DCHECK(GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadString::LoadKind::kRuntimeCall:
       break;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 8518b6d..ec54376 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1134,7 +1134,7 @@
     __ movl(reg, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip= */ false));
     RecordBootImageRelRoPatch(boot_image_reference);
   } else {
-    DCHECK(Runtime::Current()->UseJitCompilation());
+    DCHECK(GetCompilerOptions().IsJitCompiler());
     gc::Heap* heap = Runtime::Current()->GetHeap();
     DCHECK(!heap->GetBootImageSpaces().empty());
     const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference;
@@ -5966,11 +5966,11 @@
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
+      DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kJitBootImageAddress:
     case HLoadClass::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
+      DCHECK(GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kRuntimeCall:
       break;
@@ -6162,11 +6162,11 @@
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
+      DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadString::LoadKind::kJitBootImageAddress:
     case HLoadString::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
+      DCHECK(GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadString::LoadKind::kRuntimeCall:
       break;
diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc
index f406983..f946e50 100644
--- a/compiler/optimizing/code_sinking.cc
+++ b/compiler/optimizing/code_sinking.cc
@@ -58,8 +58,8 @@
     }
   }
 
-  // Check allocations first, as they can throw, but it is safe to move them.
-  if (instruction->IsNewInstance() || instruction->IsNewArray()) {
+  // Check allocations and strings first, as they can throw, but it is safe to move them.
+  if (instruction->IsNewInstance() || instruction->IsNewArray() || instruction->IsLoadString()) {
     return true;
   }
 
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index d9b4f79..f4f44a0 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -81,8 +81,9 @@
     HGraph* graph = CreateCFG(data);
     // Remove suspend checks, they cannot be executed in this context.
     RemoveSuspendChecks(graph);
-    OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
-    RunCode(target_config, *compiler_options_, graph, [](HGraph*) {}, has_result, expected);
+    std::unique_ptr<CompilerOptions> compiler_options =
+        CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
+    RunCode(target_config, *compiler_options, graph, [](HGraph*) {}, has_result, expected);
   }
 }
 
@@ -93,8 +94,9 @@
     HGraph* graph = CreateCFG(data, DataType::Type::kInt64);
     // Remove suspend checks, they cannot be executed in this context.
     RemoveSuspendChecks(graph);
-    OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
-    RunCode(target_config, *compiler_options_, graph, [](HGraph*) {}, has_result, expected);
+    std::unique_ptr<CompilerOptions> compiler_options =
+        CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
+    RunCode(target_config, *compiler_options, graph, [](HGraph*) {}, has_result, expected);
   }
 }
 
@@ -445,7 +447,9 @@
 
     ASSERT_FALSE(equal->IsEmittedAtUseSite());
     graph->BuildDominatorTree();
-    PrepareForRegisterAllocation(graph, *compiler_options_).Run();
+    std::unique_ptr<CompilerOptions> compiler_options =
+        CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
+    PrepareForRegisterAllocation(graph, *compiler_options).Run();
     ASSERT_TRUE(equal->IsEmittedAtUseSite());
 
     auto hook_before_codegen = [](HGraph* graph_in) {
@@ -454,8 +458,7 @@
       block->InsertInstructionBefore(move, block->GetLastInstruction());
     };
 
-    OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
-    RunCode(target_config, *compiler_options_, graph, hook_before_codegen, true, 0);
+    RunCode(target_config, *compiler_options, graph, hook_before_codegen, true, 0);
   }
 }
 
@@ -501,8 +504,9 @@
             new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator());
         block->InsertInstructionBefore(move, block->GetLastInstruction());
       };
-      OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
-      RunCode(target_config, *compiler_options_, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
+      std::unique_ptr<CompilerOptions> compiler_options =
+          CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
+      RunCode(target_config, *compiler_options, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
     }
   }
 }
@@ -569,8 +573,9 @@
             new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator());
         block->InsertInstructionBefore(move, block->GetLastInstruction());
       };
-      OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
-      RunCode(target_config, *compiler_options_, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
+      std::unique_ptr<CompilerOptions> compiler_options =
+          CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
+      RunCode(target_config, *compiler_options, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
     }
   }
 }
@@ -679,8 +684,9 @@
   block->AddInstruction(new (GetAllocator()) HReturn(comparison));
 
   graph->BuildDominatorTree();
-  OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
-  RunCode(target_config, *compiler_options_, graph, [](HGraph*) {}, true, expected_result);
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
+  RunCode(target_config, *compiler_options, graph, [](HGraph*) {}, true, expected_result);
 }
 
 TEST_F(CodegenTest, ComparisonsInt) {
@@ -711,9 +717,10 @@
 
 #ifdef ART_ENABLE_CODEGEN_arm
 TEST_F(CodegenTest, ARMVIXLParallelMoveResolver) {
-  OverrideInstructionSetFeatures(InstructionSet::kThumb2, "default");
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(InstructionSet::kThumb2, "default");
   HGraph* graph = CreateGraph();
-  arm::CodeGeneratorARMVIXL codegen(graph, *compiler_options_);
+  arm::CodeGeneratorARMVIXL codegen(graph, *compiler_options);
 
   codegen.Initialize();
 
@@ -734,9 +741,10 @@
 #ifdef ART_ENABLE_CODEGEN_arm64
 // Regression test for b/34760542.
 TEST_F(CodegenTest, ARM64ParallelMoveResolverB34760542) {
-  OverrideInstructionSetFeatures(InstructionSet::kArm64, "default");
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
   HGraph* graph = CreateGraph();
-  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
+  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
 
   codegen.Initialize();
 
@@ -783,9 +791,10 @@
 
 // Check that ParallelMoveResolver works fine for ARM64 for both cases when SIMD is on and off.
 TEST_F(CodegenTest, ARM64ParallelMoveResolverSIMD) {
-  OverrideInstructionSetFeatures(InstructionSet::kArm64, "default");
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
   HGraph* graph = CreateGraph();
-  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
+  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
 
   codegen.Initialize();
 
@@ -818,9 +827,10 @@
 
 // Check that ART ISA Features are propagated to VIXL for arm64 (using cortex-a75 as example).
 TEST_F(CodegenTest, ARM64IsaVIXLFeaturesA75) {
-  OverrideInstructionSetFeatures(InstructionSet::kArm64, "cortex-a75");
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "cortex-a75");
   HGraph* graph = CreateGraph();
-  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
+  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
   vixl::CPUFeatures* features = codegen.GetVIXLAssembler()->GetCPUFeatures();
 
   EXPECT_TRUE(features->Has(vixl::CPUFeatures::kCRC32));
@@ -832,9 +842,10 @@
 
 // Check that ART ISA Features are propagated to VIXL for arm64 (using cortex-a53 as example).
 TEST_F(CodegenTest, ARM64IsaVIXLFeaturesA53) {
-  OverrideInstructionSetFeatures(InstructionSet::kArm64, "cortex-a53");
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "cortex-a53");
   HGraph* graph = CreateGraph();
-  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
+  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
   vixl::CPUFeatures* features = codegen.GetVIXLAssembler()->GetCPUFeatures();
 
   EXPECT_TRUE(features->Has(vixl::CPUFeatures::kCRC32));
@@ -850,9 +861,10 @@
 // allocated on stack per callee-saved FP register to be preserved in the frame entry as
 // ABI states.
 TEST_F(CodegenTest, ARM64FrameSizeSIMD) {
-  OverrideInstructionSetFeatures(InstructionSet::kArm64, "default");
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
   HGraph* graph = CreateGraph();
-  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
+  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
 
   codegen.Initialize();
   graph->SetHasSIMD(true);
@@ -869,9 +881,10 @@
 }
 
 TEST_F(CodegenTest, ARM64FrameSizeNoSIMD) {
-  OverrideInstructionSetFeatures(InstructionSet::kArm64, "default");
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
   HGraph* graph = CreateGraph();
-  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
+  arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
 
   codegen.Initialize();
   graph->SetHasSIMD(false);
diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h
index 9fbd7d6..9d15f1f 100644
--- a/compiler/optimizing/codegen_test_utils.h
+++ b/compiler/optimizing/codegen_test_utils.h
@@ -313,25 +313,25 @@
 }
 
 #ifdef ART_ENABLE_CODEGEN_arm
-CodeGenerator* create_codegen_arm_vixl32(HGraph* graph, const CompilerOptions& compiler_options) {
+inline CodeGenerator* create_codegen_arm_vixl32(HGraph* graph, const CompilerOptions& compiler_options) {
   return new (graph->GetAllocator()) TestCodeGeneratorARMVIXL(graph, compiler_options);
 }
 #endif
 
 #ifdef ART_ENABLE_CODEGEN_arm64
-CodeGenerator* create_codegen_arm64(HGraph* graph, const CompilerOptions& compiler_options) {
+inline CodeGenerator* create_codegen_arm64(HGraph* graph, const CompilerOptions& compiler_options) {
   return new (graph->GetAllocator()) TestCodeGeneratorARM64(graph, compiler_options);
 }
 #endif
 
 #ifdef ART_ENABLE_CODEGEN_x86
-CodeGenerator* create_codegen_x86(HGraph* graph, const CompilerOptions& compiler_options) {
+inline CodeGenerator* create_codegen_x86(HGraph* graph, const CompilerOptions& compiler_options) {
   return new (graph->GetAllocator()) TestCodeGeneratorX86(graph, compiler_options);
 }
 #endif
 
 #ifdef ART_ENABLE_CODEGEN_x86_64
-CodeGenerator* create_codegen_x86_64(HGraph* graph, const CompilerOptions& compiler_options) {
+inline CodeGenerator* create_codegen_x86_64(HGraph* graph, const CompilerOptions& compiler_options) {
   return new (graph->GetAllocator()) x86_64::CodeGeneratorX86_64(graph, compiler_options);
 }
 #endif
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 05a0afa..ef5669a 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -21,7 +21,7 @@
 #include "base/logging.h"
 #include "builder.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "constant_folding.h"
 #include "data_type-inl.h"
 #include "dead_code_elimination.h"
@@ -414,7 +414,7 @@
 static bool IsMethodUnverified(const CompilerOptions& compiler_options, ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (!method->GetDeclaringClass()->IsVerified()) {
-    if (Runtime::Current()->UseJitCompilation()) {
+    if (compiler_options.IsJitCompiler()) {
       // We're at runtime, we know this is cold code if the class
       // is not verified, so don't bother analyzing.
       return true;
@@ -489,11 +489,19 @@
 }
 
 bool HInliner::TryInline(HInvoke* invoke_instruction) {
-  if (invoke_instruction->IsInvokeUnresolved() ||
-      invoke_instruction->IsInvokePolymorphic() ||
-      invoke_instruction->IsInvokeCustom()) {
-    return false;  // Don't bother to move further if we know the method is unresolved or the
-                   // invocation is polymorphic (invoke-{polymorphic,custom}).
+  MaybeRecordStat(stats_, MethodCompilationStat::kTryInline);
+
+  // Don't bother to move further if we know the method is unresolved or the invocation is
+  // polymorphic (invoke-{polymorphic,custom}).
+  if (invoke_instruction->IsInvokeUnresolved()) {
+    MaybeRecordStat(stats_, MethodCompilationStat::kNotInlinedUnresolved);
+    return false;
+  } else if (invoke_instruction->IsInvokePolymorphic()) {
+    MaybeRecordStat(stats_, MethodCompilationStat::kNotInlinedPolymorphic);
+    return false;
+  } else if (invoke_instruction->IsInvokeCustom()) {
+    MaybeRecordStat(stats_, MethodCompilationStat::kNotInlinedCustom);
+    return false;
   }
 
   ScopedObjectAccess soa(Thread::Current());
@@ -665,7 +673,7 @@
     StackHandleScope<1>* hs,
     /*out*/Handle<mirror::ObjectArray<mirror::Class>>* inline_cache)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  DCHECK(Runtime::Current()->UseJitCompilation());
+  DCHECK(codegen_->GetCompilerOptions().IsJitCompiler());
 
   ArtMethod* caller = graph_->GetArtMethod();
   // Under JIT, we should always know the caller.
@@ -855,7 +863,8 @@
 
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   PointerSize pointer_size = class_linker->GetImagePointerSize();
-  Handle<mirror::Class> monomorphic_type = handles_->NewHandle(GetMonomorphicType(classes));
+  Handle<mirror::Class> monomorphic_type =
+      graph_->GetHandleCache()->NewHandle(GetMonomorphicType(classes));
   resolved_method = ResolveMethodFromInlineCache(
       monomorphic_type, resolved_method, invoke_instruction, pointer_size);
 
@@ -891,7 +900,6 @@
   ReferenceTypePropagation rtp_fixup(graph_,
                                      outer_compilation_unit_.GetClassLoader(),
                                      outer_compilation_unit_.GetDexCache(),
-                                     handles_,
                                      /* is_first_run= */ false);
   rtp_fixup.Run();
 
@@ -1019,7 +1027,7 @@
     }
     ArtMethod* method = nullptr;
 
-    Handle<mirror::Class> handle = handles_->NewHandle(classes->Get(i));
+    Handle<mirror::Class> handle = graph_->GetHandleCache()->NewHandle(classes->Get(i));
     method = ResolveMethodFromInlineCache(
         handle, resolved_method, invoke_instruction, pointer_size);
     if (method == nullptr) {
@@ -1091,7 +1099,6 @@
   ReferenceTypePropagation rtp_fixup(graph_,
                                      outer_compilation_unit_.GetClassLoader(),
                                      outer_compilation_unit_.GetDexCache(),
-                                     handles_,
                                      /* is_first_run= */ false);
   rtp_fixup.Run();
   return true;
@@ -1178,7 +1185,7 @@
     ArtMethod* resolved_method,
     Handle<mirror::ObjectArray<mirror::Class>> classes) {
   // This optimization only works under JIT for now.
-  if (!Runtime::Current()->UseJitCompilation()) {
+  if (!codegen_->GetCompilerOptions().IsJitCompiler()) {
     return false;
   }
 
@@ -1287,7 +1294,6 @@
   ReferenceTypePropagation rtp_fixup(graph_,
                                      outer_compilation_unit_.GetClassLoader(),
                                      outer_compilation_unit_.GetDexCache(),
-                                     handles_,
                                      /* is_first_run= */ false);
   rtp_fixup.Run();
 
@@ -1411,7 +1417,6 @@
     ReferenceTypePropagation(graph_,
                              outer_compilation_unit_.GetClassLoader(),
                              outer_compilation_unit_.GetDexCache(),
-                             handles_,
                              /* is_first_run= */ false).Run();
   }
   return true;
@@ -1729,11 +1734,11 @@
       /* dex_pc= */ 0);
   if (iget->GetType() == DataType::Type::kReference) {
     // Use the same dex_cache that we used for field lookup as the hint_dex_cache.
-    Handle<mirror::DexCache> dex_cache = handles_->NewHandle(referrer->GetDexCache());
+    Handle<mirror::DexCache> dex_cache =
+        graph_->GetHandleCache()->NewHandle(referrer->GetDexCache());
     ReferenceTypePropagation rtp(graph_,
                                  outer_compilation_unit_.GetClassLoader(),
                                  dex_cache,
-                                 handles_,
                                  /* is_first_run= */ false);
     rtp.Visit(iget);
   }
@@ -1772,11 +1777,9 @@
 }
 
 template <typename T>
-static inline Handle<T> NewHandleIfDifferent(ObjPtr<T> object,
-                                             Handle<T> hint,
-                                             VariableSizedHandleScope* handles)
+static inline Handle<T> NewHandleIfDifferent(ObjPtr<T> object, Handle<T> hint, HGraph* graph)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  return (object != hint.Get()) ? handles->NewHandle(object) : hint;
+  return (object != hint.Get()) ? graph->GetHandleCache()->NewHandle(object) : hint;
 }
 
 static bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee)
@@ -1839,7 +1842,6 @@
     ReferenceTypePropagation(callee_graph,
                              outer_compilation_unit_.GetClassLoader(),
                              dex_compilation_unit.GetDexCache(),
-                             handles_,
                              /* is_first_run= */ false).Run();
   }
 }
@@ -1997,13 +1999,14 @@
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   Handle<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(),
                                                             caller_compilation_unit_.GetDexCache(),
-                                                            handles_);
+                                                            graph_);
   Handle<mirror::ClassLoader> class_loader =
       NewHandleIfDifferent(resolved_method->GetDeclaringClass()->GetClassLoader(),
                            caller_compilation_unit_.GetClassLoader(),
-                           handles_);
+                           graph_);
 
-  Handle<mirror::Class> compiling_class = handles_->NewHandle(resolved_method->GetDeclaringClass());
+  Handle<mirror::Class> compiling_class =
+      graph_->GetHandleCache()->NewHandle(resolved_method->GetDeclaringClass());
   DexCompilationUnit dex_compilation_unit(
       class_loader,
       class_linker,
@@ -2035,6 +2038,7 @@
   HGraph* callee_graph = new (graph_->GetAllocator()) HGraph(
       graph_->GetAllocator(),
       graph_->GetArenaStack(),
+      graph_->GetHandleCache()->GetHandles(),
       callee_dex_file,
       method_index,
       codegen_->GetCompilerOptions().GetInstructionSet(),
@@ -2042,7 +2046,6 @@
       callee_dead_reference_safe,
       graph_->IsDebuggable(),
       /* osr= */ false,
-      /* is_shared_jit_code= */ graph_->IsCompilingForSharedJitCode(),
       /* baseline= */ graph_->IsCompilingBaseline(),
       /* start_instruction_id= */ caller_instruction_counter);
   callee_graph->SetArtMethod(resolved_method);
@@ -2066,8 +2069,7 @@
                         &outer_compilation_unit_,
                         codegen_,
                         inline_stats_,
-                        resolved_method->GetQuickenedInfo(),
-                        handles_);
+                        resolved_method->GetQuickenedInfo());
 
   if (builder.BuildGraph() != kAnalysisSuccess) {
     LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCannotBuild)
@@ -2158,7 +2160,6 @@
                    codegen_,
                    outer_compilation_unit_,
                    dex_compilation_unit,
-                   handles_,
                    inline_stats_,
                    total_number_of_dex_registers_ + accessor.RegistersSize(),
                    total_number_of_instructions_ + number_of_instructions,
@@ -2167,7 +2168,8 @@
   inliner.Run();
 }
 
-static bool IsReferenceTypeRefinement(ReferenceTypeInfo declared_rti,
+static bool IsReferenceTypeRefinement(ObjPtr<mirror::Class> declared_class,
+                                      bool declared_is_exact,
                                       bool declared_can_be_null,
                                       HInstruction* actual_obj)
     REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -2176,22 +2178,29 @@
   }
 
   ReferenceTypeInfo actual_rti = actual_obj->GetReferenceTypeInfo();
-  return (actual_rti.IsExact() && !declared_rti.IsExact()) ||
-          declared_rti.IsStrictSupertypeOf(actual_rti);
+  ObjPtr<mirror::Class> actual_class = actual_rti.GetTypeHandle().Get();
+  return (actual_rti.IsExact() && !declared_is_exact) ||
+         (declared_class != actual_class && declared_class->IsAssignableFrom(actual_class));
 }
 
-ReferenceTypeInfo HInliner::GetClassRTI(ObjPtr<mirror::Class> klass) {
-  return ReferenceTypePropagation::IsAdmissible(klass)
-      ? ReferenceTypeInfo::Create(handles_->NewHandle(klass))
-      : graph_->GetInexactObjectRti();
+static bool IsReferenceTypeRefinement(ObjPtr<mirror::Class> declared_class,
+                                      bool declared_can_be_null,
+                                      HInstruction* actual_obj)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  bool admissible = ReferenceTypePropagation::IsAdmissible(declared_class);
+  return IsReferenceTypeRefinement(
+      admissible ? declared_class : GetClassRoot<mirror::Class>(),
+      /*declared_is_exact=*/ admissible && declared_class->CannotBeAssignedFromOtherTypes(),
+      declared_can_be_null,
+      actual_obj);
 }
 
 bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method) {
   // If this is an instance call, test whether the type of the `this` argument
   // is more specific than the class which declares the method.
   if (!resolved_method->IsStatic()) {
-    if (IsReferenceTypeRefinement(GetClassRTI(resolved_method->GetDeclaringClass()),
-                                  /* declared_can_be_null= */ false,
+    if (IsReferenceTypeRefinement(resolved_method->GetDeclaringClass(),
+                                  /*declared_can_be_null=*/ false,
                                   invoke_instruction->InputAt(0u))) {
       return true;
     }
@@ -2210,9 +2219,7 @@
     if (input->GetType() == DataType::Type::kReference) {
       ObjPtr<mirror::Class> param_cls = resolved_method->LookupResolvedClassFromTypeIndex(
           param_list->GetTypeItem(param_idx).type_idx_);
-      if (IsReferenceTypeRefinement(GetClassRTI(param_cls),
-                                    /* declared_can_be_null= */ true,
-                                    input)) {
+      if (IsReferenceTypeRefinement(param_cls, /*declared_can_be_null=*/ true, input)) {
         return true;
       }
     }
@@ -2227,8 +2234,10 @@
   if (return_replacement != nullptr) {
     if (return_replacement->GetType() == DataType::Type::kReference) {
       // Test if the return type is a refinement of the declared return type.
-      if (IsReferenceTypeRefinement(invoke_instruction->GetReferenceTypeInfo(),
-                                    /* declared_can_be_null= */ true,
+      ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo();
+      if (IsReferenceTypeRefinement(invoke_rti.GetTypeHandle().Get(),
+                                    invoke_rti.IsExact(),
+                                    /*declared_can_be_null=*/ true,
                                     return_replacement)) {
         return true;
       } else if (return_replacement->IsInstanceFieldGet()) {
@@ -2258,7 +2267,10 @@
         // some functionality from the reference type propagation.
         DCHECK(return_replacement->IsPhi());
         ObjPtr<mirror::Class> cls = resolved_method->LookupResolvedReturnType();
-        return_replacement->SetReferenceTypeInfo(GetClassRTI(cls));
+        ReferenceTypeInfo rti = ReferenceTypePropagation::IsAdmissible(cls)
+            ? ReferenceTypeInfo::Create(graph_->GetHandleCache()->NewHandle(cls))
+            : graph_->GetInexactObjectRti();
+        return_replacement->SetReferenceTypeInfo(rti);
       }
     }
   }
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 882ba4e..6510857 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -37,7 +37,6 @@
            CodeGenerator* codegen,
            const DexCompilationUnit& outer_compilation_unit,
            const DexCompilationUnit& caller_compilation_unit,
-           VariableSizedHandleScope* handles,
            OptimizingCompilerStats* stats,
            size_t total_number_of_dex_registers,
            size_t total_number_of_instructions,
@@ -54,7 +53,6 @@
         parent_(parent),
         depth_(depth),
         inlining_budget_(0),
-        handles_(handles),
         inline_stats_(nullptr) {}
 
   bool Run() override;
@@ -250,11 +248,6 @@
   void FixUpReturnReferenceType(ArtMethod* resolved_method, HInstruction* return_replacement)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Creates an instance of ReferenceTypeInfo from `klass` if `klass` is
-  // admissible (see ReferenceTypePropagation::IsAdmissible for details).
-  // Otherwise returns inexact Object RTI.
-  ReferenceTypeInfo GetClassRTI(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
-
   bool ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -335,7 +328,6 @@
 
   // The budget left for inlining, in number of instructions.
   size_t inlining_budget_;
-  VariableSizedHandleScope* const handles_;
 
   // Used to record stats about optimizations on the inlined graph.
   // If the inlining is successful, these stats are merged to the caller graph's stats.
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index ea3d3c0..df236c1 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -52,11 +52,9 @@
                                          CodeGenerator* code_generator,
                                          ArrayRef<const uint8_t> interpreter_metadata,
                                          OptimizingCompilerStats* compiler_stats,
-                                         VariableSizedHandleScope* handles,
                                          ScopedArenaAllocator* local_allocator)
     : allocator_(graph->GetAllocator()),
       graph_(graph),
-      handles_(handles),
       dex_file_(dex_file),
       code_item_accessor_(accessor),
       return_type_(return_type),
@@ -382,13 +380,14 @@
         AppendInstruction(new (allocator_) HNativeDebugInfo(dex_pc));
       }
 
-      DCHECK(!Thread::Current()->IsExceptionPending())
+      // Note: There may be no Thread for gtests.
+      DCHECK(Thread::Current() == nullptr || !Thread::Current()->IsExceptionPending())
           << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex())
           << " " << pair.Inst().Name() << "@" << dex_pc;
       if (!ProcessDexInstruction(pair.Inst(), dex_pc, quicken_index)) {
         return false;
       }
-      DCHECK(!Thread::Current()->IsExceptionPending())
+      DCHECK(Thread::Current() == nullptr || !Thread::Current()->IsExceptionPending())
           << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex())
           << " " << pair.Inst().Name() << "@" << dex_pc;
 
@@ -1353,18 +1352,18 @@
 
   // Check if the class will be initialized at runtime.
   if (cls->IsInitialized()) {
-    Runtime* runtime = Runtime::Current();
-    if (runtime->IsAotCompiler()) {
+    const CompilerOptions& compiler_options = code_generator_->GetCompilerOptions();
+    if (compiler_options.IsAotCompiler()) {
       // Assume loaded only if klass is in the boot image. App classes cannot be assumed
       // loaded because we don't even know what class loader will be used to load them.
-      if (IsInBootImage(cls, code_generator_->GetCompilerOptions())) {
+      if (IsInBootImage(cls, compiler_options)) {
         return true;
       }
     } else {
-      DCHECK(runtime->UseJitCompilation());
+      DCHECK(compiler_options.IsJitCompiler());
       if (Runtime::Current()->GetJit()->CanAssumeInitialized(
               cls,
-              graph_->IsCompilingForSharedJitCode())) {
+              compiler_options.IsJitCompilerForSharedCode())) {
         // For JIT, the class cannot revert to an uninitialized state.
         return true;
       }
@@ -1436,7 +1435,7 @@
   if (IsInitialized(klass)) {
     *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
   } else {
-    Handle<mirror::Class> h_klass = handles_->NewHandle(klass);
+    Handle<mirror::Class> h_klass = graph_->GetHandleCache()->NewHandle(klass);
     HLoadClass* cls = BuildLoadClass(h_klass->GetDexTypeIndex(),
                                      h_klass->GetDexFile(),
                                      h_klass,
@@ -1957,7 +1956,8 @@
 
   DataType::Type field_type = GetFieldAccessType(*dex_file_, field_index);
 
-  Handle<mirror::Class> klass = handles_->NewHandle(resolved_field->GetDeclaringClass());
+  Handle<mirror::Class> klass =
+      graph_->GetHandleCache()->NewHandle(resolved_field->GetDeclaringClass());
   HLoadClass* constant = BuildLoadClass(klass->GetDexTypeIndex(),
                                         klass->GetDexFile(),
                                         klass,
@@ -2207,7 +2207,7 @@
   HSharpening::ProcessLoadString(load_string,
                                  code_generator_,
                                  *dex_compilation_unit_,
-                                 handles_);
+                                 graph_->GetHandleCache()->GetHandles());
   AppendInstruction(load_string);
 }
 
@@ -2235,7 +2235,7 @@
     }
   }
 
-  // Note: `klass` must be from `handles_`.
+  // Note: `klass` must be from `graph_->GetHandleCache()`.
   bool is_referrers_class =
       (klass != nullptr) && (outer_compilation_unit_->GetCompilingClass().Get() == klass.Get());
   HLoadClass* load_class = new (allocator_) HLoadClass(
@@ -2273,7 +2273,7 @@
   DCHECK_EQ(klass == nullptr, soa.Self()->IsExceptionPending());
   soa.Self()->ClearException();  // Clean up the exception left by type resolution if any.
 
-  Handle<mirror::Class> h_klass = handles_->NewHandle(klass);
+  Handle<mirror::Class> h_klass = graph_->GetHandleCache()->NewHandle(klass);
   class_cache_.Put(type_index, h_klass);
   return h_klass;
 }
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 95d3315..04f2a22 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -41,7 +41,6 @@
 class OptimizingCompilerStats;
 class ScopedObjectAccess;
 class SsaBuilder;
-class VariableSizedHandleScope;
 
 namespace mirror {
 class Class;
@@ -61,7 +60,6 @@
                       CodeGenerator* code_generator,
                       ArrayRef<const uint8_t> interpreter_metadata,
                       OptimizingCompilerStats* compiler_stats,
-                      VariableSizedHandleScope* handles,
                       ScopedArenaAllocator* local_allocator);
 
   bool Build();
@@ -301,7 +299,6 @@
 
   ArenaAllocator* const allocator_;
   HGraph* const graph_;
-  VariableSizedHandleScope* const handles_;
 
   // The dex file where the method being compiled is, and the bytecode data.
   const DexFile* const dex_file_;
@@ -343,7 +340,7 @@
   ScopedArenaVector<HBasicBlock*> loop_headers_;
 
   // Cached resolved types for the current compilation unit's DexFile.
-  // Handle<>s reference entries in the `handles_`.
+  // Handle<>s reference entries in the `graph_->GetHandleCache()`.
   ScopedArenaSafeMap<dex::TypeIndex, Handle<mirror::Class>> class_cache_;
 
   static constexpr int kDefaultNumberOfLoops = 2;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 8e0b414..5ac77a5 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -18,7 +18,7 @@
 
 #include "art_method-inl.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "data_type-inl.h"
 #include "escape.h"
 #include "intrinsics.h"
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index d940105..653d92a 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -20,7 +20,7 @@
 #include "art_method-inl.h"
 #include "base/utils.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/invoke_type.h"
 #include "driver/compiler_options.h"
 #include "gc/space/image_space.h"
@@ -215,12 +215,12 @@
     if (cache == nullptr) {
       return;  // No cache in the boot image.
     }
-    if (runtime->UseJitCompilation()) {
+    if (compiler_options.IsJitCompiler()) {
       if (!CheckIntegerCache(self, runtime->GetClassLinker(), boot_image_live_objects, cache)) {
         return;  // The cache was somehow messed up, probably by using reflection.
       }
     } else {
-      DCHECK(runtime->IsAotCompiler());
+      DCHECK(compiler_options.IsAotCompiler());
       DCHECK(CheckIntegerCache(self, runtime->GetClassLinker(), boot_image_live_objects, cache));
       if (invoke->InputAt(0)->IsIntConstant()) {
         int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
@@ -287,8 +287,7 @@
   // we need to provide data that shall not lead to a crash even if the fields were
   // modified through reflection since ComputeIntegerValueOfLocations() when JITting.
 
-  Runtime* runtime = Runtime::Current();
-  ClassLinker* class_linker = runtime->GetClassLinker();
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
 
@@ -328,7 +327,7 @@
     ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
     DCHECK(value_field != nullptr);
     info.value_offset = value_field->GetOffset().Uint32Value();
-    if (runtime->UseJitCompilation()) {
+    if (compiler_options.IsJitCompiler()) {
       // Use the current `IntegerCache.low` for JIT to avoid truly surprising behavior if the
       // code messes up the `value` field in the lowest cached Integer using reflection.
       info.low = GetIntegerCacheLowFromIntegerCache(self, class_linker);
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index 50bfe84..d56ae11 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -41,7 +41,9 @@
 void LinearizeTest::TestCode(const std::vector<uint16_t>& data,
                              const uint32_t (&expected_order)[number_of_blocks]) {
   HGraph* graph = CreateCFG(data);
-  std::unique_ptr<CodeGenerator> codegen = CodeGenerator::Create(graph, *compiler_options_);
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(kRuntimeISA, "default");
+  std::unique_ptr<CodeGenerator> codegen = CodeGenerator::Create(graph, *compiler_options);
   SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator());
   liveness.Analyze();
 
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 60f513c..bb8a4dc 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -28,12 +28,15 @@
 namespace art {
 
 class LiveRangesTest : public OptimizingUnitTest {
- public:
+ protected:
   HGraph* BuildGraph(const std::vector<uint16_t>& data);
+
+  std::unique_ptr<CompilerOptions> compiler_options_;
 };
 
 HGraph* LiveRangesTest::BuildGraph(const std::vector<uint16_t>& data) {
   HGraph* graph = CreateCFG(data);
+  compiler_options_ = CommonCompilerTest::CreateCompilerOptions(kRuntimeISA, "default");
   // Suspend checks implementation may change in the future, and this test relies
   // on how instructions are ordered.
   RemoveSuspendChecks(graph);
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index f11f7a9..ba3787e 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -47,8 +47,10 @@
 void LivenessTest::TestCode(const std::vector<uint16_t>& data, const char* expected) {
   HGraph* graph = CreateCFG(data);
   // `Inline` conditions into ifs.
-  PrepareForRegisterAllocation(graph, *compiler_options_).Run();
-  std::unique_ptr<CodeGenerator> codegen = CodeGenerator::Create(graph, *compiler_options_);
+  std::unique_ptr<CompilerOptions> compiler_options =
+      CommonCompilerTest::CreateCompilerOptions(kRuntimeISA, "default");
+  PrepareForRegisterAllocation(graph, *compiler_options).Run();
+  std::unique_ptr<CodeGenerator> codegen = CodeGenerator::Create(graph, *compiler_options);
   SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator());
   liveness.Analyze();
 
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 4c150da..24041e9 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -668,7 +668,7 @@
         // Keep the store inside irreducible loops.
       }
     }
-    if (possibly_redundant) {
+    if (possibly_redundant && !instruction->CanThrow()) {
       possibly_removed_stores_.push_back(instruction);
     }
 
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc
index 7380378..02cb633 100644
--- a/compiler/optimizing/load_store_elimination_test.cc
+++ b/compiler/optimizing/load_store_elimination_test.cc
@@ -26,7 +26,7 @@
 
 namespace art {
 
-class LoadStoreEliminationTest : public ImprovedOptimizingUnitTest {
+class LoadStoreEliminationTest : public OptimizingUnitTest {
  public:
   void PerformLSE() {
     graph_->BuildDominatorTree();
@@ -61,11 +61,9 @@
   //      |
   //     exit
   void CreateTestControlFlowGraph() {
-    pre_header_ = new (GetAllocator()) HBasicBlock(graph_);
-    loop_ = new (GetAllocator()) HBasicBlock(graph_);
-
-    graph_->AddBlock(pre_header_);
-    graph_->AddBlock(loop_);
+    InitGraphAndParameters();
+    pre_header_ = AddNewBlock();
+    loop_ = AddNewBlock();
 
     entry_block_->ReplaceSuccessor(return_block_, pre_header_);
     pre_header_->AddSuccessor(loop_);
@@ -117,15 +115,12 @@
   //
   // Return: the basic blocks forming the CFG in the following order {upper, left, right, down}.
   std::tuple<HBasicBlock*, HBasicBlock*, HBasicBlock*, HBasicBlock*> CreateDiamondShapedCFG() {
+    InitGraphAndParameters();
     CreateEntryBlockInstructions();
 
-    HBasicBlock* upper = new (GetAllocator()) HBasicBlock(graph_);
-    HBasicBlock* left = new (GetAllocator()) HBasicBlock(graph_);
-    HBasicBlock* right = new (GetAllocator()) HBasicBlock(graph_);
-
-    graph_->AddBlock(upper);
-    graph_->AddBlock(left);
-    graph_->AddBlock(right);
+    HBasicBlock* upper = AddNewBlock();
+    HBasicBlock* left = AddNewBlock();
+    HBasicBlock* right = AddNewBlock();
 
     entry_block_->ReplaceSuccessor(return_block_, upper);
     upper->AddSuccessor(left);
@@ -232,21 +227,22 @@
     return store;
   }
 
-  void CreateParameters() override {
-    parameters_.push_back(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
-                                                               dex::TypeIndex(0),
-                                                               0,
-                                                               DataType::Type::kInt32));
+  void InitGraphAndParameters() {
+    InitGraph();
+    AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+                                                      dex::TypeIndex(0),
+                                                      0,
+                                                      DataType::Type::kInt32));
     array_ = parameters_.back();
-    parameters_.push_back(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
-                                                               dex::TypeIndex(1),
-                                                               1,
-                                                               DataType::Type::kInt32));
+    AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+                                                      dex::TypeIndex(1),
+                                                      1,
+                                                      DataType::Type::kInt32));
     i_ = parameters_.back();
-    parameters_.push_back(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
-                                                               dex::TypeIndex(1),
-                                                               2,
-                                                               DataType::Type::kInt32));
+    AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+                                                      dex::TypeIndex(1),
+                                                      2,
+                                                      DataType::Type::kInt32));
     j_ = parameters_.back();
   }
 
@@ -264,7 +260,6 @@
 };
 
 TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c1 = graph_->GetIntConstant(1);
@@ -293,7 +288,6 @@
 }
 
 TEST_F(LoadStoreEliminationTest, SameHeapValue1) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c1 = graph_->GetIntConstant(1);
@@ -316,7 +310,6 @@
 }
 
 TEST_F(LoadStoreEliminationTest, SameHeapValue2) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   // Test LSE handling same value stores on vector.
@@ -334,7 +327,6 @@
 }
 
 TEST_F(LoadStoreEliminationTest, SameHeapValue3) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   // VecStore array[i...] = vdata;
@@ -350,7 +342,6 @@
 }
 
 TEST_F(LoadStoreEliminationTest, OverlappingLoadStore) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c1 = graph_->GetIntConstant(1);
@@ -408,7 +399,6 @@
 // }
 // a[j] = 1;
 TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithoutSideEffects) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c1 = graph_->GetIntConstant(1);
@@ -438,7 +428,6 @@
 //   a[j] = 0;
 // }
 TEST_F(LoadStoreEliminationTest, StoreAfterSIMDLoopWithSideEffects) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -477,7 +466,6 @@
 //   x = a[j];
 // }
 TEST_F(LoadStoreEliminationTest, LoadAfterSIMDLoopWithSideEffects) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -521,8 +509,6 @@
 //   'vstore3' is not removed.
 //   'vstore4' is not removed. Such cases are not supported at the moment.
 TEST_F(LoadStoreEliminationTest, MergePredecessorVecStores) {
-  InitGraph();
-
   HBasicBlock* upper;
   HBasicBlock* left;
   HBasicBlock* right;
@@ -564,8 +550,6 @@
 //   'store2' is not removed.
 //   'store3' is removed.
 TEST_F(LoadStoreEliminationTest, MergePredecessorStores) {
-  InitGraph();
-
   HBasicBlock* upper;
   HBasicBlock* left;
   HBasicBlock* right;
@@ -604,7 +588,6 @@
 //   'vload' is removed.
 //   'vstore3' is removed.
 TEST_F(LoadStoreEliminationTest, RedundantVStoreVLoadInLoop) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -639,7 +622,6 @@
 // This causes stores after such loops not to be removed, even
 // their values are known.
 TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithSideEffects) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -669,7 +651,6 @@
 // As it is not allowed to use defaults for VecLoads, check if there is a new created array
 // a VecLoad used in a loop and after it is not replaced with a default.
 TEST_F(LoadStoreEliminationTest, VLoadDefaultValueInLoopWithoutWriteSideEffects) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -694,7 +675,6 @@
 // As it is not allowed to use defaults for VecLoads, check if there is a new created array
 // a VecLoad is not replaced with a default.
 TEST_F(LoadStoreEliminationTest, VLoadDefaultValue) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -718,7 +698,6 @@
 // As it is allowed to use defaults for ordinary loads, check if there is a new created array
 // a load used in a loop and after it is replaced with a default.
 TEST_F(LoadStoreEliminationTest, LoadDefaultValueInLoopWithoutWriteSideEffects) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -743,7 +722,6 @@
 // As it is allowed to use defaults for ordinary loads, check if there is a new created array
 // a load is replaced with a default.
 TEST_F(LoadStoreEliminationTest, LoadDefaultValue) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -768,7 +746,6 @@
 // check if there is a new created array, a VecLoad and a load used in a loop and after it,
 // VecLoad is not replaced with a default but the load is.
 TEST_F(LoadStoreEliminationTest, VLoadAndLoadDefaultValueInLoopWithoutWriteSideEffects) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -800,7 +777,6 @@
 // check if there is a new created array, a VecLoad and a load,
 // VecLoad is not replaced with a default but the load is.
 TEST_F(LoadStoreEliminationTest, VLoadAndLoadDefaultValue) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -831,7 +807,6 @@
 // loads getting the same value.
 // Check a load getting a known value is eliminated (a loop test case).
 TEST_F(LoadStoreEliminationTest, VLoadDefaultValueAndVLoadInLoopWithoutWriteSideEffects) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
@@ -863,7 +838,6 @@
 // loads getting the same value.
 // Check a load getting a known value is eliminated.
 TEST_F(LoadStoreEliminationTest, VLoadDefaultValueAndVLoad) {
-  InitGraph();
   CreateTestControlFlowGraph();
 
   HInstruction* c0 = graph_->GetIntConstant(0);
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 8dead2f..4c9b01c 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -431,7 +431,7 @@
                         InductionVarRange* induction_range) {
   for (int i = 0; i < count; i++) {
     // Perform peeling.
-    PeelUnrollSimpleHelper helper(loop_info, induction_range);
+    LoopClonerSimpleHelper helper(loop_info, induction_range);
     helper.DoPeeling();
   }
 }
@@ -807,7 +807,7 @@
 
     // Perform unrolling.
     HLoopInformation* loop_info = analysis_info->GetLoopInfo();
-    PeelUnrollSimpleHelper helper(loop_info, &induction_range_);
+    LoopClonerSimpleHelper helper(loop_info, &induction_range_);
     helper.DoUnrolling();
 
     // Remove the redundant loop check after unrolling.
@@ -832,7 +832,7 @@
 
   if (generate_code) {
     // Perform peeling.
-    PeelUnrollSimpleHelper helper(loop_info, &induction_range_);
+    LoopClonerSimpleHelper helper(loop_info, &induction_range_);
     helper.DoPeeling();
 
     // Statically evaluate loop check after peeling for loop invariant condition.
@@ -905,7 +905,7 @@
   }
 
   // Run 'IsLoopClonable' the last as it might be time-consuming.
-  if (!PeelUnrollHelper::IsLoopClonable(loop_info)) {
+  if (!LoopClonerHelper::IsLoopClonable(loop_info)) {
     return false;
   }
 
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index 8b4d58e..bda2528 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -15,6 +15,7 @@
  */
 
 #include "code_generator.h"
+#include "driver/compiler_options.h"
 #include "loop_optimization.h"
 #include "optimizing_unit_test.h"
 
@@ -28,12 +29,12 @@
 class LoopOptimizationTest : public OptimizingUnitTest {
  protected:
   void SetUp() override {
-    OverrideInstructionSetFeatures(instruction_set_, "default");
     OptimizingUnitTest::SetUp();
 
     graph_ = CreateGraph();
     BuildGraph();
     iva_  = new (GetAllocator()) HInductionVarAnalysis(graph_);
+    compiler_options_ = CommonCompilerTest::CreateCompilerOptions(kRuntimeISA, "default");
     DCHECK(compiler_options_ != nullptr);
     codegen_ = CodeGenerator::Create(graph_, *compiler_options_);
     DCHECK(codegen_.get() != nullptr);
@@ -43,6 +44,7 @@
 
   void TearDown() override {
     codegen_.reset();
+    compiler_options_.reset();
     graph_ = nullptr;
     ResetPoolAndAllocator();
     OptimizingUnitTest::TearDown();
@@ -117,6 +119,7 @@
   // General building fields.
   HGraph* graph_;
 
+  std::unique_ptr<CompilerOptions> compiler_options_;
   std::unique_ptr<CodeGenerator> codegen_;
   HInductionVarAnalysis* iva_;
   HLoopOptimization* loop_opt_;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 810871c..3ea13b6 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -23,7 +23,7 @@
 #include "base/logging.h"
 #include "base/stl_util.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "code_generator.h"
 #include "common_dominator.h"
 #include "intrinsics.h"
@@ -39,12 +39,11 @@
 // double).
 static constexpr bool kEnableFloatingPointStaticEvaluation = (FLT_EVAL_METHOD == 0);
 
-void HGraph::InitializeInexactObjectRTI(VariableSizedHandleScope* handles) {
+ReferenceTypeInfo::TypeHandle HandleCache::CreateRootHandle(VariableSizedHandleScope* handles,
+                                                            ClassRoot class_root) {
+  // Mutator lock is required for NewHandle and GetClassRoot().
   ScopedObjectAccess soa(Thread::Current());
-  // Create the inexact Object reference type and store it in the HGraph.
-  inexact_object_rti_ = ReferenceTypeInfo::Create(
-      handles->NewHandle(GetClassRoot<mirror::Object>()),
-      /* is_exact= */ false);
+  return handles->NewHandle(GetClassRoot(class_root));
 }
 
 void HGraph::AddBlock(HBasicBlock* block) {
@@ -662,7 +661,7 @@
   // id and/or any invariants the graph is assuming when adding new instructions.
   if ((cached_null_constant_ == nullptr) || (cached_null_constant_->GetBlock() == nullptr)) {
     cached_null_constant_ = new (allocator_) HNullConstant(dex_pc);
-    cached_null_constant_->SetReferenceTypeInfo(inexact_object_rti_);
+    cached_null_constant_->SetReferenceTypeInfo(GetInexactObjectRti());
     InsertConstant(cached_null_constant_);
   }
   if (kIsDebugBuild) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3427893..7c0e973 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -32,6 +32,7 @@
 #include "base/stl_util.h"
 #include "base/transform_array_ref.h"
 #include "art_method.h"
+#include "class_root.h"
 #include "data_type.h"
 #include "deoptimization_kind.h"
 #include "dex/dex_file.h"
@@ -272,13 +273,6 @@
     return GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get());
   }
 
-  bool IsStrictSupertypeOf(ReferenceTypeInfo rti) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(IsValid());
-    DCHECK(rti.IsValid());
-    return GetTypeHandle().Get() != rti.GetTypeHandle().Get() &&
-        GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get());
-  }
-
   // Returns true if the type information provide the same amount of details.
   // Note that it does not mean that the instructions have the same actual type
   // (because the type can be the result of a merge).
@@ -309,11 +303,75 @@
 
 std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs);
 
+class HandleCache {
+ public:
+  explicit HandleCache(VariableSizedHandleScope* handles) : handles_(handles) { }
+
+  VariableSizedHandleScope* GetHandles() { return handles_; }
+
+  template <typename T>
+  MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_) {
+    return handles_->NewHandle(object);
+  }
+
+  template <typename T>
+  MutableHandle<T> NewHandle(ObjPtr<T> object) REQUIRES_SHARED(Locks::mutator_lock_) {
+    return handles_->NewHandle(object);
+  }
+
+  ReferenceTypeInfo::TypeHandle GetObjectClassHandle() {
+    return GetRootHandle(ClassRoot::kJavaLangObject, &object_class_handle_);
+  }
+
+  ReferenceTypeInfo::TypeHandle GetClassClassHandle() {
+    return GetRootHandle(ClassRoot::kJavaLangClass, &class_class_handle_);
+  }
+
+  ReferenceTypeInfo::TypeHandle GetMethodHandleClassHandle() {
+    return GetRootHandle(ClassRoot::kJavaLangInvokeMethodHandleImpl, &method_handle_class_handle_);
+  }
+
+  ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle() {
+    return GetRootHandle(ClassRoot::kJavaLangInvokeMethodType, &method_type_class_handle_);
+  }
+
+  ReferenceTypeInfo::TypeHandle GetStringClassHandle() {
+    return GetRootHandle(ClassRoot::kJavaLangString, &string_class_handle_);
+  }
+
+  ReferenceTypeInfo::TypeHandle GetThrowableClassHandle() {
+    return GetRootHandle(ClassRoot::kJavaLangThrowable, &throwable_class_handle_);
+  }
+
+
+ private:
+  inline ReferenceTypeInfo::TypeHandle GetRootHandle(ClassRoot class_root,
+                                                     ReferenceTypeInfo::TypeHandle* cache) {
+    if (UNLIKELY(!ReferenceTypeInfo::IsValidHandle(*cache))) {
+      *cache = CreateRootHandle(handles_, class_root);
+    }
+    return *cache;
+  }
+
+  static ReferenceTypeInfo::TypeHandle CreateRootHandle(VariableSizedHandleScope* handles,
+                                                        ClassRoot class_root);
+
+  VariableSizedHandleScope* handles_;
+
+  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_;
+};
+
 // Control-flow graph of a method. Contains a list of basic blocks.
 class HGraph : public ArenaObject<kArenaAllocGraph> {
  public:
   HGraph(ArenaAllocator* allocator,
          ArenaStack* arena_stack,
+         VariableSizedHandleScope* handles,
          const DexFile& dex_file,
          uint32_t method_idx,
          InstructionSet instruction_set,
@@ -321,11 +379,11 @@
          bool dead_reference_safe = false,
          bool debuggable = false,
          bool osr = false,
-         bool is_shared_jit_code = false,
          bool baseline = false,
          int start_instruction_id = 0)
       : allocator_(allocator),
         arena_stack_(arena_stack),
+        handle_cache_(handles),
         blocks_(allocator->Adapter(kArenaAllocBlockList)),
         reverse_post_order_(allocator->Adapter(kArenaAllocReversePostOrder)),
         linear_order_(allocator->Adapter(kArenaAllocLinearOrder)),
@@ -357,19 +415,17 @@
         cached_double_constants_(std::less<int64_t>(), allocator->Adapter(kArenaAllocConstantsMap)),
         cached_current_method_(nullptr),
         art_method_(nullptr),
-        inexact_object_rti_(ReferenceTypeInfo::CreateInvalid()),
         osr_(osr),
         baseline_(baseline),
-        cha_single_implementation_list_(allocator->Adapter(kArenaAllocCHA)),
-        is_shared_jit_code_(is_shared_jit_code) {
+        cha_single_implementation_list_(allocator->Adapter(kArenaAllocCHA)) {
     blocks_.reserve(kDefaultNumberOfBlocks);
   }
 
-  // Acquires and stores RTI of inexact Object to be used when creating HNullConstant.
-  void InitializeInexactObjectRTI(VariableSizedHandleScope* handles);
-
   ArenaAllocator* GetAllocator() const { return allocator_; }
   ArenaStack* GetArenaStack() const { return arena_stack_; }
+
+  HandleCache* GetHandleCache() { return &handle_cache_; }
+
   const ArenaVector<HBasicBlock*>& GetBlocks() const { return blocks_; }
 
   bool IsInSsaForm() const { return in_ssa_form_; }
@@ -593,10 +649,6 @@
 
   bool IsCompilingBaseline() const { return baseline_; }
 
-  bool IsCompilingForSharedJitCode() const {
-    return is_shared_jit_code_;
-  }
-
   ArenaSet<ArtMethod*>& GetCHASingleImplementationList() {
     return cha_single_implementation_list_;
   }
@@ -632,7 +684,9 @@
   // before cursor.
   HInstruction* InsertOppositeCondition(HInstruction* cond, HInstruction* cursor);
 
-  ReferenceTypeInfo GetInexactObjectRti() const { return inexact_object_rti_; }
+  ReferenceTypeInfo GetInexactObjectRti() {
+    return ReferenceTypeInfo::Create(handle_cache_.GetObjectClassHandle(), /* is_exact= */ false);
+  }
 
   uint32_t GetNumberOfCHAGuards() { return number_of_cha_guards_; }
   void SetNumberOfCHAGuards(uint32_t num) { number_of_cha_guards_ = num; }
@@ -675,6 +729,8 @@
   ArenaAllocator* const allocator_;
   ArenaStack* const arena_stack_;
 
+  HandleCache handle_cache_;
+
   // List of blocks in insertion order.
   ArenaVector<HBasicBlock*> blocks_;
 
@@ -781,10 +837,6 @@
   // (such as when the superclass could not be found).
   ArtMethod* art_method_;
 
-  // Keep the RTI of inexact Object to avoid having to pass stack handle
-  // collection pointer to passes which may create NullConstant.
-  ReferenceTypeInfo inexact_object_rti_;
-
   // Whether we are compiling this graph for on stack replacement: this will
   // make all loops seen as irreducible and emit special stack maps to mark
   // compiled code entries which the interpreter can directly jump to.
@@ -797,10 +849,6 @@
   // List of methods that are assumed to have single implementation.
   ArenaSet<ArtMethod*> cha_single_implementation_list_;
 
-  // Whether we are JIT compiling in the shared region area, putting
-  // restrictions on, for example, how literals are being generated.
-  bool is_shared_jit_code_;
-
   friend class SsaBuilder;           // For caching constants.
   friend class SsaLivenessAnalysis;  // For the linear order.
   friend class HInliner;             // For the reverse post order.
@@ -1387,7 +1435,7 @@
   DISALLOW_COPY_AND_ASSIGN(HLoopInformationOutwardIterator);
 };
 
-#define FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M)                         \
+#define FOR_EACH_CONCRETE_INSTRUCTION_SCALAR_COMMON(M)                  \
   M(Above, Condition)                                                   \
   M(AboveOrEqual, Condition)                                            \
   M(Abs, UnaryOperation)                                                \
@@ -1477,7 +1525,9 @@
   M(TryBoundary, Instruction)                                           \
   M(TypeConversion, Instruction)                                        \
   M(UShr, BinaryOperation)                                              \
-  M(Xor, BinaryOperation)                                               \
+  M(Xor, BinaryOperation)
+
+#define FOR_EACH_CONCRETE_INSTRUCTION_VECTOR_COMMON(M)                  \
   M(VecReplicateScalar, VecUnaryOperation)                              \
   M(VecExtractScalar, VecUnaryOperation)                                \
   M(VecReduce, VecUnaryOperation)                                       \
@@ -1508,6 +1558,10 @@
   M(VecLoad, VecMemoryOperation)                                        \
   M(VecStore, VecMemoryOperation)                                       \
 
+#define FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M)                         \
+  FOR_EACH_CONCRETE_INSTRUCTION_SCALAR_COMMON(M)                        \
+  FOR_EACH_CONCRETE_INSTRUCTION_VECTOR_COMMON(M)
+
 /*
  * Instructions, shared across several (not all) architectures.
  */
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index fb60c01..7483190 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -167,8 +167,7 @@
     HGraph* graph,
     OptimizingCompilerStats* stats,
     CodeGenerator* codegen,
-    const DexCompilationUnit& dex_compilation_unit,
-    VariableSizedHandleScope* handles) {
+    const DexCompilationUnit& dex_compilation_unit) {
   ArenaVector<HOptimization*> optimizations(allocator->Adapter());
 
   // Some optimizations require SideEffectsAnalysis or HInductionVarAnalysis
@@ -243,7 +242,6 @@
                                        codegen,
                                        dex_compilation_unit,    // outer_compilation_unit
                                        dex_compilation_unit,    // outermost_compilation_unit
-                                       handles,
                                        stats,
                                        accessor.RegistersSize(),
                                        /* total_number_of_instructions= */ 0,
@@ -253,7 +251,7 @@
         break;
       }
       case OptimizationPass::kSelectGenerator:
-        opt = new (allocator) HSelectGenerator(graph, handles, stats, pass_name);
+        opt = new (allocator) HSelectGenerator(graph, stats, pass_name);
         break;
       case OptimizationPass::kInstructionSimplifier:
         opt = new (allocator) InstructionSimplifier(graph, codegen, stats, pass_name);
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index f4777ad..5ed3762 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -142,8 +142,7 @@
     HGraph* graph,
     OptimizingCompilerStats* stats,
     CodeGenerator* codegen,
-    const DexCompilationUnit& dex_compilation_unit,
-    VariableSizedHandleScope* handles);
+    const DexCompilationUnit& dex_compilation_unit);
 
 }  // namespace art
 
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3945b17..2d5216a 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -32,7 +32,6 @@
 #include "base/scoped_arena_allocator.h"
 #include "base/timing_logger.h"
 #include "builder.h"
-#include "class_root.h"
 #include "code_generator.h"
 #include "compiled_method.h"
 #include "compiler.h"
@@ -308,7 +307,6 @@
                         CodeGenerator* codegen,
                         const DexCompilationUnit& dex_compilation_unit,
                         PassObserver* pass_observer,
-                        VariableSizedHandleScope* handles,
                         const OptimizationDef definitions[],
                         size_t length) const {
     // Convert definitions to optimization passes.
@@ -319,8 +317,7 @@
         graph,
         compilation_stats_.get(),
         codegen,
-        dex_compilation_unit,
-        handles);
+        dex_compilation_unit);
     DCHECK_EQ(length, optimizations.size());
     // Run the optimization passes one by one. Any "depends_on" pass refers back to
     // the most recent occurrence of that pass, skipped or executed.
@@ -351,17 +348,15 @@
       CodeGenerator* codegen,
       const DexCompilationUnit& dex_compilation_unit,
       PassObserver* pass_observer,
-      VariableSizedHandleScope* handles,
       const OptimizationDef (&definitions)[length]) const {
     return RunOptimizations(
-        graph, codegen, dex_compilation_unit, pass_observer, handles, definitions, length);
+        graph, codegen, dex_compilation_unit, pass_observer, definitions, length);
   }
 
   void RunOptimizations(HGraph* graph,
                         CodeGenerator* codegen,
                         const DexCompilationUnit& dex_compilation_unit,
-                        PassObserver* pass_observer,
-                        VariableSizedHandleScope* handles) const;
+                        PassObserver* pass_observer) const;
 
  private:
   // Create a 'CompiledMethod' for an optimized graph.
@@ -384,7 +379,6 @@
                             ArtMethod* method,
                             bool baseline,
                             bool osr,
-                            bool is_shared_jit_code,
                             VariableSizedHandleScope* handles) const;
 
   CodeGenerator* TryCompileIntrinsic(ArenaAllocator* allocator,
@@ -397,14 +391,12 @@
   bool RunArchOptimizations(HGraph* graph,
                             CodeGenerator* codegen,
                             const DexCompilationUnit& dex_compilation_unit,
-                            PassObserver* pass_observer,
-                            VariableSizedHandleScope* handles) const;
+                            PassObserver* pass_observer) const;
 
   bool RunBaselineOptimizations(HGraph* graph,
                                 CodeGenerator* codegen,
                                 const DexCompilationUnit& dex_compilation_unit,
-                                PassObserver* pass_observer,
-                                VariableSizedHandleScope* handles) const;
+                                PassObserver* pass_observer) const;
 
   std::vector<uint8_t> GenerateJitDebugInfo(const debug::MethodDebugInfo& method_debug_info);
 
@@ -457,8 +449,7 @@
 bool OptimizingCompiler::RunBaselineOptimizations(HGraph* graph,
                                                   CodeGenerator* codegen,
                                                   const DexCompilationUnit& dex_compilation_unit,
-                                                  PassObserver* pass_observer,
-                                                  VariableSizedHandleScope* handles) const {
+                                                  PassObserver* pass_observer) const {
   switch (codegen->GetCompilerOptions().GetInstructionSet()) {
 #ifdef ART_ENABLE_CODEGEN_x86
     case InstructionSet::kX86: {
@@ -469,7 +460,6 @@
                               codegen,
                               dex_compilation_unit,
                               pass_observer,
-                              handles,
                               x86_optimizations);
     }
 #endif
@@ -478,7 +468,6 @@
       UNUSED(codegen);
       UNUSED(dex_compilation_unit);
       UNUSED(pass_observer);
-      UNUSED(handles);
       return false;
   }
 }
@@ -486,8 +475,7 @@
 bool OptimizingCompiler::RunArchOptimizations(HGraph* graph,
                                               CodeGenerator* codegen,
                                               const DexCompilationUnit& dex_compilation_unit,
-                                              PassObserver* pass_observer,
-                                              VariableSizedHandleScope* handles) const {
+                                              PassObserver* pass_observer) const {
   switch (codegen->GetCompilerOptions().GetInstructionSet()) {
 #if defined(ART_ENABLE_CODEGEN_arm)
     case InstructionSet::kThumb2:
@@ -502,7 +490,6 @@
                               codegen,
                               dex_compilation_unit,
                               pass_observer,
-                              handles,
                               arm_optimizations);
     }
 #endif
@@ -518,7 +505,6 @@
                               codegen,
                               dex_compilation_unit,
                               pass_observer,
-                              handles,
                               arm64_optimizations);
     }
 #endif
@@ -535,7 +521,6 @@
                               codegen,
                               dex_compilation_unit,
                               pass_observer,
-                              handles,
                               x86_optimizations);
     }
 #endif
@@ -551,7 +536,6 @@
                               codegen,
                               dex_compilation_unit,
                               pass_observer,
-                              handles,
                               x86_64_optimizations);
     }
 #endif
@@ -596,8 +580,7 @@
 void OptimizingCompiler::RunOptimizations(HGraph* graph,
                                           CodeGenerator* codegen,
                                           const DexCompilationUnit& dex_compilation_unit,
-                                          PassObserver* pass_observer,
-                                          VariableSizedHandleScope* handles) const {
+                                          PassObserver* pass_observer) const {
   const std::vector<std::string>* pass_names = GetCompilerOptions().GetPassesToRun();
   if (pass_names != nullptr) {
     // If passes were defined on command-line, build the optimization
@@ -613,7 +596,6 @@
                      codegen,
                      dex_compilation_unit,
                      pass_observer,
-                     handles,
                      optimizations.data(),
                      length);
     return;
@@ -683,10 +665,9 @@
                    codegen,
                    dex_compilation_unit,
                    pass_observer,
-                   handles,
                    optimizations);
 
-  RunArchOptimizations(graph, codegen, dex_compilation_unit, pass_observer, handles);
+  RunArchOptimizations(graph, codegen, dex_compilation_unit, pass_observer);
 }
 
 static ArenaVector<linker::LinkerPatch> EmitAndSortLinkerPatches(CodeGenerator* codegen) {
@@ -737,7 +718,6 @@
                                               ArtMethod* method,
                                               bool baseline,
                                               bool osr,
-                                              bool is_shared_jit_code,
                                               VariableSizedHandleScope* handles) const {
   MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptBytecodeCompilation);
   const CompilerOptions& compiler_options = GetCompilerOptions();
@@ -799,6 +779,7 @@
   HGraph* graph = new (allocator) HGraph(
       allocator,
       arena_stack,
+      handles,
       dex_file,
       method_idx,
       compiler_options.GetInstructionSet(),
@@ -806,7 +787,6 @@
       dead_reference_safe,
       compiler_options.GetDebuggable(),
       /* osr= */ osr,
-      /* is_shared_jit_code= */ is_shared_jit_code,
       /* baseline= */ baseline);
 
   if (method != nullptr) {
@@ -838,8 +818,7 @@
                           &dex_compilation_unit,
                           codegen.get(),
                           compilation_stats_.get(),
-                          interpreter_metadata,
-                          handles);
+                          interpreter_metadata);
     GraphAnalysisResult result = builder.BuildGraph();
     if (result != kAnalysisSuccess) {
       switch (result) {
@@ -882,9 +861,9 @@
   }
 
   if (baseline) {
-    RunBaselineOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer, handles);
+    RunBaselineOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer);
   } else {
-    RunOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer, handles);
+    RunOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer);
   }
 
   RegisterAllocator::Strategy regalloc_strategy =
@@ -927,6 +906,7 @@
   HGraph* graph = new (allocator) HGraph(
       allocator,
       arena_stack,
+      handles,
       dex_file,
       method_idx,
       compiler_options.GetInstructionSet(),
@@ -963,8 +943,7 @@
                           &dex_compilation_unit,
                           codegen.get(),
                           compilation_stats_.get(),
-                          /* interpreter_metadata= */ ArrayRef<const uint8_t>(),
-                          handles);
+                          /* interpreter_metadata= */ ArrayRef<const uint8_t>());
     builder.BuildIntrinsicGraph(method);
   }
 
@@ -977,10 +956,9 @@
                    codegen.get(),
                    dex_compilation_unit,
                    &pass_observer,
-                   handles,
                    optimizations);
 
-  RunArchOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer, handles);
+  RunArchOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer);
 
   AllocateRegisters(graph,
                     codegen.get(),
@@ -1011,6 +989,7 @@
                                             const DexFile& dex_file,
                                             Handle<mirror::DexCache> dex_cache) const {
   const CompilerOptions& compiler_options = GetCompilerOptions();
+  DCHECK(compiler_options.IsAotCompiler());
   CompiledMethod* compiled_method = nullptr;
   Runtime* runtime = Runtime::Current();
   DCHECK(runtime->IsAotCompiler());
@@ -1069,7 +1048,6 @@
                        method,
                        compiler_options.IsBaseline(),
                        /* osr= */ false,
-                       /* is_shared_jit_code= */ false,
                        &handles));
       }
     }
@@ -1218,6 +1196,9 @@
                                     bool baseline,
                                     bool osr,
                                     jit::JitLogger* jit_logger) {
+  const CompilerOptions& compiler_options = GetCompilerOptions();
+  DCHECK(compiler_options.IsJitCompiler());
+  DCHECK_EQ(compiler_options.IsJitCompilerForSharedCode(), code_cache->IsSharedRegion(*region));
   StackHandleScope<3> hs(self);
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
       method->GetDeclaringClass()->GetClassLoader()));
@@ -1234,7 +1215,6 @@
   ArenaAllocator allocator(runtime->GetJitArenaPool());
 
   if (UNLIKELY(method->IsNative())) {
-    const CompilerOptions& compiler_options = GetCompilerOptions();
     JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod(
         compiler_options, access_flags, method_idx, *dex_file);
     std::vector<Handle<mirror::Object>> roots;
@@ -1335,9 +1315,8 @@
                    &code_allocator,
                    dex_compilation_unit,
                    method,
-                   baseline || GetCompilerOptions().IsBaseline(),
+                   baseline || compiler_options.IsBaseline(),
                    osr,
-                   /* is_shared_jit_code= */ code_cache->IsSharedRegion(*region),
                    &handles));
     if (codegen.get() == nullptr) {
       return false;
@@ -1372,7 +1351,6 @@
                      }));
 
   // Add debug info after we know the code location but before we update entry-point.
-  const CompilerOptions& compiler_options = GetCompilerOptions();
   std::vector<uint8_t> debug_info;
   if (compiler_options.GenerateAnyDebugInfo()) {
     debug::MethodDebugInfo info = {};
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 83dbef7..621e863 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -97,6 +97,10 @@
   kNotInlinedWont,
   kNotInlinedRecursiveBudget,
   kNotInlinedProxy,
+  kNotInlinedUnresolved,
+  kNotInlinedPolymorphic,
+  kNotInlinedCustom,
+  kTryInline,
   kConstructorFenceGeneratedNew,
   kConstructorFenceGeneratedFinal,
   kConstructorFenceRemovedLSE,
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index eb262bc..484e444 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -56,11 +56,11 @@
 #define FIVE_REGISTERS_CODE_ITEM(...)  N_REGISTERS_CODE_ITEM(5, __VA_ARGS__)
 #define SIX_REGISTERS_CODE_ITEM(...)   N_REGISTERS_CODE_ITEM(6, __VA_ARGS__)
 
-LiveInterval* BuildInterval(const size_t ranges[][2],
-                            size_t number_of_ranges,
-                            ScopedArenaAllocator* allocator,
-                            int reg = -1,
-                            HInstruction* defined_by = nullptr) {
+inline LiveInterval* BuildInterval(const size_t ranges[][2],
+                                   size_t number_of_ranges,
+                                   ScopedArenaAllocator* allocator,
+                                   int reg = -1,
+                                   HInstruction* defined_by = nullptr) {
   LiveInterval* interval =
       LiveInterval::MakeInterval(allocator, DataType::Type::kInt32, defined_by);
   if (defined_by != nullptr) {
@@ -73,7 +73,7 @@
   return interval;
 }
 
-void RemoveSuspendChecks(HGraph* graph) {
+inline void RemoveSuspendChecks(HGraph* graph) {
   for (HBasicBlock* block : graph->GetBlocks()) {
     if (block != nullptr) {
       if (block->GetLoopInformation() != nullptr) {
@@ -109,7 +109,12 @@
 // multiple inheritance errors from having two gtest as a parent twice.
 class OptimizingUnitTestHelper {
  public:
-  OptimizingUnitTestHelper() : pool_and_allocator_(new ArenaPoolAndAllocator()) { }
+  OptimizingUnitTestHelper()
+      : pool_and_allocator_(new ArenaPoolAndAllocator()),
+        graph_(nullptr),
+        entry_block_(nullptr),
+        return_block_(nullptr),
+        exit_block_(nullptr) { }
 
   ArenaAllocator* GetAllocator() { return pool_and_allocator_->GetAllocator(); }
   ArenaStack* GetArenaStack() { return pool_and_allocator_->GetArenaStack(); }
@@ -117,10 +122,9 @@
 
   void ResetPoolAndAllocator() {
     pool_and_allocator_.reset(new ArenaPoolAndAllocator());
-    handles_.reset();  // When getting rid of the old HGraph, we can also reset handles_.
   }
 
-  HGraph* CreateGraph() {
+  HGraph* CreateGraph(VariableSizedHandleScope* handles = nullptr) {
     ArenaAllocator* const allocator = pool_and_allocator_->GetAllocator();
 
     // Reserve a big array of 0s so the dex file constructor can offsets from the header.
@@ -137,18 +141,21 @@
         /*oat_dex_file*/ nullptr,
         /*container*/ nullptr));
 
-    return new (allocator) HGraph(
+    graph_ = new (allocator) HGraph(
         allocator,
         pool_and_allocator_->GetArenaStack(),
+        handles,
         *dex_files_.back(),
         /*method_idx*/-1,
         kRuntimeISA);
+    return graph_;
   }
 
   // Create a control-flow graph from Dex instructions.
   HGraph* CreateCFG(const std::vector<uint16_t>& data,
-                    DataType::Type return_type = DataType::Type::kInt32) {
-    HGraph* graph = CreateGraph();
+                    DataType::Type return_type = DataType::Type::kInt32,
+                    VariableSizedHandleScope* handles = nullptr) {
+    HGraph* graph = CreateGraph(handles);
 
     // The code item data might not aligned to 4 bytes, copy it to ensure that.
     const size_t code_item_size = data.size() * sizeof(data.front());
@@ -158,13 +165,9 @@
     const dex::CodeItem* code_item = reinterpret_cast<const dex::CodeItem*>(aligned_data);
 
     {
-      ScopedObjectAccess soa(Thread::Current());
-      if (handles_ == nullptr) {
-        handles_.reset(new VariableSizedHandleScope(soa.Self()));
-      }
       const DexCompilationUnit* dex_compilation_unit =
           new (graph->GetAllocator()) DexCompilationUnit(
-              handles_->NewHandle<mirror::ClassLoader>(nullptr),
+              /* class_loader= */ Handle<mirror::ClassLoader>(),  // Invalid handle.
               /* class_linker= */ nullptr,
               graph->GetDexFile(),
               code_item,
@@ -172,14 +175,41 @@
               /* method_idx= */ dex::kDexNoIndex,
               /* access_flags= */ 0u,
               /* verified_method= */ nullptr,
-              handles_->NewHandle<mirror::DexCache>(nullptr));
+              /* dex_cache= */ Handle<mirror::DexCache>());  // Invalid handle.
       CodeItemDebugInfoAccessor accessor(graph->GetDexFile(), code_item, /*dex_method_idx*/ 0u);
-      HGraphBuilder builder(graph, dex_compilation_unit, accessor, handles_.get(), return_type);
+      HGraphBuilder builder(graph, dex_compilation_unit, accessor, return_type);
       bool graph_built = (builder.BuildGraph() == kAnalysisSuccess);
       return graph_built ? graph : nullptr;
     }
   }
 
+  void InitGraph() {
+    CreateGraph();
+    entry_block_ = AddNewBlock();
+    return_block_ = AddNewBlock();
+    exit_block_ = AddNewBlock();
+
+    graph_->SetEntryBlock(entry_block_);
+    graph_->SetExitBlock(exit_block_);
+
+    entry_block_->AddSuccessor(return_block_);
+    return_block_->AddSuccessor(exit_block_);
+
+    return_block_->AddInstruction(new (GetAllocator()) HReturnVoid());
+    exit_block_->AddInstruction(new (GetAllocator()) HExit());
+  }
+
+  void AddParameter(HInstruction* parameter) {
+    entry_block_->AddInstruction(parameter);
+    parameters_.push_back(parameter);
+  }
+
+  HBasicBlock* AddNewBlock() {
+    HBasicBlock* block = new (GetAllocator()) HBasicBlock(graph_);
+    graph_->AddBlock(block);
+    return block;
+  }
+
   // Run GraphChecker with all checks.
   //
   // Return: the status whether the run is successful.
@@ -187,6 +217,10 @@
     return CheckGraph(graph, /*check_ref_type_info=*/true);
   }
 
+  bool CheckGraph() {
+    return CheckGraph(graph_);
+  }
+
   // Run GraphChecker with all checks except reference type information checks.
   //
   // Return: the status whether the run is successful.
@@ -194,62 +228,8 @@
     return CheckGraph(graph, /*check_ref_type_info=*/false);
   }
 
- private:
-  bool CheckGraph(HGraph* graph, bool check_ref_type_info) {
-    GraphChecker checker(graph);
-    checker.SetRefTypeInfoCheckEnabled(check_ref_type_info);
-    checker.Run();
-    checker.Dump(std::cerr);
-    return checker.IsValid();
-  }
-
-  std::vector<std::unique_ptr<const StandardDexFile>> dex_files_;
-  std::unique_ptr<ArenaPoolAndAllocator> pool_and_allocator_;
-  std::unique_ptr<VariableSizedHandleScope> handles_;
-};
-
-class OptimizingUnitTest : public CommonCompilerTest, public OptimizingUnitTestHelper {};
-
-// OptimizingUnitTest with some handy functions to ease the graph creation.
-class ImprovedOptimizingUnitTest : public OptimizingUnitTest {
- public:
-  ImprovedOptimizingUnitTest() : graph_(CreateGraph()),
-                                 entry_block_(nullptr),
-                                 return_block_(nullptr),
-                                 exit_block_(nullptr) {}
-
-  virtual ~ImprovedOptimizingUnitTest() {}
-
-  void InitGraph() {
-    entry_block_ = new (GetAllocator()) HBasicBlock(graph_);
-    graph_->AddBlock(entry_block_);
-    graph_->SetEntryBlock(entry_block_);
-
-    return_block_ = new (GetAllocator()) HBasicBlock(graph_);
-    graph_->AddBlock(return_block_);
-
-    exit_block_ = new (GetAllocator()) HBasicBlock(graph_);
-    graph_->AddBlock(exit_block_);
-    graph_->SetExitBlock(exit_block_);
-
-    entry_block_->AddSuccessor(return_block_);
-    return_block_->AddSuccessor(exit_block_);
-
-    CreateParameters();
-    for (HInstruction* parameter : parameters_) {
-      entry_block_->AddInstruction(parameter);
-    }
-
-    return_block_->AddInstruction(new (GetAllocator()) HReturnVoid());
-    exit_block_->AddInstruction(new (GetAllocator()) HExit());
-  }
-
-  bool CheckGraph() {
-    return OptimizingUnitTestHelper::CheckGraph(graph_);
-  }
-
   bool CheckGraphSkipRefTypeInfoChecks() {
-    return OptimizingUnitTestHelper::CheckGraphSkipRefTypeInfoChecks(graph_);
+    return CheckGraphSkipRefTypeInfoChecks(graph_);
   }
 
   HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction,
@@ -267,12 +247,18 @@
   }
 
  protected:
-  // Create parameters to be added to the graph entry block.
-  // Subclasses can override it to create parameters they need.
-  virtual void CreateParameters() { /* do nothing */ }
+  bool CheckGraph(HGraph* graph, bool check_ref_type_info) {
+    GraphChecker checker(graph);
+    checker.SetRefTypeInfoCheckEnabled(check_ref_type_info);
+    checker.Run();
+    checker.Dump(std::cerr);
+    return checker.IsValid();
+  }
+
+  std::vector<std::unique_ptr<const StandardDexFile>> dex_files_;
+  std::unique_ptr<ArenaPoolAndAllocator> pool_and_allocator_;
 
   HGraph* graph_;
-
   HBasicBlock* entry_block_;
   HBasicBlock* return_block_;
   HBasicBlock* exit_block_;
@@ -280,6 +266,8 @@
   std::vector<HInstruction*> parameters_;
 };
 
+class OptimizingUnitTest : public CommonArtTest, public OptimizingUnitTestHelper {};
+
 // Naive string diff data type.
 typedef std::list<std::pair<std::string, std::string>> diff_t;
 
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index 1d8d1a6..4ff293c 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -226,6 +226,15 @@
     }
 
     switch (invoke->GetIntrinsic()) {
+      case Intrinsics::kMathAbsDouble:
+      case Intrinsics::kMathAbsFloat:
+      case Intrinsics::kMathMaxDoubleDouble:
+      case Intrinsics::kMathMaxFloatFloat:
+      case Intrinsics::kMathMinDoubleDouble:
+      case Intrinsics::kMathMinFloatFloat:
+        LOG(FATAL) << "Unreachable min/max/abs: intrinsics should have been lowered "
+                      "to IR nodes by instruction simplifier";
+        UNREACHABLE();
       case Intrinsics::kIntegerValueOf:
         // This intrinsic can be call free if it loads the address of the boot image object.
         // If we're compiling PIC, we need the address base for loading from .data.bimg.rel.ro.
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 4929e0a..8576943 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -22,7 +22,7 @@
 #include "base/scoped_arena_containers.h"
 #include "base/enums.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "handle_scope-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
@@ -40,54 +40,15 @@
   }
 }
 
-static inline ReferenceTypeInfo::TypeHandle GetRootHandle(VariableSizedHandleScope* handles,
-                                                          ClassRoot class_root,
-                                                          ReferenceTypeInfo::TypeHandle* cache) {
-  if (!ReferenceTypeInfo::IsValidHandle(*cache)) {
-    // Mutator lock is required for NewHandle.
-    ScopedObjectAccess soa(Thread::Current());
-    *cache = handles->NewHandle(GetClassRoot(class_root));
-  }
-  return *cache;
-}
-
-ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() {
-  return GetRootHandle(handles_, ClassRoot::kJavaLangObject, &object_class_handle_);
-}
-
-ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetClassClassHandle() {
-  return GetRootHandle(handles_, ClassRoot::kJavaLangClass, &class_class_handle_);
-}
-
-ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodHandleClassHandle() {
-  return GetRootHandle(handles_,
-                       ClassRoot::kJavaLangInvokeMethodHandleImpl,
-                       &method_handle_class_handle_);
-}
-
-ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodTypeClassHandle() {
-  return GetRootHandle(handles_, ClassRoot::kJavaLangInvokeMethodType, &method_type_class_handle_);
-}
-
-ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetStringClassHandle() {
-  return GetRootHandle(handles_, ClassRoot::kJavaLangString, &string_class_handle_);
-}
-
-ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetThrowableClassHandle() {
-  return GetRootHandle(handles_, ClassRoot::kJavaLangThrowable, &throwable_class_handle_);
-}
-
 class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
  public:
   RTPVisitor(HGraph* graph,
              Handle<mirror::ClassLoader> class_loader,
              Handle<mirror::DexCache> hint_dex_cache,
-             HandleCache* handle_cache,
              bool is_first_run)
     : HGraphDelegateVisitor(graph),
       class_loader_(class_loader),
       hint_dex_cache_(hint_dex_cache),
-      handle_cache_(handle_cache),
       allocator_(graph->GetArenaStack()),
       worklist_(allocator_.Adapter(kArenaAllocReferenceTypePropagation)),
       is_first_run_(is_first_run) {
@@ -138,11 +99,14 @@
   void AddToWorklist(HInstruction* instruction);
   void AddDependentInstructionsToWorklist(HInstruction* instruction);
 
+  HandleCache* GetHandleCache() {
+    return GetGraph()->GetHandleCache();
+  }
+
   static constexpr size_t kDefaultWorklistSize = 8;
 
   Handle<mirror::ClassLoader> class_loader_;
   Handle<mirror::DexCache> hint_dex_cache_;
-  HandleCache* const handle_cache_;
 
   // Use local allocator for allocating memory.
   ScopedArenaAllocator allocator_;
@@ -153,19 +117,17 @@
 ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
                                                    Handle<mirror::ClassLoader> class_loader,
                                                    Handle<mirror::DexCache> hint_dex_cache,
-                                                   VariableSizedHandleScope* handles,
                                                    bool is_first_run,
                                                    const char* name)
     : HOptimization(graph, name),
       class_loader_(class_loader),
       hint_dex_cache_(hint_dex_cache),
-      handle_cache_(handles),
       is_first_run_(is_first_run) {
 }
 
 void ReferenceTypePropagation::ValidateTypes() {
-  // TODO: move this to the graph checker.
-  if (kIsDebugBuild) {
+  // TODO: move this to the graph checker. Note: There may be no Thread for gtests.
+  if (kIsDebugBuild && Thread::Current() != nullptr) {
     ScopedObjectAccess soa(Thread::Current());
     for (HBasicBlock* block : graph_->GetReversePostOrder()) {
       for (HInstructionIterator iti(block->GetInstructions()); !iti.Done(); iti.Advance()) {
@@ -200,7 +162,6 @@
   RTPVisitor visitor(graph_,
                      class_loader_,
                      hint_dex_cache_,
-                     &handle_cache_,
                      is_first_run_);
   instruction->Accept(&visitor);
 }
@@ -360,7 +321,7 @@
 }
 
 bool ReferenceTypePropagation::Run() {
-  RTPVisitor visitor(graph_, class_loader_, hint_dex_cache_, &handle_cache_, is_first_run_);
+  RTPVisitor visitor(graph_, class_loader_, hint_dex_cache_, is_first_run_);
 
   // To properly propagate type info we need to visit in the dominator-based order.
   // Reverse post order guarantees a node's dominators are visited first.
@@ -426,8 +387,8 @@
       ? ifInstruction->IfTrueSuccessor()
       : ifInstruction->IfFalseSuccessor();
 
-  ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
-      handle_cache_->GetObjectClassHandle(), /* is_exact= */ false);
+  ReferenceTypeInfo object_rti =
+      ReferenceTypeInfo::Create(GetHandleCache()->GetObjectClassHandle(), /* is_exact= */ false);
 
   BoundTypeIn(obj, notNullBlock, /* start_instruction= */ nullptr, object_rti);
 }
@@ -571,13 +532,13 @@
           << "Expected String.<init>: " << method->PrettyMethod();
     }
     instr->SetReferenceTypeInfo(
-        ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact= */ true));
+        ReferenceTypeInfo::Create(GetHandleCache()->GetStringClassHandle(), /* is_exact= */ true));
   } else if (IsAdmissible(klass)) {
-    ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass);
+    ReferenceTypeInfo::TypeHandle handle = GetHandleCache()->NewHandle(klass);
     is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes();
     instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
   } else {
-    instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
+    instr->SetReferenceTypeInfo(GetGraph()->GetInexactObjectRti());
   }
 }
 
@@ -647,7 +608,7 @@
     HUnresolvedInstanceFieldGet* instr) {
   // TODO: Use descriptor to get the actual type.
   if (instr->GetFieldType() == DataType::Type::kReference) {
-    instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
+    instr->SetReferenceTypeInfo(GetGraph()->GetInexactObjectRti());
   }
 }
 
@@ -655,7 +616,7 @@
     HUnresolvedStaticFieldGet* instr) {
   // TODO: Use descriptor to get the actual type.
   if (instr->GetFieldType() == DataType::Type::kReference) {
-    instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
+    instr->SetReferenceTypeInfo(GetGraph()->GetInexactObjectRti());
   }
 }
 
@@ -665,7 +626,7 @@
     instr->SetValidLoadedClassRTI();
   }
   instr->SetReferenceTypeInfo(
-      ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact= */ true));
+      ReferenceTypeInfo::Create(GetHandleCache()->GetClassClassHandle(), /* is_exact= */ true));
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) {
@@ -681,18 +642,17 @@
 
 void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodHandle(HLoadMethodHandle* instr) {
   instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
-      handle_cache_->GetMethodHandleClassHandle(),
-      /* is_exact= */ true));
+      GetHandleCache()->GetMethodHandleClassHandle(), /* is_exact= */ true));
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodType(HLoadMethodType* instr) {
-  instr->SetReferenceTypeInfo(
-      ReferenceTypeInfo::Create(handle_cache_->GetMethodTypeClassHandle(), /* is_exact= */ true));
+  instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
+      GetHandleCache()->GetMethodTypeClassHandle(), /* is_exact= */ true));
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitLoadString(HLoadString* instr) {
   instr->SetReferenceTypeInfo(
-      ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact= */ true));
+      ReferenceTypeInfo::Create(GetHandleCache()->GetStringClassHandle(), /* is_exact= */ true));
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitLoadException(HLoadException* instr) {
@@ -705,8 +665,8 @@
                             catch_info->GetCatchDexFile(),
                             /* is_exact= */ false);
   } else {
-    instr->SetReferenceTypeInfo(
-        ReferenceTypeInfo::Create(handle_cache_->GetThrowableClassHandle(), /* is_exact= */ false));
+    instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
+        GetHandleCache()->GetThrowableClassHandle(), /* is_exact= */ false));
   }
 }
 
@@ -805,14 +765,13 @@
 }
 
 void ReferenceTypePropagation::FixUpInstructionType(HInstruction* instruction,
-                                                    VariableSizedHandleScope* handle_scope) {
+                                                    HandleCache* handle_cache) {
   if (instruction->IsSelect()) {
     ScopedObjectAccess soa(Thread::Current());
-    HandleCache handle_cache(handle_scope);
     HSelect* select = instruction->AsSelect();
     ReferenceTypeInfo false_rti = select->GetFalseValue()->GetReferenceTypeInfo();
     ReferenceTypeInfo true_rti = select->GetTrueValue()->GetReferenceTypeInfo();
-    select->SetReferenceTypeInfo(MergeTypes(false_rti, true_rti, &handle_cache));
+    select->SetReferenceTypeInfo(MergeTypes(false_rti, true_rti, handle_cache));
   } else {
     LOG(FATAL) << "Invalid instruction in FixUpInstructionType";
   }
@@ -873,12 +832,12 @@
   Handle<mirror::Class> handle = parent_rti.GetTypeHandle();
   if (handle->IsObjectArrayClass() && IsAdmissible(handle->GetComponentType())) {
     ReferenceTypeInfo::TypeHandle component_handle =
-        handle_cache_->NewHandle(handle->GetComponentType());
+        GetHandleCache()->NewHandle(handle->GetComponentType());
     bool is_exact = component_handle->CannotBeAssignedFromOtherTypes();
     instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(component_handle, is_exact));
   } else {
     // We don't know what the parent actually is, so we fallback to object.
-    instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
+    instr->SetReferenceTypeInfo(GetGraph()->GetInexactObjectRti());
   }
 }
 
@@ -981,7 +940,7 @@
     if (inputs[i]->IsNullConstant()) {
       continue;
     }
-    new_rti = MergeTypes(new_rti, inputs[i]->GetReferenceTypeInfo(), handle_cache_);
+    new_rti = MergeTypes(new_rti, inputs[i]->GetReferenceTypeInfo(), GetHandleCache());
     if (new_rti.IsValid() && new_rti.IsObjectClass()) {
       if (!new_rti.IsExact()) {
         break;
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 7c6a0484..344dea8 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -33,7 +33,6 @@
   ReferenceTypePropagation(HGraph* graph,
                            Handle<mirror::ClassLoader> class_loader,
                            Handle<mirror::DexCache> hint_dex_cache,
-                           VariableSizedHandleScope* handles,
                            bool is_first_run,
                            const char* name = kReferenceTypePropagationPassName);
 
@@ -45,9 +44,11 @@
   // 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.
   static bool IsAdmissible(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
-    return klass != nullptr &&
-           klass->IsResolved() &&
-           (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType()));
+    while (klass != nullptr && klass->IsArrayClass()) {
+      DCHECK(klass->IsResolved());
+      klass = klass->GetComponentType();
+    }
+    return klass != nullptr && klass->IsResolved();
   }
 
   static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation";
@@ -55,42 +56,9 @@
   // Fix the reference type for an instruction whose inputs have changed.
   // For a select instruction, the reference types of the inputs are merged
   // and the resulting reference type is set on the select instruction.
-  static void FixUpInstructionType(HInstruction* instruction,
-                                   VariableSizedHandleScope* handle_scope);
+  static void FixUpInstructionType(HInstruction* instruction, HandleCache* handle_cache);
 
  private:
-  class HandleCache {
-   public:
-    explicit HandleCache(VariableSizedHandleScope* handles) : handles_(handles) { }
-
-    template <typename T>
-    MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_) {
-      return handles_->NewHandle(object);
-    }
-
-    template <typename T>
-    MutableHandle<T> NewHandle(ObjPtr<T> object) REQUIRES_SHARED(Locks::mutator_lock_) {
-      return handles_->NewHandle(object);
-    }
-
-    ReferenceTypeInfo::TypeHandle GetObjectClassHandle();
-    ReferenceTypeInfo::TypeHandle GetClassClassHandle();
-    ReferenceTypeInfo::TypeHandle GetMethodHandleClassHandle();
-    ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle();
-    ReferenceTypeInfo::TypeHandle GetStringClassHandle();
-    ReferenceTypeInfo::TypeHandle GetThrowableClassHandle();
-
-   private:
-    VariableSizedHandleScope* handles_;
-
-    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_;
-  };
-
   class RTPVisitor;
 
   static ReferenceTypeInfo MergeTypes(const ReferenceTypeInfo& a,
@@ -106,7 +74,6 @@
   // graph_->GetDexFile(). Since we may look up also in other dex files, it's used only
   // as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache().
   Handle<mirror::DexCache> hint_dex_cache_;
-  HandleCache handle_cache_;
 
   // Whether this reference type propagation is the first run we are doing.
   const bool is_first_run_;
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index 028b6d3b7..a0d6609 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -28,18 +28,17 @@
  * Fixture class for unit testing the ReferenceTypePropagation phase. Used to verify the
  * functionality of methods and situations that are hard to set up with checker tests.
  */
-class ReferenceTypePropagationTest : public OptimizingUnitTest {
+class ReferenceTypePropagationTest : public CommonCompilerTest, public OptimizingUnitTestHelper {
  public:
-  ReferenceTypePropagationTest() : graph_(CreateGraph()), propagation_(nullptr) { }
+  ReferenceTypePropagationTest() : graph_(nullptr), propagation_(nullptr) { }
 
   ~ReferenceTypePropagationTest() { }
 
   void SetupPropagation(VariableSizedHandleScope* handles) {
-    graph_->InitializeInexactObjectRTI(handles);
+    graph_ = CreateGraph(handles);
     propagation_ = new (GetAllocator()) ReferenceTypePropagation(graph_,
                                                                  Handle<mirror::ClassLoader>(),
                                                                  Handle<mirror::DexCache>(),
-                                                                 handles,
                                                                  true,
                                                                  "test_prop");
   }
@@ -47,7 +46,7 @@
   // Relay method to merge type in reference type propagation.
   ReferenceTypeInfo MergeTypes(const ReferenceTypeInfo& a,
                                const ReferenceTypeInfo& b) REQUIRES_SHARED(Locks::mutator_lock_) {
-    return propagation_->MergeTypes(a, b, &propagation_->handle_cache_);
+    return propagation_->MergeTypes(a, b, graph_->GetHandleCache());
   }
 
   // Helper method to construct an invalid type.
@@ -57,12 +56,12 @@
 
   // Helper method to construct the Object type.
   ReferenceTypeInfo ObjectType(bool is_exact = true) REQUIRES_SHARED(Locks::mutator_lock_) {
-    return ReferenceTypeInfo::Create(propagation_->handle_cache_.GetObjectClassHandle(), is_exact);
+    return ReferenceTypeInfo::Create(graph_->GetHandleCache()->GetObjectClassHandle(), is_exact);
   }
 
   // Helper method to construct the String type.
   ReferenceTypeInfo StringType(bool is_exact = true) REQUIRES_SHARED(Locks::mutator_lock_) {
-    return ReferenceTypeInfo::Create(propagation_->handle_cache_.GetStringClassHandle(), is_exact);
+    return ReferenceTypeInfo::Create(graph_->GetHandleCache()->GetStringClassHandle(), is_exact);
   }
 
   // General building fields.
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 79eb082..d1db40b 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -41,9 +41,9 @@
 class RegisterAllocatorTest : public OptimizingUnitTest {
  protected:
   void SetUp() override {
-    // This test is using the x86 ISA.
-    OverrideInstructionSetFeatures(InstructionSet::kX86, "default");
     OptimizingUnitTest::SetUp();
+    // This test is using the x86 ISA.
+    compiler_options_ = CommonCompilerTest::CreateCompilerOptions(InstructionSet::kX86, "default");
   }
 
   // These functions need to access private variables of LocationSummary, so we declare it
@@ -74,6 +74,8 @@
                                                 /* processing_core_registers= */ true,
                                                 /* log_fatal_on_failure= */ false);
   }
+
+  std::unique_ptr<CompilerOptions> compiler_options_;
 };
 
 // This macro should include all register allocation strategies that should be tested.
diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc
index 7835b1d..b5ec93e 100644
--- a/compiler/optimizing/scheduler_test.cc
+++ b/compiler/optimizing/scheduler_test.cc
@@ -188,9 +188,10 @@
       HInstructionScheduling scheduling(graph, target_config.GetInstructionSet());
       scheduling.Run(/*only_optimize_loop_blocks*/ false, /*schedule_randomly*/ true);
 
-      OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
+      std::unique_ptr<CompilerOptions> compiler_options =
+          CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
       RunCode(target_config,
-              *compiler_options_,
+              *compiler_options,
               graph,
               [](HGraph* graph_arg) { RemoveSuspendChecks(graph_arg); },
               has_result, expected);
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index dcc7f77..5405382 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -24,11 +24,9 @@
 static constexpr size_t kMaxInstructionsInBranch = 1u;
 
 HSelectGenerator::HSelectGenerator(HGraph* graph,
-                                   VariableSizedHandleScope* handles,
                                    OptimizingCompilerStats* stats,
                                    const char* name)
-    : HOptimization(graph, name, stats),
-      handle_scope_(handles) {
+    : HOptimization(graph, name, stats) {
 }
 
 // Returns true if `block` has only one predecessor, ends with a Goto
@@ -163,7 +161,7 @@
     if (both_successors_return) {
       if (true_value->GetType() == DataType::Type::kReference) {
         DCHECK(false_value->GetType() == DataType::Type::kReference);
-        ReferenceTypePropagation::FixUpInstructionType(select, handle_scope_);
+        ReferenceTypePropagation::FixUpInstructionType(select, graph_->GetHandleCache());
       }
     } else if (phi->GetType() == DataType::Type::kReference) {
       select->SetReferenceTypeInfo(phi->GetReferenceTypeInfo());
diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h
index 2889166..30ac8a8 100644
--- a/compiler/optimizing/select_generator.h
+++ b/compiler/optimizing/select_generator.h
@@ -64,7 +64,6 @@
 class HSelectGenerator : public HOptimization {
  public:
   HSelectGenerator(HGraph* graph,
-                   VariableSizedHandleScope* handles,
                    OptimizingCompilerStats* stats,
                    const char* name = kSelectGeneratorPassName);
 
@@ -73,7 +72,6 @@
   static constexpr const char* kSelectGeneratorPassName = "select_generator";
 
  private:
-  VariableSizedHandleScope* handle_scope_;
   DISALLOW_COPY_AND_ASSIGN(HSelectGenerator);
 };
 
diff --git a/compiler/optimizing/select_generator_test.cc b/compiler/optimizing/select_generator_test.cc
index 6e68c6c..b18d41a 100644
--- a/compiler/optimizing/select_generator_test.cc
+++ b/compiler/optimizing/select_generator_test.cc
@@ -24,24 +24,20 @@
 
 namespace art {
 
-class SelectGeneratorTest : public ImprovedOptimizingUnitTest {
- private:
-  void CreateParameters() override {
-    parameters_.push_back(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
-                                                               dex::TypeIndex(0),
-                                                               0,
-                                                               DataType::Type::kInt32));
+class SelectGeneratorTest : public OptimizingUnitTest {
+ protected:
+  void InitGraphAndParameters() {
+    InitGraph();
+    AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+                                                      dex::TypeIndex(0),
+                                                      0,
+                                                      DataType::Type::kInt32));
   }
 
- public:
   void ConstructBasicGraphForSelect(HInstruction* instr) {
-    HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_);
-    HBasicBlock* then_block = new (GetAllocator()) HBasicBlock(graph_);
-    HBasicBlock* else_block = new (GetAllocator()) HBasicBlock(graph_);
-
-    graph_->AddBlock(if_block);
-    graph_->AddBlock(then_block);
-    graph_->AddBlock(else_block);
+    HBasicBlock* if_block = AddNewBlock();
+    HBasicBlock* then_block = AddNewBlock();
+    HBasicBlock* else_block = AddNewBlock();
 
     entry_block_->ReplaceSuccessor(return_block_, if_block);
 
@@ -82,7 +78,7 @@
 
 // HDivZeroCheck might throw and should not be hoisted from the conditional to an unconditional.
 TEST_F(SelectGeneratorTest, testZeroCheck) {
-  InitGraph();
+  InitGraphAndParameters();
   HDivZeroCheck* instr = new (GetAllocator()) HDivZeroCheck(parameters_[0], 0);
   ConstructBasicGraphForSelect(instr);
 
@@ -95,7 +91,7 @@
 
 // Test that SelectGenerator succeeds with HAdd.
 TEST_F(SelectGeneratorTest, testAdd) {
-  InitGraph();
+  InitGraphAndParameters();
   HAdd* instr = new (GetAllocator()) HAdd(DataType::Type::kInt32,
                                           parameters_[0],
                                           parameters_[0], 0);
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index b8471e3..1539421 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -101,11 +101,11 @@
       method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBssEntry;
     }
     code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
-  } else if (Runtime::Current()->UseJitCompilation()) {
+  } else if (compiler_options.IsJitCompiler()) {
     ScopedObjectAccess soa(Thread::Current());
     if (Runtime::Current()->GetJit()->CanEncodeMethod(
             callee,
-            codegen->GetGraph()->IsCompilingForSharedJitCode())) {
+            compiler_options.IsJitCompilerForSharedCode())) {
       method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kJitDirectAddress;
       method_load_data = reinterpret_cast<uintptr_t>(callee);
       code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
@@ -165,7 +165,7 @@
     const CompilerOptions& compiler_options = codegen->GetCompilerOptions();
     if (compiler_options.IsBootImage() || compiler_options.IsBootImageExtension()) {
       // Compiling boot image or boot image extension. Check if the class is a boot image class.
-      DCHECK(!runtime->UseJitCompilation());
+      DCHECK(!compiler_options.IsJitCompiler());
       if (!compiler_options.GetCompilePic()) {
         // Test configuration, do not sharpen.
         desired_load_kind = HLoadClass::LoadKind::kRuntimeCall;
@@ -184,14 +184,14 @@
     } else {
       is_in_boot_image = (klass != nullptr) &&
           runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get());
-      if (runtime->UseJitCompilation()) {
+      if (compiler_options.IsJitCompiler()) {
         DCHECK(!compiler_options.GetCompilePic());
         if (is_in_boot_image) {
           desired_load_kind = HLoadClass::LoadKind::kJitBootImageAddress;
         } else if (klass != nullptr) {
           if (runtime->GetJit()->CanEncodeClass(
                   klass.Get(),
-                  codegen->GetGraph()->IsCompilingForSharedJitCode())) {
+                  compiler_options.IsJitCompilerForSharedCode())) {
             desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
           } else {
             // Shared JIT code cannot encode a literal that the GC can move.
@@ -239,7 +239,8 @@
   DCHECK(!klass->IsProxyClass());
   DCHECK(!klass->IsArrayClass());
 
-  if (Runtime::Current()->UseJitCompilation()) {
+  const CompilerOptions& compiler_options = codegen->GetCompilerOptions();
+  if (compiler_options.IsJitCompiler()) {
     // If we're JITting, try to assign a type check bitstring (fall through).
   } else if (codegen->GetCompilerOptions().IsBootImage()) {
     const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex());
@@ -259,8 +260,8 @@
   if ((false) &&  // FIXME: Inliner does not respect CompilerDriver::ShouldCompileMethod()
                   // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569
       kIsDebugBuild &&
-      codegen->GetCompilerOptions().IsBootImage() &&
-      codegen->GetCompilerOptions().IsForceDeterminism()) {
+      compiler_options.IsBootImage() &&
+      compiler_options.IsForceDeterminism()) {
     SubtypeCheckInfo::State old_state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass);
     CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed)
         << klass->PrettyDescriptor() << "/" << old_state
@@ -325,7 +326,7 @@
     if (compiler_options.IsBootImage() || compiler_options.IsBootImageExtension()) {
       // Compiling boot image or boot image extension. Resolve the string and allocate it
       // if needed, to ensure the string will be added to the boot image.
-      DCHECK(!runtime->UseJitCompilation());
+      DCHECK(!compiler_options.IsJitCompiler());
       if (compiler_options.GetCompilePic()) {
         if (compiler_options.IsForceDeterminism()) {
           // Strings for methods we're compiling should be pre-resolved but Strings in inlined
@@ -354,7 +355,7 @@
         // Test configuration, do not sharpen.
         desired_load_kind = HLoadString::LoadKind::kRuntimeCall;
       }
-    } else if (runtime->UseJitCompilation()) {
+    } else if (compiler_options.IsJitCompiler()) {
       DCHECK(!codegen->GetCompilerOptions().GetCompilePic());
       string = class_linker->LookupString(string_index, dex_cache.Get());
       if (string != nullptr) {
@@ -363,7 +364,7 @@
           desired_load_kind = HLoadString::LoadKind::kJitBootImageAddress;
         } else if (runtime->GetJit()->CanEncodeString(
                   string,
-                  codegen->GetGraph()->IsCompilingForSharedJitCode())) {
+                  compiler_options.IsJitCompilerForSharedCode())) {
           desired_load_kind = HLoadString::LoadKind::kJitTableAddress;
         } else {
           // Shared JIT code cannot encode a literal that the GC can move.
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index a5e8ff6..67ee83c 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -540,7 +540,6 @@
   ReferenceTypePropagation(graph_,
                            class_loader_,
                            dex_cache_,
-                           handles_,
                            /* is_first_run= */ true).Run();
 
   // HInstructionBuilder duplicated ArrayGet instructions with ambiguous type
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index bb892c9..a7d4e0e 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -51,19 +51,16 @@
   SsaBuilder(HGraph* graph,
              Handle<mirror::ClassLoader> class_loader,
              Handle<mirror::DexCache> dex_cache,
-             VariableSizedHandleScope* handles,
              ScopedArenaAllocator* local_allocator)
       : graph_(graph),
         class_loader_(class_loader),
         dex_cache_(dex_cache),
-        handles_(handles),
         agets_fixed_(false),
         local_allocator_(local_allocator),
         ambiguous_agets_(local_allocator->Adapter(kArenaAllocGraphBuilder)),
         ambiguous_asets_(local_allocator->Adapter(kArenaAllocGraphBuilder)),
         uninitialized_strings_(local_allocator->Adapter(kArenaAllocGraphBuilder)),
         uninitialized_string_phis_(local_allocator->Adapter(kArenaAllocGraphBuilder)) {
-    graph_->InitializeInexactObjectRTI(handles);
   }
 
   GraphAnalysisResult BuildSsa();
@@ -129,7 +126,6 @@
   HGraph* const graph_;
   Handle<mirror::ClassLoader> class_loader_;
   Handle<mirror::DexCache> dex_cache_;
-  VariableSizedHandleScope* const handles_;
 
   // True if types of ambiguous ArrayGets have been resolved.
   bool agets_fixed_;
diff --git a/compiler/optimizing/ssa_liveness_analysis_test.cc b/compiler/optimizing/ssa_liveness_analysis_test.cc
index 352c44f..a477893 100644
--- a/compiler/optimizing/ssa_liveness_analysis_test.cc
+++ b/compiler/optimizing/ssa_liveness_analysis_test.cc
@@ -32,8 +32,9 @@
   void SetUp() override {
     OptimizingUnitTest::SetUp();
     graph_ = CreateGraph();
+    compiler_options_ = CommonCompilerTest::CreateCompilerOptions(kRuntimeISA, "default");
     codegen_ = CodeGenerator::Create(graph_, *compiler_options_);
-    CHECK(codegen_ != nullptr) << instruction_set_ << " is not a supported target architecture.";
+    CHECK(codegen_ != nullptr);
     // Create entry block.
     entry_ = new (GetAllocator()) HBasicBlock(graph_);
     graph_->AddBlock(entry_);
@@ -50,6 +51,7 @@
   }
 
   HGraph* graph_;
+  std::unique_ptr<CompilerOptions> compiler_options_;
   std::unique_ptr<CodeGenerator> codegen_;
   HBasicBlock* entry_;
 };
diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc
index 9f7a316..b968491 100644
--- a/compiler/optimizing/superblock_cloner.cc
+++ b/compiler/optimizing/superblock_cloner.cc
@@ -20,7 +20,7 @@
 #include "induction_var_range.h"
 #include "graph_checker.h"
 
-#include <iostream>
+#include <sstream>
 
 namespace art {
 
@@ -227,6 +227,40 @@
   }
 }
 
+bool SuperblockCloner::IsRemapInfoForVersioning() const {
+  return remap_incoming_->empty() &&
+         remap_orig_internal_->empty() &&
+         remap_copy_internal_->empty();
+}
+
+void SuperblockCloner::CopyIncomingEdgesForVersioning() {
+  for (uint32_t orig_block_id : orig_bb_set_.Indexes()) {
+    HBasicBlock* orig_block = GetBlockById(orig_block_id);
+    size_t incoming_edge_count = 0;
+    for (HBasicBlock* orig_pred : orig_block->GetPredecessors()) {
+      uint32_t orig_pred_id = orig_pred->GetBlockId();
+      if (IsInOrigBBSet(orig_pred_id)) {
+        continue;
+      }
+
+      HBasicBlock* copy_block = GetBlockCopy(orig_block);
+      // This corresponds to the requirement on the order of predecessors: all the incoming
+      // edges must be seen before the internal ones. This is always true for natural loops.
+      // TODO: remove this requirement.
+      DCHECK_EQ(orig_block->GetPredecessorIndexOf(orig_pred), incoming_edge_count);
+      for (HInstructionIterator it(orig_block->GetPhis()); !it.Done(); it.Advance()) {
+        HPhi* orig_phi = it.Current()->AsPhi();
+        HPhi* copy_phi = GetInstrCopy(orig_phi)->AsPhi();
+        HInstruction* orig_phi_input = orig_phi->InputAt(incoming_edge_count);
+        // Add the corresponding input of the original phi to the copy one.
+        copy_phi->AddInput(orig_phi_input);
+      }
+      copy_block->AddPredecessor(orig_pred);
+      incoming_edge_count++;
+    }
+  }
+}
+
 //
 // Local versions of CF calculation/adjustment routines.
 //
@@ -452,6 +486,12 @@
 }
 
 void SuperblockCloner::RemapEdgesSuccessors() {
+  // By this stage all the blocks have been copied, copy phis - created with no inputs;
+  // no copy edges have been created so far.
+  if (IsRemapInfoForVersioning()) {
+    CopyIncomingEdgesForVersioning();
+  }
+
   // Redirect incoming edges.
   for (HEdge e : *remap_incoming_) {
     HBasicBlock* orig_block = GetBlockById(e.GetFrom());
@@ -646,25 +686,26 @@
     if (bb == nullptr) {
       continue;
     }
-    std::cout << bb->GetBlockId();
-    std::cout << " <- ";
+    std::ostringstream oss;
+    oss << bb->GetBlockId();
+    oss << " <- ";
     for (HBasicBlock* pred : bb->GetPredecessors()) {
-      std::cout << pred->GetBlockId() << " ";
+      oss << pred->GetBlockId() << " ";
     }
-    std::cout << " -> ";
+    oss << " -> ";
     for (HBasicBlock* succ : bb->GetSuccessors()) {
-      std::cout << succ->GetBlockId()  << " ";
+      oss << succ->GetBlockId()  << " ";
     }
 
     if (bb->GetDominator()) {
-      std::cout << " dom " << bb->GetDominator()->GetBlockId();
+      oss << " dom " << bb->GetDominator()->GetBlockId();
     }
 
     if (bb->GetLoopInformation()) {
-      std::cout <<  "\tloop: " << bb->GetLoopInformation()->GetHeader()->GetBlockId();
+      oss <<  "\tloop: " << bb->GetLoopInformation()->GetHeader()->GetBlockId();
     }
 
-    std::cout << std::endl;
+    LOG(INFO) << oss.str();
   }
 }
 
@@ -747,35 +788,35 @@
     checker.Run();
     if (!checker.IsValid()) {
       for (const std::string& error : checker.GetErrors()) {
-        std::cout << error << std::endl;
+        LOG(ERROR) << error;
       }
-      LOG(FATAL) << "GraphChecker failed: superblock cloner\n";
+      LOG(FATAL) << "GraphChecker failed: superblock cloner";
     }
   }
 }
 
 void DumpBBSet(const ArenaBitVector* set) {
   for (uint32_t idx : set->Indexes()) {
-    std::cout << idx << "\n";
+    LOG(INFO) << idx;
   }
 }
 
 void SuperblockCloner::DumpInputSets() {
-  std::cout << "orig_bb_set:\n";
+  LOG(INFO) << "orig_bb_set:";
   for (uint32_t idx : orig_bb_set_.Indexes()) {
-    std::cout << idx << "\n";
+    LOG(INFO) << idx;
   }
-  std::cout << "remap_orig_internal:\n";
+  LOG(INFO) << "remap_orig_internal:";
   for (HEdge e : *remap_orig_internal_) {
-    std::cout << e << "\n";
+    LOG(INFO) << e;
   }
-  std::cout << "remap_copy_internal:\n";
+  LOG(INFO) << "remap_copy_internal:";
   for (auto e : *remap_copy_internal_) {
-    std::cout << e << "\n";
+    LOG(INFO) << e;
   }
-  std::cout << "remap_incoming:\n";
+  LOG(INFO) << "remap_incoming:";
   for (auto e : *remap_incoming_) {
-    std::cout << e << "\n";
+    LOG(INFO) << e;
   }
 }
 
@@ -837,8 +878,8 @@
   return true;
 }
 
+// Checks that loop unrolling/peeling/versioning is being conducted.
 bool SuperblockCloner::IsFastCase() const {
-  // Check that loop unrolling/loop peeling is being conducted.
   // Check that all the basic blocks belong to the same loop.
   bool flag = false;
   HLoopInformation* common_loop_info = nullptr;
@@ -854,11 +895,15 @@
     }
   }
 
-  // Check that orig_bb_set_ corresponds to loop peeling/unrolling.
+  // Check that orig_bb_set_ corresponds to loop peeling/unrolling/versioning.
   if (common_loop_info == nullptr || !orig_bb_set_.SameBitsSet(&common_loop_info->GetBlocks())) {
     return false;
   }
 
+  if (IsRemapInfoForVersioning()) {
+    return true;
+  }
+
   bool peeling_or_unrolling = false;
   HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
   HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
@@ -1012,8 +1057,7 @@
     HBasicBlock* copy_block = CloneBasicBlock(orig_block);
     bb_map_->Put(orig_block, copy_block);
     if (kSuperblockClonerLogging) {
-      std::cout << "new block :" << copy_block->GetBlockId() << ": " << orig_block->GetBlockId() <<
-                   std::endl;
+      LOG(INFO) << "new block :" << copy_block->GetBlockId() << ": " << orig_block->GetBlockId();
     }
   }
 }
@@ -1089,14 +1133,14 @@
   return current;
 }
 
-bool PeelUnrollHelper::IsLoopClonable(HLoopInformation* loop_info) {
-  PeelUnrollHelper helper(
+bool LoopClonerHelper::IsLoopClonable(HLoopInformation* loop_info) {
+  LoopClonerHelper helper(
       loop_info, /* bb_map= */ nullptr, /* hir_map= */ nullptr, /* induction_range= */ nullptr);
   return helper.IsLoopClonable();
 }
 
-HBasicBlock* PeelUnrollHelper::DoPeelUnrollImpl(bool to_unroll) {
-  // For now do peeling only for natural loops.
+HBasicBlock* LoopClonerHelper::DoLoopTransformationImpl(TransformationKind transformation) {
+  // For now do transformations only for natural loops.
   DCHECK(!loop_info_->IsIrreducible());
 
   HBasicBlock* loop_header = loop_info_->GetHeader();
@@ -1105,9 +1149,25 @@
   HGraph* graph = loop_header->GetGraph();
 
   if (kSuperblockClonerLogging) {
-    std::cout << "Method: " << graph->PrettyMethod() << std::endl;
-    std::cout << "Scalar loop " << (to_unroll ? "unrolling" : "peeling") <<
-                 " was applied to the loop <" << loop_header->GetBlockId() << ">." << std::endl;
+    LOG(INFO) << "Method: " << graph->PrettyMethod();
+    std::ostringstream oss;
+    oss << "Scalar loop ";
+    switch (transformation) {
+      case TransformationKind::kPeeling:
+        oss << "peeling";
+        break;
+      case TransformationKind::kUnrolling:
+        oss<< "unrolling";
+        break;
+      case TransformationKind::kVersioning:
+        oss << "versioning";
+        break;
+      default:
+        LOG(FATAL) << "Unreachable";
+        UNREACHABLE();
+    }
+    oss << " was applied to the loop <" << loop_header->GetBlockId() << ">.";
+    LOG(INFO) << oss.str();
   }
 
   ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool());
@@ -1116,11 +1176,14 @@
   HEdgeSet remap_copy_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
   HEdgeSet remap_incoming(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
 
-  CollectRemappingInfoForPeelUnroll(to_unroll,
-                                    loop_info_,
-                                    &remap_orig_internal,
-                                    &remap_copy_internal,
-                                    &remap_incoming);
+  // No remapping needed for loop versioning.
+  if (transformation != TransformationKind::kVersioning) {
+    CollectRemappingInfoForPeelUnroll(transformation == TransformationKind::kUnrolling,
+                                      loop_info_,
+                                      &remap_orig_internal,
+                                      &remap_copy_internal,
+                                      &remap_incoming);
+  }
 
   cloner_.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming);
   cloner_.Run();
@@ -1132,7 +1195,7 @@
   return loop_header;
 }
 
-PeelUnrollSimpleHelper::PeelUnrollSimpleHelper(HLoopInformation* info,
+LoopClonerSimpleHelper::LoopClonerSimpleHelper(HLoopInformation* info,
                                                InductionVarRange* induction_range)
   : bb_map_(std::less<HBasicBlock*>(),
             info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)),
diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h
index 5af1e4d8..1f6ee74 100644
--- a/compiler/optimizing/superblock_cloner.h
+++ b/compiler/optimizing/superblock_cloner.h
@@ -89,7 +89,8 @@
 // fine grain manipulation with IR; data flow and graph properties are resolved/adjusted
 // automatically. The clone transformation is defined by specifying a set of basic blocks to copy
 // and a set of rules how to treat edges, remap their successors. By using this approach such
-// optimizations as Branch Target Expansion, Loop Peeling, Loop Unrolling can be implemented.
+// optimizations as Branch Target Expansion, Loop Peeling, Loop Unrolling, Loop Versioning can be
+// implemented.
 //
 // The idea of the transformation is based on "Superblock cloning" technique described in the book
 // "Engineering a Compiler. Second Edition", Keith D. Cooper, Linda Torczon, Rice University
@@ -161,7 +162,7 @@
   //
   // TODO: formally describe the criteria.
   //
-  // Loop peeling and unrolling satisfy the criteria.
+  // Loop peeling, unrolling and versioning satisfy the criteria.
   bool IsFastCase() const;
 
   // Runs the copy algorithm according to the description.
@@ -297,6 +298,18 @@
   // Remaps copy internal edge to its origin, adjusts the phi inputs in orig_succ.
   void RemapCopyInternalEdge(HBasicBlock* orig_block, HBasicBlock* orig_succ);
 
+  // Checks whether the edges remapping info corresponds to the subgraph versioning case:
+  //  - none of the incoming edges are to be remapped (they are being duplicated).
+  //  - none of the internal edges are to be remapped.
+  bool IsRemapInfoForVersioning() const;
+
+  // Processes incoming edges for subgraph versioning case: for each incoming edge (X, Y) adds
+  // an edge (X, Y_1) where Y_1 = Copy(Y) and add corresponding phi input to copy phi.
+  //
+  // Note: such node X will now have two successors, its unconditional branch instruction
+  // will be invalid and should be adjusted to some conditional branch by the client code.
+  void CopyIncomingEdgesForVersioning();
+
   //
   // Local versions of control flow calculation/adjustment routines.
   //
@@ -362,19 +375,19 @@
   DISALLOW_COPY_AND_ASSIGN(SuperblockCloner);
 };
 
-// Helper class to perform loop peeling/unrolling.
+// Helper class to perform loop peeling/unrolling/versioning.
 //
 // This helper should be used when correspondence map between original and copied
 // basic blocks/instructions are demanded.
-class PeelUnrollHelper : public ValueObject {
+class LoopClonerHelper : public ValueObject {
  public:
-  PeelUnrollHelper(HLoopInformation* info,
+  LoopClonerHelper(HLoopInformation* info,
                    SuperblockCloner::HBasicBlockMap* bb_map,
                    SuperblockCloner::HInstructionMap* hir_map,
                    InductionVarRange* induction_range) :
       loop_info_(info),
       cloner_(info->GetHeader()->GetGraph(), &info->GetBlocks(), bb_map, hir_map, induction_range) {
-    // For now do peeling/unrolling only for natural loops.
+    // For now do transformations only for natural loops.
     DCHECK(!info->IsIrreducible());
   }
 
@@ -384,33 +397,121 @@
   // Returns whether the loop can be peeled/unrolled.
   bool IsLoopClonable() const { return cloner_.IsSubgraphClonable(); }
 
-  HBasicBlock* DoPeeling() { return DoPeelUnrollImpl(/* to_unroll= */ false); }
-  HBasicBlock* DoUnrolling() { return DoPeelUnrollImpl(/* to_unroll= */ true); }
+  // Perform loop peeling.
+  //
+  // Control flow of an example (ignoring critical edges splitting).
+  //
+  //       Before                    After
+  //
+  //         |B|                      |B|
+  //          |                        |
+  //          v                        v
+  //         |1|                      |1|
+  //          |                        |
+  //          v                        v
+  //         |2|<-\                  |2A|
+  //         / \  /                   / \
+  //        v   v/                   /   v
+  //       |4|  |3|                 /   |3A|
+  //        |                      /     /
+  //        v                     |     v
+  //       |E|                     \   |2|<-\
+  //                                \ / \   /
+  //                                 v   v /
+  //                                |4|  |3|
+  //                                 |
+  //                                 v
+  //                                |E|
+  HBasicBlock* DoPeeling() {
+    return DoLoopTransformationImpl(TransformationKind::kPeeling);
+  }
+
+  // Perform loop unrolling.
+  //
+  // Control flow of an example (ignoring critical edges splitting).
+  //
+  //       Before                    After
+  //
+  //         |B|                      |B|
+  //          |                        |
+  //          v                        v
+  //         |1|                      |1|
+  //          |                        |
+  //          v                        v
+  //         |2|<-\                   |2A|<-\
+  //         / \  /                   / \    \
+  //        v   v/                   /   v    \
+  //       |4|  |3|                 /   |3A|   |
+  //        |                      /     /    /
+  //        v                     |     v    /
+  //       |E|                     \   |2|  /
+  //                                \ / \  /
+  //                                 v   v/
+  //                                |4| |3|
+  //                                 |
+  //                                 v
+  //                                |E|
+  HBasicBlock* DoUnrolling() {
+    return DoLoopTransformationImpl(TransformationKind::kUnrolling);
+  }
+
+  // Perform loop versioning.
+  //
+  // Control flow of an example (ignoring critical edges splitting).
+  //
+  //       Before                    After
+  //
+  //         |B|                      |B|
+  //          |                        |
+  //          v                        v
+  //         |1|                      |1|_________
+  //          |                        |          |
+  //          v                        v          v
+  //         |2|<-\                   |2|<-\     |2A|<-\
+  //         / \  /                   / \  /     /  \  /
+  //        v   v/                   |   v/      |   v/
+  //        |   |3|                  |  |3|      | |3A|
+  //        |                        | __________|
+  //        |                        ||
+  //        v                        vv
+  //       |4|                       |4|
+  //        |                         |
+  //        v                         v
+  //       |E|                       |E|
+  HBasicBlock* DoVersioning() {
+    return DoLoopTransformationImpl(TransformationKind::kVersioning);
+  }
+
   HLoopInformation* GetRegionToBeAdjusted() const { return cloner_.GetRegionToBeAdjusted(); }
 
  protected:
-  // Applies loop peeling/unrolling for the loop specified by 'loop_info'.
-  //
-  // Depending on 'do_unroll' either unrolls loop by 2 or peels one iteration from it.
-  HBasicBlock* DoPeelUnrollImpl(bool to_unroll);
+  enum class TransformationKind {
+    kPeeling,
+    kUnrolling,
+    kVersioning,
+  };
+
+  // Applies a specific loop transformation to the loop.
+  HBasicBlock* DoLoopTransformationImpl(TransformationKind transformation);
 
  private:
   HLoopInformation* loop_info_;
   SuperblockCloner cloner_;
 
-  DISALLOW_COPY_AND_ASSIGN(PeelUnrollHelper);
+  DISALLOW_COPY_AND_ASSIGN(LoopClonerHelper);
 };
 
-// Helper class to perform loop peeling/unrolling.
+// Helper class to perform loop peeling/unrolling/versioning.
 //
 // This helper should be used when there is no need to get correspondence information between
 // original and copied basic blocks/instructions.
-class PeelUnrollSimpleHelper : public ValueObject {
+class LoopClonerSimpleHelper : public ValueObject {
  public:
-  PeelUnrollSimpleHelper(HLoopInformation* info, InductionVarRange* induction_range);
+  LoopClonerSimpleHelper(HLoopInformation* info, InductionVarRange* induction_range);
   bool IsLoopClonable() const { return helper_.IsLoopClonable(); }
   HBasicBlock* DoPeeling() { return helper_.DoPeeling(); }
   HBasicBlock* DoUnrolling() { return helper_.DoUnrolling(); }
+  HBasicBlock* DoVersioning() { return helper_.DoVersioning(); }
   HLoopInformation* GetRegionToBeAdjusted() const { return helper_.GetRegionToBeAdjusted(); }
 
   const SuperblockCloner::HBasicBlockMap* GetBasicBlockMap() const { return &bb_map_; }
@@ -419,9 +520,9 @@
  private:
   SuperblockCloner::HBasicBlockMap bb_map_;
   SuperblockCloner::HInstructionMap hir_map_;
-  PeelUnrollHelper helper_;
+  LoopClonerHelper helper_;
 
-  DISALLOW_COPY_AND_ASSIGN(PeelUnrollSimpleHelper);
+  DISALLOW_COPY_AND_ASSIGN(LoopClonerSimpleHelper);
 };
 
 // Collects edge remapping info for loop peeling/unrolling for the loop specified by loop info.
diff --git a/compiler/optimizing/superblock_cloner_test.cc b/compiler/optimizing/superblock_cloner_test.cc
index ddcf154..d8d68b7 100644
--- a/compiler/optimizing/superblock_cloner_test.cc
+++ b/compiler/optimizing/superblock_cloner_test.cc
@@ -30,27 +30,23 @@
 
 // This class provides methods and helpers for testing various cloning and copying routines:
 // individual instruction cloning and cloning of the more coarse-grain structures.
-class SuperblockClonerTest : public ImprovedOptimizingUnitTest {
- private:
-  void CreateParameters() override {
-    parameters_.push_back(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
-                                                               dex::TypeIndex(0),
-                                                               0,
-                                                               DataType::Type::kInt32));
+class SuperblockClonerTest : public OptimizingUnitTest {
+ protected:
+  void InitGraphAndParameters() {
+    InitGraph();
+    AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+                                                      dex::TypeIndex(0),
+                                                      0,
+                                                      DataType::Type::kInt32));
   }
 
- public:
   void CreateBasicLoopControlFlow(HBasicBlock* position,
                                   HBasicBlock* successor,
                                   /* out */ HBasicBlock** header_p,
                                   /* out */ HBasicBlock** body_p) {
-    HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_);
-    HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_);
-    HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_);
-
-    graph_->AddBlock(loop_preheader);
-    graph_->AddBlock(loop_header);
-    graph_->AddBlock(loop_body);
+    HBasicBlock* loop_preheader = AddNewBlock();
+    HBasicBlock* loop_header = AddNewBlock();
+    HBasicBlock* loop_body = AddNewBlock();
 
     position->ReplaceSuccessor(successor, loop_preheader);
 
@@ -121,7 +117,7 @@
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
 
-  InitGraph();
+  InitGraphAndParameters();
   CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
   graph_->BuildDominatorTree();
@@ -151,9 +147,9 @@
 TEST_F(SuperblockClonerTest, CloneBasicBlocks) {
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
-  ArenaAllocator* arena = graph_->GetAllocator();
+  ArenaAllocator* arena = GetAllocator();
 
-  InitGraph();
+  InitGraphAndParameters();
   CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
   graph_->BuildDominatorTree();
@@ -232,9 +228,9 @@
 TEST_F(SuperblockClonerTest, AdjustControlFlowInfo) {
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
-  ArenaAllocator* arena = graph_->GetAllocator();
+  ArenaAllocator* arena = GetAllocator();
 
-  InitGraph();
+  InitGraphAndParameters();
   CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
   graph_->BuildDominatorTree();
@@ -271,13 +267,12 @@
 TEST_F(SuperblockClonerTest, IsGraphConnected) {
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
-  ArenaAllocator* arena = graph_->GetAllocator();
+  ArenaAllocator* arena = GetAllocator();
 
-  InitGraph();
+  InitGraphAndParameters();
   CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
-  HBasicBlock* unreachable_block = new (GetAllocator()) HBasicBlock(graph_);
-  graph_->AddBlock(unreachable_block);
+  HBasicBlock* unreachable_block = AddNewBlock();
 
   HBasicBlockSet bb_set(
       arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
@@ -292,34 +287,12 @@
 
 // Tests SuperblockCloner for loop peeling case.
 //
-// Control Flow of the example (ignoring critical edges splitting).
-//
-//       Before                    After
-//
-//         |B|                      |B|
-//          |                        |
-//          v                        v
-//         |1|                      |1|
-//          |                        |
-//          v                        v
-//         |2|<-\              (6) |2A|
-//         / \  /                   / \
-//        v   v/                   /   v
-//       |4|  |3|                 /   |3A| (7)
-//        |                      /     /
-//        v                     |     v
-//       |E|                     \   |2|<-\
-//                                \ / \   /
-//                                 v   v /
-//                                |4|  |3|
-//                                 |
-//                                 v
-//                                |E|
+// See an ASCII graphics example near LoopClonerHelper::DoPeeling.
 TEST_F(SuperblockClonerTest, LoopPeeling) {
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
 
-  InitGraph();
+  InitGraphAndParameters();
   CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
   graph_->BuildDominatorTree();
@@ -331,7 +304,7 @@
       std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
 
   HLoopInformation* loop_info = header->GetLoopInformation();
-  PeelUnrollHelper helper(loop_info, &bb_map, &hir_map, /* induction_range= */ nullptr);
+  LoopClonerHelper helper(loop_info, &bb_map, &hir_map, /* induction_range= */ nullptr);
   EXPECT_TRUE(helper.IsLoopClonable());
   HBasicBlock* new_header = helper.DoPeeling();
   HLoopInformation* new_loop_info = new_header->GetLoopInformation();
@@ -351,34 +324,12 @@
 
 // Tests SuperblockCloner for loop unrolling case.
 //
-// Control Flow of the example (ignoring critical edges splitting).
-//
-//       Before                    After
-//
-//         |B|                      |B|
-//          |                        |
-//          v                        v
-//         |1|                      |1|
-//          |                        |
-//          v                        v
-//         |2|<-\               (6) |2A|<-\
-//         / \  /                   / \    \
-//        v   v/                   /   v    \
-//       |4|  |3|                 /(7)|3A|   |
-//        |                      /     /    /
-//        v                     |     v    /
-//       |E|                     \   |2|  /
-//                                \ / \  /
-//                                 v   v/
-//                                |4| |3|
-//                                 |
-//                                 v
-//                                |E|
+// See an ASCII graphics example near LoopClonerHelper::DoUnrolling.
 TEST_F(SuperblockClonerTest, LoopUnrolling) {
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
 
-  InitGraph();
+  InitGraphAndParameters();
   CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
   graph_->BuildDominatorTree();
@@ -390,7 +341,7 @@
       std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
 
   HLoopInformation* loop_info = header->GetLoopInformation();
-  PeelUnrollHelper helper(loop_info, &bb_map, &hir_map, /* induction_range= */ nullptr);
+  LoopClonerHelper helper(loop_info, &bb_map, &hir_map, /* induction_range= */ nullptr);
   EXPECT_TRUE(helper.IsLoopClonable());
   HBasicBlock* new_header = helper.DoUnrolling();
 
@@ -408,22 +359,69 @@
   EXPECT_EQ(loop_info->GetBackEdges()[0], bb_map.Get(loop_body));
 }
 
+// Tests SuperblockCloner for loop versioning case.
+//
+// See an ASCII graphics example near LoopClonerHelper::DoVersioning.
+TEST_F(SuperblockClonerTest, LoopVersioning) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+
+  InitGraphAndParameters();
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  graph_->BuildDominatorTree();
+  EXPECT_TRUE(CheckGraph());
+
+  HBasicBlockMap bb_map(
+      std::less<HBasicBlock*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+  HInstructionMap hir_map(
+      std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+
+  HLoopInformation* loop_info = header->GetLoopInformation();
+  HBasicBlock* original_preheader = loop_info->GetPreHeader();
+  LoopClonerHelper helper(loop_info, &bb_map, &hir_map, /* induction_range= */ nullptr);
+  EXPECT_TRUE(helper.IsLoopClonable());
+  HBasicBlock* new_header = helper.DoVersioning();
+  EXPECT_EQ(header, new_header);
+
+  EXPECT_TRUE(CheckGraph());
+
+  HBasicBlock* second_header = bb_map.Get(header);
+  HBasicBlock* second_body = bb_map.Get(loop_body);
+  HLoopInformation* second_loop_info = second_header->GetLoopInformation();
+
+  // Check loop body successors.
+  EXPECT_EQ(loop_body->GetSingleSuccessor(), header);
+  EXPECT_EQ(second_body->GetSingleSuccessor(), second_header);
+
+  // Check loop structure.
+  EXPECT_EQ(loop_info, header->GetLoopInformation());
+  EXPECT_EQ(loop_info->GetHeader(), header);
+  EXPECT_EQ(second_loop_info->GetHeader(), second_header);
+
+  EXPECT_EQ(loop_info->GetBackEdges().size(), 1u);
+  EXPECT_EQ(second_loop_info->GetBackEdges().size(), 1u);
+
+  EXPECT_EQ(loop_info->GetBackEdges()[0], loop_body);
+  EXPECT_EQ(second_loop_info->GetBackEdges()[0], second_body);
+
+  EXPECT_EQ(original_preheader->GetSuccessors().size(), 2u);
+}
+
 // Checks that loop unrolling works fine for a loop with multiple back edges. Tests that after
 // the transformation the loop has a single preheader.
 TEST_F(SuperblockClonerTest, LoopPeelingMultipleBackEdges) {
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
 
-  InitGraph();
+  InitGraphAndParameters();
   CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
 
   // Transform a basic loop to have multiple back edges.
   HBasicBlock* latch = header->GetSuccessors()[1];
-  HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_);
-  HBasicBlock* temp1 = new (GetAllocator()) HBasicBlock(graph_);
-  graph_->AddBlock(if_block);
-  graph_->AddBlock(temp1);
+  HBasicBlock* if_block = AddNewBlock();
+  HBasicBlock* temp1 = AddNewBlock();
   header->ReplaceSuccessor(latch, if_block);
   if_block->AddSuccessor(latch);
   if_block->AddSuccessor(temp1);
@@ -445,7 +443,7 @@
   EXPECT_TRUE(CheckGraph());
 
   HLoopInformation* loop_info = header->GetLoopInformation();
-  PeelUnrollSimpleHelper helper(loop_info, /* induction_range= */ nullptr);
+  LoopClonerSimpleHelper helper(loop_info, /* induction_range= */ nullptr);
   HBasicBlock* new_header = helper.DoPeeling();
   EXPECT_EQ(header, new_header);
 
@@ -469,7 +467,7 @@
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
 
-  InitGraph();
+  InitGraphAndParameters();
 
   // Create the following nested structure of loops
   //   Headers:  1    2 3
@@ -494,7 +492,7 @@
 
   // Check nested loops structure.
   CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header);
-  PeelUnrollSimpleHelper helper(loop1_header->GetLoopInformation(), /* induction_range= */ nullptr);
+  LoopClonerSimpleHelper helper(loop1_header->GetLoopInformation(), /* induction_range= */ nullptr);
   helper.DoPeeling();
   // Check that nested loops structure has not changed after the transformation.
   CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header);
@@ -516,7 +514,7 @@
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
 
-  InitGraph();
+  InitGraphAndParameters();
 
   // Create the following nested structure of loops
   //   Headers:  1 2 3        4
@@ -540,7 +538,7 @@
   graph_->BuildDominatorTree();
   EXPECT_TRUE(CheckGraph());
 
-  PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation(), /* induction_range= */ nullptr);
+  LoopClonerSimpleHelper helper(loop3_header->GetLoopInformation(), /* induction_range= */ nullptr);
   helper.DoPeeling();
   HLoopInformation* loop1 = loop1_header->GetLoopInformation();
   HLoopInformation* loop2 = loop2_header->GetLoopInformation();
@@ -573,7 +571,7 @@
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
 
-  InitGraph();
+  InitGraphAndParameters();
 
   // Create the following nested structure of loops then peel loop3.
   //   Headers:  1 2 3
@@ -592,8 +590,7 @@
   HBasicBlock* loop_body3 = loop_body;
 
   // Change the loop3 - insert an exit which leads to loop1.
-  HBasicBlock* loop3_extra_if_block = new (GetAllocator()) HBasicBlock(graph_);
-  graph_->AddBlock(loop3_extra_if_block);
+  HBasicBlock* loop3_extra_if_block = AddNewBlock();
   loop3_extra_if_block->AddInstruction(new (GetAllocator()) HIf(parameters_[0]));
 
   loop3_header->ReplaceSuccessor(loop_body3, loop3_extra_if_block);
@@ -606,7 +603,7 @@
   HBasicBlock* loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0];
   EXPECT_TRUE(loop1_header->GetLoopInformation()->Contains(*loop3_long_exit));
 
-  PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation(), /* induction_range= */ nullptr);
+  LoopClonerSimpleHelper helper(loop3_header->GetLoopInformation(), /* induction_range= */ nullptr);
   helper.DoPeeling();
 
   HLoopInformation* loop1 = loop1_header->GetLoopInformation();
@@ -626,9 +623,9 @@
 TEST_F(SuperblockClonerTest, FastCaseCheck) {
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
-  ArenaAllocator* arena = graph_->GetAllocator();
+  ArenaAllocator* arena = GetAllocator();
 
-  InitGraph();
+  InitGraphAndParameters();
   CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
   graph_->BuildDominatorTree();
@@ -683,7 +680,7 @@
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
 
-  InitGraph();
+  InitGraphAndParameters();
 
   // Create the following nested structure of loops
   //   Headers:  1 2 3      4      5
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 0e82704..7fd1cc2 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -55,24 +55,14 @@
         },
     },
 
-    target: {
-        android: {
-            static_libs: [
-                "libz",
-            ],
-        },
-        host: {
-            shared_libs: [
-                "libz",
-            ],
-        },
-    },
     generated_sources: ["art_dex2oat_operator_srcs"],
     shared_libs: [
         "libbase",
 
         // For SHA-1 checksumming of build ID
         "libcrypto",
+
+        "libz",
     ],
     export_include_dirs: ["."],
 }
@@ -168,6 +158,12 @@
     ],
     host_supported: true,
     export_include_dirs: ["include"],
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.release",
+        "com.android.art.debug",
+    ],
 }
 
 cc_defaults {
@@ -244,6 +240,7 @@
         "libartpalette",
         "libbase",
         "libsigchain",
+        "libz",
     ],
     static_libs: [
         "libart-dex2oat",
@@ -271,14 +268,8 @@
             lto: {
                 thin: true,
             },
-            static_libs: [
-                "libz",
-            ],
         },
         host: {
-            shared_libs: [
-                "libz",
-            ],
             // Override the prefer32 added by art_cc_binary when
             // HOST_PREFER_32_BIT is in use. Necessary because the logic in
             // Soong for setting ctx.Config().BuildOSTarget (used in
@@ -316,20 +307,13 @@
         "libartpalette",
         "libbase",
         "libsigchain",
+        "libz",
     ],
     static_libs: [
         "libartd-dex2oat",
     ],
     target: {
-        android: {
-            static_libs: [
-                "libz",
-            ],
-        },
         host: {
-            shared_libs: [
-                "libz",
-            ],
             // Override the prefer32 added by art_cc_binary when
             // HOST_PREFER_32_BIT is in use. Necessary because the logic in
             // Soong for setting ctx.Config().BuildOSTarget (used in
@@ -452,6 +436,7 @@
         "linker/oat_writer_test.cc",
         "verifier_deps_test.cc",
     ],
+    required: ["dex2oatd"],
 
     codegen: {
         arm: {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ee838da..025828a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -521,6 +521,9 @@
   UsageError("");
   UsageError("  --max-image-block-size=<size>: Maximum solid block size for compressed images.");
   UsageError("");
+  UsageError("  --compile-individually: Compiles dex files individually, unloading classes in");
+  UsageError("      between compiling each file.");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -774,6 +777,15 @@
   Handle<mirror::Object> old_field_value_;
 };
 
+class OatKeyValueStore : public SafeMap<std::string, std::string> {
+ public:
+  using SafeMap::Put;
+
+  iterator Put(const std::string& k, bool v) {
+    return SafeMap::Put(k, v ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+  }
+};
+
 class Dex2Oat final {
  public:
   explicit Dex2Oat(TimingLogger* timings) :
@@ -815,7 +827,8 @@
       timings_(timings),
       force_determinism_(false),
       check_linkage_conditions_(false),
-      crash_on_linkage_violation_(false)
+      crash_on_linkage_violation_(false),
+      compile_individually_(false)
       {}
 
   ~Dex2Oat() {
@@ -891,6 +904,7 @@
   }
 
   void ProcessOptions(ParserOptions* parser_options) {
+    compiler_options_->compiler_type_ = CompilerOptions::CompilerType::kAotCompiler;
     compiler_options_->compile_pic_ = true;  // All AOT compilation is PIC.
 
     if (android_root_.empty()) {
@@ -1111,7 +1125,7 @@
     }
 
     // Fill some values into the key-value store for the oat header.
-    key_value_store_.reset(new SafeMap<std::string, std::string>());
+    key_value_store_.reset(new OatKeyValueStore());
 
     // Automatically force determinism for the boot image and boot image extensions in a host build.
     if (!kIsTargetBuild && (IsBootImage() || IsBootImageExtension())) {
@@ -1200,16 +1214,13 @@
       }
       key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
     }
-    key_value_store_->Put(
-        OatHeader::kDebuggableKey,
-        compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
-    key_value_store_->Put(
-        OatHeader::kNativeDebuggableKey,
-        compiler_options_->GetNativeDebuggable() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+    key_value_store_->Put(OatHeader::kDebuggableKey, compiler_options_->debuggable_);
+    key_value_store_->Put(OatHeader::kNativeDebuggableKey,
+                          compiler_options_->GetNativeDebuggable());
     key_value_store_->Put(OatHeader::kCompilerFilter,
-        CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
-    key_value_store_->Put(OatHeader::kConcurrentCopying,
-                          kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+                          CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
+    key_value_store_->Put(OatHeader::kConcurrentCopying, kUseReadBarrier);
+    key_value_store_->Put(OatHeader::kRequiresImage, compiler_options_->IsGeneratingImage());
     if (invocation_file_.get() != -1) {
       std::ostringstream oss;
       for (int i = 0; i < argc; ++i) {
@@ -1370,6 +1381,7 @@
     if (args.Exists(M::ForceDeterminism)) {
       force_determinism_ = true;
     }
+    AssignTrueIfExists(args, M::CompileIndividually, &compile_individually_);
 
     if (args.Exists(M::Base)) {
       ParseBase(*args.Get(M::Base));
@@ -1559,18 +1571,17 @@
     // Note: we're only invalidating the magic data in the file, as dex2oat needs the rest of
     // the information to remain valid.
     if (update_input_vdex_) {
-      std::unique_ptr<BufferedOutputStream> vdex_out =
-          std::make_unique<BufferedOutputStream>(
-              std::make_unique<FileOutputStream>(vdex_files_.back().get()));
-      if (!vdex_out->WriteFully(&VdexFile::VerifierDepsHeader::kVdexInvalidMagic,
-                                arraysize(VdexFile::VerifierDepsHeader::kVdexInvalidMagic))) {
-        PLOG(ERROR) << "Failed to invalidate vdex header. File: " << vdex_out->GetLocation();
+      File* vdex_file = vdex_files_.back().get();
+      if (!vdex_file->PwriteFully(&VdexFile::VerifierDepsHeader::kVdexInvalidMagic,
+                                  arraysize(VdexFile::VerifierDepsHeader::kVdexInvalidMagic),
+                                  /*offset=*/ 0u)) {
+        PLOG(ERROR) << "Failed to invalidate vdex header. File: " << vdex_file->GetPath();
         return false;
       }
 
-      if (!vdex_out->Flush()) {
+      if (vdex_file->Flush() != 0) {
         PLOG(ERROR) << "Failed to flush stream after invalidating header of vdex file."
-                    << " File: " << vdex_out->GetLocation();
+                    << " File: " << vdex_file->GetPath();
         return false;
       }
     }
@@ -2009,17 +2020,17 @@
   }
 
   bool ShouldCompileDexFilesIndividually() const {
-    // Compile individually if we are:
-    // 1. not building an image,
-    // 2. not verifying a vdex file,
-    // 3. using multidex,
+    // Compile individually if we are specifically asked to, or
+    // 1. not building an image, and
+    // 2. not verifying a vdex file, and
+    // 3. using multidex, and
     // 4. not doing any AOT compilation.
     // This means extract, no-vdex verify, and quicken, will use the individual compilation
     // mode (to reduce RAM used by the compiler).
-    return !IsImage() &&
-        !update_input_vdex_ &&
-        compiler_options_->dex_files_for_oat_file_.size() > 1 &&
-        !CompilerFilter::IsAotCompilationEnabled(compiler_options_->GetCompilerFilter());
+    return compile_individually_ ||
+           (!IsImage() && !update_input_vdex_ &&
+            compiler_options_->dex_files_for_oat_file_.size() > 1 &&
+            !CompilerFilter::IsAotCompilationEnabled(compiler_options_->GetCompilerFilter()));
   }
 
   uint32_t GetCombinedChecksums() const {
@@ -2297,23 +2308,8 @@
       verifier::VerifierDeps* verifier_deps = callbacks_->GetVerifierDeps();
       for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
         File* vdex_file = vdex_files_[i].get();
-        std::unique_ptr<BufferedOutputStream> vdex_out =
-            std::make_unique<BufferedOutputStream>(
-                std::make_unique<FileOutputStream>(vdex_file));
-
-        if (!oat_writers_[i]->WriteVerifierDeps(vdex_out.get(), verifier_deps)) {
-          LOG(ERROR) << "Failed to write verifier dependencies into VDEX " << vdex_file->GetPath();
-          return false;
-        }
-
-        if (!oat_writers_[i]->WriteQuickeningInfo(vdex_out.get())) {
-          LOG(ERROR) << "Failed to write quickening info into VDEX " << vdex_file->GetPath();
-          return false;
-        }
-
-        // VDEX finalized, seek back to the beginning and write checksums and the header.
-        if (!oat_writers_[i]->WriteChecksumsAndVdexHeader(vdex_out.get())) {
-          LOG(ERROR) << "Failed to write vdex header into VDEX " << vdex_file->GetPath();
+        if (!oat_writers_[i]->FinishVdexFile(vdex_file, verifier_deps)) {
+          LOG(ERROR) << "Failed to finish VDEX file " << vdex_file->GetPath();
           return false;
         }
       }
@@ -2999,7 +2995,7 @@
   std::unique_ptr<CompilerOptions> compiler_options_;
   Compiler::Kind compiler_kind_;
 
-  std::unique_ptr<SafeMap<std::string, std::string> > key_value_store_;
+  std::unique_ptr<OatKeyValueStore> key_value_store_;
 
   std::unique_ptr<VerificationResults> verification_results_;
 
@@ -3108,6 +3104,9 @@
   // The reason for invoking the compiler.
   std::string compilation_reason_;
 
+  // Whether to force individual compilation.
+  bool compile_individually_;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
 };
 
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index d01b64f..71e9b1b 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -184,13 +184,11 @@
   }
   // Compile only a subset of the libcore dex files to make this test shorter.
   std::vector<std::string> libcore_dex_files = GetLibCoreDexFileNames();
-  // The primary image must contain at least core-oj and core-libart to initialize the runtime
-  // and we also need the core-icu4j if we want to compile these with full profile.
+  // The primary image must contain at least core-oj and core-libart to initialize the runtime.
   ASSERT_NE(std::string::npos, libcore_dex_files[0].find("core-oj"));
   ASSERT_NE(std::string::npos, libcore_dex_files[1].find("core-libart"));
-  ASSERT_NE(std::string::npos, libcore_dex_files[2].find("core-icu4j"));
   ArrayRef<const std::string> dex_files =
-      ArrayRef<const std::string>(libcore_dex_files).SubArray(/*pos=*/ 0u, /*length=*/ 3u);
+      ArrayRef<const std::string>(libcore_dex_files).SubArray(/*pos=*/ 0u, /*length=*/ 2u);
 
   ImageSizes base_sizes = CompileImageAndGetSizes(dex_files, {});
   ImageSizes everything_sizes;
@@ -274,19 +272,17 @@
 
   ArrayRef<const std::string> full_bcp(libcore_dex_files);
   size_t total_dex_files = full_bcp.size();
-  ASSERT_GE(total_dex_files, 5u);  // 3 for "head", 1 for "tail", at least one for "mid", see below.
+  ASSERT_GE(total_dex_files, 4u);  // 2 for "head", 1 for "tail", at least one for "mid", see below.
 
-  // The primary image must contain at least core-oj and core-libart to initialize the runtime
-  // and we also need the core-icu4j if we want to compile these with full profile.
+  // The primary image must contain at least core-oj and core-libart to initialize the runtime.
   ASSERT_NE(std::string::npos, full_bcp[0].find("core-oj"));
   ASSERT_NE(std::string::npos, full_bcp[1].find("core-libart"));
-  ASSERT_NE(std::string::npos, full_bcp[2].find("core-icu4j"));
-  ArrayRef<const std::string> head_dex_files = full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ 3u);
+  ArrayRef<const std::string> head_dex_files = full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ 2u);
   // Middle part is everything else except for conscrypt.
   ASSERT_NE(std::string::npos, full_bcp[full_bcp.size() - 1u].find("conscrypt"));
   ArrayRef<const std::string> mid_bcp =
       full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ total_dex_files - 1u);
-  ArrayRef<const std::string> mid_dex_files = mid_bcp.SubArray(/*pos=*/ 3u);
+  ArrayRef<const std::string> mid_dex_files = mid_bcp.SubArray(/*pos=*/ 2u);
   // Tail is just the conscrypt.
   ArrayRef<const std::string> tail_dex_files =
       full_bcp.SubArray(/*pos=*/ total_dex_files - 1u, /*length=*/ 1u);
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index d282c3b..81b9c98 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -262,7 +262,9 @@
           .IntoKey(M::RuntimeOptions)
       .Define("--compilation-reason=_")
           .WithType<std::string>()
-          .IntoKey(M::CompilationReason);
+          .IntoKey(M::CompilationReason)
+      .Define("--compile-individually")
+          .IntoKey(M::CompileIndividually);
 
   AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder);
 
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index 805a13e..8b018bf 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -94,5 +94,7 @@
 DEX2OAT_OPTIONS_KEY (std::string,                    CompilationReason)
 DEX2OAT_OPTIONS_KEY (Unit,                           CheckLinkageConditions)
 DEX2OAT_OPTIONS_KEY (Unit,                           CrashOnLinkageViolation)
+DEX2OAT_OPTIONS_KEY (Unit,                           CompileIndividually)
+
 
 #undef DEX2OAT_OPTIONS_KEY
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 45b7e4a..81fd09a 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -896,6 +896,9 @@
                                       /* use_fd= */ true));
       EXPECT_GT(vdex_file1->GetLength(), 0u);
     }
+    // Get the dex file checksums.
+    std::vector<uint32_t> checksums1;
+    GetDexFileChecksums(dex_location, odex_location, &checksums1);
     // Unquicken by running the verify compiler filter on the vdex file.
     {
       std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd());
@@ -909,6 +912,13 @@
     }
     ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
     CheckResult(dex_location, odex_location);
+    // Verify that the checksums did not change.
+    std::vector<uint32_t> checksums2;
+    GetDexFileChecksums(dex_location, odex_location, &checksums2);
+    ASSERT_EQ(checksums1.size(), checksums2.size());
+    for (size_t i = 0; i != checksums1.size(); ++i) {
+      EXPECT_EQ(checksums1[i], checksums2[i]) << i;
+    }
     ASSERT_TRUE(success_);
   }
 
@@ -937,7 +947,6 @@
                                       /* use_fd= */ true));
       EXPECT_GT(vdex_file1->GetLength(), 0u);
     }
-
     // Unquicken by running the verify compiler filter on the vdex file.
     {
       std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd());
@@ -979,6 +988,24 @@
       }
     }
   }
+
+  void GetDexFileChecksums(const std::string& dex_location,
+                           const std::string& odex_location,
+                           /*out*/std::vector<uint32_t>* checksums) {
+    std::string error_msg;
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
+                                                     odex_location.c_str(),
+                                                     odex_location.c_str(),
+                                                     /*executable=*/ false,
+                                                     /*low_4gb=*/ false,
+                                                     dex_location,
+                                                     &error_msg));
+    ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+    ASSERT_GE(odex_file->GetOatDexFiles().size(), 1u);
+    for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) {
+      checksums->push_back(oat_dex_file->GetDexFileLocationChecksum());
+    }
+  }
 };
 
 TEST_F(Dex2oatUnquickenTest, UnquickenMultiDex) {
@@ -1281,7 +1308,6 @@
   const std::string unload_vdex_name = out_dir + "/unload.vdex";
   const std::string no_unload_oat_name = out_dir + "/nounload.oat";
   const std::string no_unload_vdex_name = out_dir + "/nounload.vdex";
-  const std::string app_image_name = out_dir + "/unload.art";
   std::string error_msg;
   const std::vector<gc::space::ImageSpace*>& spaces = runtime->GetHeap()->GetBootImageSpaces();
   ASSERT_GT(spaces.size(), 0u);
@@ -1309,7 +1335,7 @@
       base_oat_name,
       CompilerFilter::Filter::kQuicken,
       &error_msg,
-      {"--force-determinism", "--avoid-storing-invocation", "--app-image-file=" + app_image_name});
+      {"--force-determinism", "--avoid-storing-invocation", "--compile-individually"});
   ASSERT_EQ(res2, 0);
   Copy(base_oat_name, no_unload_oat_name);
   Copy(base_vdex_name, no_unload_vdex_name);
@@ -1326,10 +1352,6 @@
       << unload_oat_name << " " << no_unload_oat_name;
   EXPECT_EQ(unload_vdex->Compare(no_unload_vdex.get()), 0)
       << unload_vdex_name << " " << no_unload_vdex_name;
-  // App image file.
-  std::unique_ptr<File> app_image_file(OS::OpenFileForReading(app_image_name.c_str()));
-  ASSERT_TRUE(app_image_file != nullptr);
-  EXPECT_GT(app_image_file->GetLength(), 0u);
 }
 
 // Test that dexlayout section info is correctly written to the oat file for profile based
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc
index e21f492..31e78aa 100644
--- a/dex2oat/driver/compiler_driver.cc
+++ b/dex2oat/driver/compiler_driver.cc
@@ -976,8 +976,7 @@
     return true;
   }
 
-  void FindExceptionTypesToResolve(
-      std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
+  void FindExceptionTypesToResolve(std::set<TypeReference>* exceptions_to_resolve)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
     for (ObjPtr<mirror::Class> klass : classes_) {
@@ -990,7 +989,7 @@
  private:
   void FindExceptionTypesToResolveForMethod(
       ArtMethod* method,
-      std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
+      std::set<TypeReference>* exceptions_to_resolve)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     if (method->GetCodeItem() == nullptr) {
       return;  // native or abstract method
@@ -1013,8 +1012,8 @@
             dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list));
         // Add to set of types to resolve if not already in the dex cache resolved types
         if (!method->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
-          exceptions_to_resolve->emplace(encoded_catch_handler_handlers_type_idx,
-                                         method->GetDexFile());
+          exceptions_to_resolve->emplace(method->GetDexFile(),
+                                         encoded_catch_handler_handlers_type_idx);
         }
         // ignore address associated with catch handler
         DecodeUnsignedLeb128(&encoded_catch_handler_list);
@@ -1078,10 +1077,19 @@
     return;
   }
 
-  // Make sure the File[] class is in the primary boot image. b/150319075
+  // A hard-coded list of array classes that should be in the primary boot image profile. The impact
+  // of each class can be approximately measured by comparing oatdump output with and without it:
+  // `m dump-oat-boot && grep -cE 'Class.*VisiblyInitialized' boot.host-<arch>.oatdump.txt`.
+  //   - b/150319075: File[]
+  //   - b/156098788: int[][], int[][][], short[][], byte[][][]
+  //
   // TODO: Implement support for array classes in profiles and remove this workaround. b/148067697
   if (GetCompilerOptions().IsBootImage()) {
     image_classes->insert("[Ljava/io/File;");
+    image_classes->insert("[[I");
+    image_classes->insert("[[[I");
+    image_classes->insert("[[S");
+    image_classes->insert("[[[B");
   }
 
   TimingLogger::ScopedTiming t("LoadImageClasses", timings);
@@ -1107,10 +1115,12 @@
   // Resolve exception classes referenced by the loaded classes. The catch logic assumes
   // exceptions are resolved by the verifier when there is a catch block in an interested method.
   // Do this here so that exception classes appear to have been specified image classes.
-  std::set<std::pair<dex::TypeIndex, const DexFile*>> unresolved_exception_types;
-  StackHandleScope<1> hs(self);
+  std::set<TypeReference> unresolved_exception_types;
+  StackHandleScope<2u> hs(self);
   Handle<mirror::Class> java_lang_Throwable(
       hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;")));
+  MutableHandle<mirror::DexCache> dex_cache = hs.NewHandle(java_lang_Throwable->GetDexCache());
+  DCHECK(dex_cache != nullptr);
   do {
     unresolved_exception_types.clear();
     {
@@ -1121,24 +1131,25 @@
       class_linker->VisitClasses(&visitor);
       visitor.FindExceptionTypesToResolve(&unresolved_exception_types);
     }
-    for (const auto& exception_type : unresolved_exception_types) {
-      dex::TypeIndex exception_type_idx = exception_type.first;
-      const DexFile* dex_file = exception_type.second;
-      StackHandleScope<1> hs2(self);
-      Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file,
-                                                                                     nullptr)));
-      ObjPtr<mirror::Class> klass =
-          (dex_cache != nullptr)
-              ? class_linker->ResolveType(exception_type_idx,
-                                          dex_cache,
-                                          ScopedNullHandle<mirror::ClassLoader>())
-              : nullptr;
+    for (auto it = unresolved_exception_types.begin(); it != unresolved_exception_types.end(); ) {
+      dex::TypeIndex exception_type_idx = it->TypeIndex();
+      const DexFile* dex_file = it->dex_file;
+      if (dex_cache->GetDexFile() != dex_file) {
+        dex_cache.Assign(class_linker->RegisterDexFile(*dex_file, /*class_loader=*/ nullptr));
+        DCHECK(dex_cache != nullptr);
+      }
+      ObjPtr<mirror::Class> klass = class_linker->ResolveType(
+          exception_type_idx, dex_cache, ScopedNullHandle<mirror::ClassLoader>());
       if (klass == nullptr) {
         const dex::TypeId& type_id = dex_file->GetTypeId(exception_type_idx);
         const char* descriptor = dex_file->GetTypeDescriptor(type_id);
-        LOG(FATAL) << "Failed to resolve class " << descriptor;
+        VLOG(compiler) << "Failed to resolve exception class " << descriptor;
+        self->ClearException();
+        it = unresolved_exception_types.erase(it);
+      } else {
+        DCHECK(java_lang_Throwable->IsAssignableFrom(klass));
+        ++it;
       }
-      DCHECK(java_lang_Throwable->IsAssignableFrom(klass));
     }
     // Resolving exceptions may load classes that reference more exceptions, iterate until no
     // more are found
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 303c262..ee897ed 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -294,12 +294,7 @@
         ASSERT_TRUE(start_rodata_ok);
         oat_writer->Initialize(driver, writer.get(), cur_dex_files);
 
-        std::unique_ptr<BufferedOutputStream> vdex_out =
-            std::make_unique<BufferedOutputStream>(
-                std::make_unique<FileOutputStream>(out_helper.vdex_files[i].GetFile()));
-        oat_writer->WriteVerifierDeps(vdex_out.get(), nullptr);
-        oat_writer->WriteQuickeningInfo(vdex_out.get());
-        oat_writer->WriteChecksumsAndVdexHeader(vdex_out.get());
+        oat_writer->FinishVdexFile(out_helper.vdex_files[i].GetFile(), /*verifier_deps=*/ nullptr);
 
         oat_writer->PrepareLayout(&patcher);
         elf_writer->PrepareDynamicSection(oat_writer->GetOatHeader().GetExecutableOffset(),
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index b7776ff..d56fea9 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -35,7 +35,7 @@
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "compiled_method.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_types.h"
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 055f246..c005cc2 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -186,14 +186,25 @@
     return static_cast<const uint8_t*>(source_);
   }
 
+  void SetDexLayoutData(std::vector<uint8_t>&& dexlayout_data) {
+    DCHECK_GE(dexlayout_data.size(), sizeof(DexFile::Header));
+    dexlayout_data_ = std::move(dexlayout_data);
+    type_ = kRawData;
+    source_ = dexlayout_data_.data();
+  }
+
   void Clear() {
     type_ = kNone;
     source_ = nullptr;
+    // Release the memory held by `dexlayout_data_`.
+    std::vector<uint8_t> temp;
+    temp.swap(dexlayout_data_);
   }
 
  private:
   Type type_;
   const void* source_;
+  std::vector<uint8_t> dexlayout_data_;
 };
 
 // OatClassHeader is the header only part of the oat class that is required even when compilation
@@ -314,7 +325,7 @@
   CreateTypeLookupTable create_type_lookup_table_;
 
   // Dex file size. Passed in the constructor, but could be
-  // overwritten by LayoutAndWriteDexFile.
+  // overwritten by LayoutDexFile.
   size_t dex_file_size_;
 
   // Offset of start of OatDexFile from beginning of OatHeader. It is
@@ -380,6 +391,7 @@
     compiler_options_(compiler_options),
     image_writer_(nullptr),
     extract_dex_files_into_vdex_(true),
+    vdex_begin_(nullptr),
     dex_files_(nullptr),
     primary_oat_file_(false),
     vdex_size_(0u),
@@ -406,6 +418,7 @@
     size_vdex_header_(0),
     size_vdex_checksums_(0),
     size_dex_file_alignment_(0),
+    size_quickening_table_offset_(0),
     size_executable_offset_alignment_(0),
     size_oat_header_(0),
     size_oat_header_key_value_store_(0),
@@ -676,12 +689,10 @@
   vdex_size_ = sizeof(VdexFile::VerifierDepsHeader) +
       oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum);
 
-  std::unique_ptr<BufferedOutputStream> vdex_out =
-      std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file));
   // Write DEX files into VDEX, mmap and open them.
   std::vector<MemMap> dex_files_map;
   std::vector<std::unique_ptr<const DexFile>> dex_files;
-  if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex, copy_dex_files) ||
+  if (!WriteDexFiles(vdex_file, update_input_vdex, copy_dex_files, &dex_files_map) ||
       !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) {
     return false;
   }
@@ -2415,11 +2426,11 @@
 
 class OatWriter::WriteQuickeningInfoMethodVisitor {
  public:
-  WriteQuickeningInfoMethodVisitor(OatWriter* writer, OutputStream* out)
+  WriteQuickeningInfoMethodVisitor(OatWriter* writer, /*out*/std::vector<uint8_t>* buffer)
       : writer_(writer),
-        out_(out) {}
+        buffer_(buffer) {}
 
-  bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) {
+  void VisitDexMethods(const std::vector<const DexFile*>& dex_files) {
     // Map of offsets for quicken info related to method indices.
     SafeMap<const uint8_t*, uint32_t> offset_map;
     // Use method index order to minimize the encoded size of the offset table.
@@ -2435,25 +2446,20 @@
 
           // Record each index if required. written_bytes_ is the offset from the start of the
           // quicken info data.
-          // May be already inserted for deduplicate items.
+          // May be already inserted for duplicate items.
           // Add offset of one to make sure 0 represents unused.
           auto pair = offset_map.emplace(map.data(), written_bytes_ + 1);
           offset = pair.first->second;
           // Write out the map if it's not already written.
           if (pair.second) {
-            const uint32_t length = map.size() * sizeof(map.front());
-            if (!out_->WriteFully(map.data(), length)) {
-              PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod()
-                          << " to " << out_->GetLocation();
-              return false;
-            }
-            written_bytes_ += length;
+            static_assert(sizeof(map[0]) == 1u);
+            buffer_->insert(buffer_->end(), map.begin(), map.end());
+            written_bytes_ += map.size();
           }
         }
         offsets->push_back(offset);
       }
     }
-    return true;
   }
 
   size_t GetNumberOfWrittenBytes() const {
@@ -2466,7 +2472,7 @@
 
  private:
   OatWriter* const writer_;
-  OutputStream* const out_;
+  std::vector<uint8_t>* const buffer_;
   size_t written_bytes_ = 0u;
   SafeMap<const DexFile*, std::vector<uint32_t>> quicken_info_offset_indices_;
 };
@@ -2474,16 +2480,16 @@
 class OatWriter::WriteQuickeningInfoOffsetsMethodVisitor {
  public:
   WriteQuickeningInfoOffsetsMethodVisitor(
-      OutputStream* out,
+      /*out*/std::vector<uint8_t>* buffer,
       uint32_t start_offset,
       SafeMap<const DexFile*, std::vector<uint32_t>>* quicken_info_offset_indices,
       std::vector<uint32_t>* out_table_offsets)
-      : out_(out),
+      : buffer_(buffer),
         start_offset_(start_offset),
         quicken_info_offset_indices_(quicken_info_offset_indices),
         out_table_offsets_(out_table_offsets) {}
 
-  bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) {
+  void VisitDexMethods(const std::vector<const DexFile*>& dex_files) {
     for (const DexFile* dex_file : dex_files) {
       auto it = quicken_info_offset_indices_->find(dex_file);
       DCHECK(it != quicken_info_offset_indices_->end()) << "Failed to find dex file "
@@ -2501,15 +2507,10 @@
       // to the start of the quicken info section.
       out_table_offsets_->push_back(current_offset);
 
-      const uint32_t length = table_data.size() * sizeof(table_data.front());
-      if (!out_->WriteFully(table_data.data(), length)) {
-        PLOG(ERROR) << "Failed to write quickening offset table for " << dex_file->GetLocation()
-                    << " to " << out_->GetLocation();
-        return false;
-      }
-      written_bytes_ += length;
+      static_assert(sizeof(table_data[0]) == 1u);
+      buffer_->insert(buffer_->end(), table_data.begin(), table_data.end());
+      written_bytes_ += table_data.size();
     }
-    return true;
   }
 
   size_t GetNumberOfWrittenBytes() const {
@@ -2517,7 +2518,7 @@
   }
 
  private:
-  OutputStream* const out_;
+  std::vector<uint8_t>* const buffer_;
   const uint32_t start_offset_;
   size_t written_bytes_ = 0u;
   // Maps containing the offsets for the tables.
@@ -2525,148 +2526,89 @@
   std::vector<uint32_t>* const out_table_offsets_;
 };
 
-bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) {
-  if (!extract_dex_files_into_vdex_) {
+void OatWriter::WriteQuickeningInfo(/*out*/std::vector<uint8_t>* buffer) {
+  if (!extract_dex_files_into_vdex_ || !GetCompilerOptions().IsQuickeningCompilationEnabled()) {
     // Nothing to write. Leave `vdex_size_` untouched and unaligned.
     vdex_quickening_info_offset_ = vdex_size_;
     size_quickening_info_alignment_ = 0;
-    return true;
+    return;
   }
-  size_t initial_offset = vdex_size_;
+
+  TimingLogger::ScopedTiming split("VDEX quickening info", timings_);
+
   // Make sure the table is properly aligned.
-  size_t start_offset = RoundUp(initial_offset, 4u);
+  size_t start_offset = RoundUp(vdex_size_, 4u);
+  const size_t padding_bytes = start_offset - vdex_size_;
+  buffer->resize(buffer->size() + padding_bytes, 0u);
 
-  off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet);
-  if (actual_offset != static_cast<off_t>(start_offset)) {
-    PLOG(ERROR) << "Failed to seek to quickening info section. Actual: " << actual_offset
-                << " Expected: " << start_offset
-                << " Output: " << vdex_out->GetLocation();
-    return false;
+  WriteQuickeningInfoMethodVisitor write_quicken_info_visitor(this, buffer);
+  write_quicken_info_visitor.VisitDexMethods(*dex_files_);
+
+  uint32_t quicken_info_offset = write_quicken_info_visitor.GetNumberOfWrittenBytes();
+  uint32_t extra_bytes_offset = start_offset + quicken_info_offset;
+  size_t current_offset = RoundUp(extra_bytes_offset, CompactOffsetTable::kAlignment);
+  const size_t extra_bytes = current_offset - extra_bytes_offset;
+  buffer->resize(buffer->size() + extra_bytes, 0u);
+  quicken_info_offset += extra_bytes;
+
+  std::vector<uint32_t> table_offsets;
+  WriteQuickeningInfoOffsetsMethodVisitor table_visitor(
+      buffer,
+      quicken_info_offset,
+      &write_quicken_info_visitor.GetQuickenInfoOffsetIndices(),
+      /*out*/ &table_offsets);
+  table_visitor.VisitDexMethods(*dex_files_);
+
+  CHECK_EQ(table_offsets.size(), dex_files_->size());
+
+  current_offset += table_visitor.GetNumberOfWrittenBytes();
+
+  // Store the offset table offset as a preheader for each dex.
+  size_t index = 0;
+  for (const OatDexFile& oat_dex_file : oat_dex_files_) {
+    auto* quickening_table_offset = reinterpret_cast<VdexFile::QuickeningTableOffsetType*>(
+        vdex_begin_ + oat_dex_file.dex_file_offset_ - sizeof(VdexFile::QuickeningTableOffsetType));
+    *quickening_table_offset = table_offsets[index];
+    ++index;
   }
-
-  size_t current_offset = start_offset;
-  if (GetCompilerOptions().IsQuickeningCompilationEnabled()) {
-    std::vector<uint32_t> dex_files_indices;
-    WriteQuickeningInfoMethodVisitor write_quicken_info_visitor(this, vdex_out);
-    if (!write_quicken_info_visitor.VisitDexMethods(*dex_files_)) {
-      PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
-      return false;
-    }
-
-    uint32_t quicken_info_offset = write_quicken_info_visitor.GetNumberOfWrittenBytes();
-    current_offset = current_offset + quicken_info_offset;
-    uint32_t before_offset = current_offset;
-    current_offset = RoundUp(current_offset, CompactOffsetTable::kAlignment);
-    const size_t extra_bytes = current_offset - before_offset;
-    quicken_info_offset += extra_bytes;
-    actual_offset = vdex_out->Seek(current_offset, kSeekSet);
-    if (actual_offset != static_cast<off_t>(current_offset)) {
-      PLOG(ERROR) << "Failed to seek to quickening offset table section. Actual: " << actual_offset
-                  << " Expected: " << current_offset
-                  << " Output: " << vdex_out->GetLocation();
-      return false;
-    }
-
-    std::vector<uint32_t> table_offsets;
-    WriteQuickeningInfoOffsetsMethodVisitor table_visitor(
-        vdex_out,
-        quicken_info_offset,
-        &write_quicken_info_visitor.GetQuickenInfoOffsetIndices(),
-        /*out*/ &table_offsets);
-    if (!table_visitor.VisitDexMethods(*dex_files_)) {
-      PLOG(ERROR) << "Failed to write the vdex quickening info. File: "
-                  << vdex_out->GetLocation();
-      return false;
-    }
-
-    CHECK_EQ(table_offsets.size(), dex_files_->size());
-
-    current_offset += table_visitor.GetNumberOfWrittenBytes();
-
-    // Store the offset table offset as a preheader for each dex.
-    size_t index = 0;
-    for (const OatDexFile& oat_dex_file : oat_dex_files_) {
-      const off_t desired_offset = oat_dex_file.dex_file_offset_ -
-          sizeof(VdexFile::QuickeningTableOffsetType);
-      actual_offset = vdex_out->Seek(desired_offset, kSeekSet);
-      if (actual_offset != desired_offset) {
-        PLOG(ERROR) << "Failed to seek to before dex file for writing offset table offset: "
-                    << actual_offset << " Expected: " << desired_offset
-                    << " Output: " << vdex_out->GetLocation();
-        return false;
-      }
-      uint32_t offset = table_offsets[index];
-      if (!vdex_out->WriteFully(reinterpret_cast<const uint8_t*>(&offset), sizeof(offset))) {
-        PLOG(ERROR) << "Failed to write verifier deps."
-                    << " File: " << vdex_out->GetLocation();
-        return false;
-      }
-      ++index;
-    }
-    if (!vdex_out->Flush()) {
-      PLOG(ERROR) << "Failed to flush stream after writing quickening info."
-                  << " File: " << vdex_out->GetLocation();
-      return false;
-    }
-    size_quickening_info_ = current_offset - start_offset;
-  } else {
-    // We know we did not quicken.
-    size_quickening_info_ = 0;
-  }
+  size_quickening_info_ = current_offset - start_offset;
 
   if (size_quickening_info_ == 0) {
     // Nothing was written. Leave `vdex_size_` untouched and unaligned.
-    vdex_quickening_info_offset_ = initial_offset;
+    buffer->resize(buffer->size() - padding_bytes);  // Drop the padding data.
+    vdex_quickening_info_offset_ = vdex_size_;
     size_quickening_info_alignment_ = 0;
   } else {
-    vdex_size_ = start_offset + size_quickening_info_;
+    size_quickening_info_alignment_ = padding_bytes;
     vdex_quickening_info_offset_ = start_offset;
-    size_quickening_info_alignment_ = start_offset - initial_offset;
+    vdex_size_ = start_offset + size_quickening_info_;
   }
-
-  return true;
 }
 
-bool OatWriter::WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps) {
+void OatWriter::WriteVerifierDeps(verifier::VerifierDeps* verifier_deps,
+                                  /*out*/std::vector<uint8_t>* buffer) {
   if (verifier_deps == nullptr) {
     // Nothing to write. Record the offset, but no need
     // for alignment.
     vdex_verifier_deps_offset_ = vdex_size_;
-    return true;
+    return;
   }
 
+  TimingLogger::ScopedTiming split("VDEX verifier deps", timings_);
+
   size_t initial_offset = vdex_size_;
   size_t start_offset = RoundUp(initial_offset, 4u);
 
   vdex_size_ = start_offset;
   vdex_verifier_deps_offset_ = vdex_size_;
   size_verifier_deps_alignment_ = start_offset - initial_offset;
+  buffer->resize(buffer->size() + size_verifier_deps_alignment_, 0u);
 
-  off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet);
-  if (actual_offset != static_cast<off_t>(start_offset)) {
-    PLOG(ERROR) << "Failed to seek to verifier deps section. Actual: " << actual_offset
-                << " Expected: " << start_offset
-                << " Output: " << vdex_out->GetLocation();
-    return false;
-  }
+  size_t old_buffer_size = buffer->size();
+  verifier_deps->Encode(*dex_files_, buffer);
 
-  std::vector<uint8_t> buffer;
-  verifier_deps->Encode(*dex_files_, &buffer);
-
-  if (!vdex_out->WriteFully(buffer.data(), buffer.size())) {
-    PLOG(ERROR) << "Failed to write verifier deps."
-                << " File: " << vdex_out->GetLocation();
-    return false;
-  }
-  if (!vdex_out->Flush()) {
-    PLOG(ERROR) << "Failed to flush stream after writing verifier deps."
-                << " File: " << vdex_out->GetLocation();
-    return false;
-  }
-
-  size_verifier_deps_ = buffer.size();
+  size_verifier_deps_ = buffer->size() - old_buffer_size;
   vdex_size_ += size_verifier_deps_;
-  return true;
 }
 
 bool OatWriter::WriteCode(OutputStream* out) {
@@ -2752,6 +2694,7 @@
     DO_STAT(size_vdex_header_);
     DO_STAT(size_vdex_checksums_);
     DO_STAT(size_dex_file_alignment_);
+    DO_STAT(size_quickening_table_offset_);
     DO_STAT(size_executable_offset_alignment_);
     DO_STAT(size_oat_header_);
     DO_STAT(size_oat_header_key_value_store_);
@@ -3177,10 +3120,10 @@
   return true;
 }
 
-bool OatWriter::WriteDexFiles(OutputStream* out,
-                              File* file,
+bool OatWriter::WriteDexFiles(File* file,
                               bool update_input_vdex,
-                              CopyOption copy_dex_files) {
+                              CopyOption copy_dex_files,
+                              /*out*/ std::vector<MemMap>* opened_dex_files_map) {
   TimingLogger::ScopedTiming split("Write Dex files", timings_);
 
   // If extraction is enabled, only do it if not all the dex files are aligned and uncompressed.
@@ -3208,113 +3151,165 @@
     // Add the dex section header.
     vdex_size_ += sizeof(VdexFile::DexSectionHeader);
     vdex_dex_files_offset_ = vdex_size_;
-    // Write dex files.
-    for (OatDexFile& oat_dex_file : oat_dex_files_) {
-      if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) {
-        return false;
+
+    // Perform dexlayout if requested.
+    if (profile_compilation_info_ != nullptr ||
+        compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone) {
+      for (OatDexFile& oat_dex_file : oat_dex_files_) {
+        // update_input_vdex disables compact dex and layout.
+        CHECK(!update_input_vdex)
+            << "We should never update the input vdex when doing dexlayout or compact dex";
+        if (!LayoutDexFile(&oat_dex_file)) {
+          return false;
+        }
       }
     }
 
-    // Write shared dex file data section and fix up the dex file headers.
-    vdex_dex_shared_data_offset_ = vdex_size_;
-    uint32_t shared_data_size = 0u;
-
-    if (dex_container_ != nullptr) {
-      CHECK(!update_input_vdex) << "Update input vdex should have empty dex container";
-      DexContainer::Section* const section = dex_container_->GetDataSection();
-      if (section->Size() > 0) {
-        CHECK(compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone);
-        const off_t existing_offset = out->Seek(0, kSeekCurrent);
-        if (static_cast<uint32_t>(existing_offset) != vdex_dex_shared_data_offset_) {
-          PLOG(ERROR) << "Expected offset " << vdex_dex_shared_data_offset_ << " but got "
-                      << existing_offset;
-          return false;
-        }
-        shared_data_size = section->Size();
-        if (!out->WriteFully(section->Begin(), shared_data_size)) {
-          PLOG(ERROR) << "Failed to write shared data!";
-          return false;
-        }
-        if (!out->Flush()) {
-          PLOG(ERROR) << "Failed to flush after writing shared dex section.";
-          return false;
-        }
-        // Fix up the dex headers to have correct offsets to the data section.
-        for (OatDexFile& oat_dex_file : oat_dex_files_) {
-          // Overwrite the header by reading it, updating the offset, and writing it back out.
-          DexFile::Header header;
-          if (!file->PreadFully(&header, sizeof(header), oat_dex_file.dex_file_offset_)) {
-            PLOG(ERROR) << "Failed to read dex header for updating";
-            return false;
-          }
-          if (!CompactDexFile::IsMagicValid(header.magic_)) {
-            // Non-compact dex file, probably failed to convert due to duplicate methods.
-            continue;
-          }
-          CHECK_GT(vdex_dex_shared_data_offset_, oat_dex_file.dex_file_offset_);
-          // Offset is from the dex file base.
-          header.data_off_ = vdex_dex_shared_data_offset_ - oat_dex_file.dex_file_offset_;
-          // The size should already be what part of the data buffer may be used by the dex.
-          CHECK_LE(header.data_size_, shared_data_size);
-          if (!file->PwriteFully(&header, sizeof(header), oat_dex_file.dex_file_offset_)) {
-            PLOG(ERROR) << "Failed to write dex header for updating";
-            return false;
-          }
-        }
-        section->Clear();
+    // Calculate the total size after the dex files.
+    size_t vdex_size_with_dex_files = vdex_size_;
+    for (OatDexFile& oat_dex_file : oat_dex_files_) {
+      // Dex files are required to be 4 byte aligned.
+      vdex_size_with_dex_files = RoundUp(vdex_size_with_dex_files, 4u);
+      // Leave extra room for the quicken table offset.
+      vdex_size_with_dex_files += sizeof(VdexFile::QuickeningTableOffsetType);
+      // Record offset for the dex file.
+      oat_dex_file.dex_file_offset_ = vdex_size_with_dex_files;
+      // Add the size of the dex file.
+      if (oat_dex_file.dex_file_size_ < sizeof(DexFile::Header)) {
+        LOG(ERROR) << "Dex file " << oat_dex_file.GetLocation() << " is too short: "
+            << oat_dex_file.dex_file_size_ << " < " << sizeof(DexFile::Header);
+        return false;
       }
-      dex_container_.reset();
+      vdex_size_with_dex_files += oat_dex_file.dex_file_size_;
+    }
+    // Add the shared data section size.
+    const uint8_t* raw_dex_file_shared_data_begin = nullptr;
+    uint32_t shared_data_size = 0u;
+    if (dex_container_ != nullptr) {
+      shared_data_size = dex_container_->GetDataSection()->Size();
     } else {
-      const uint8_t* data_begin = nullptr;
-      for (OatDexFile& oat_dex_file : oat_dex_files_) {
-        DexFile::Header header;
-        if (!file->PreadFully(&header, sizeof(header), oat_dex_file.dex_file_offset_)) {
-          PLOG(ERROR) << "Failed to read dex header";
-          return false;
+      // Dex files from input vdex are represented as raw dex files and they can be
+      // compact dex files. These need to specify the same shared data section if any.
+      for (const OatDexFile& oat_dex_file : oat_dex_files_) {
+        if (!oat_dex_file.source_.IsRawData()) {
+          continue;
         }
-        if (!CompactDexFile::IsMagicValid(header.magic_)) {
+        const uint8_t* raw_data = oat_dex_file.source_.GetRawData();
+        const UnalignedDexFileHeader& header = *AsUnalignedDexFileHeader(raw_data);
+        if (!CompactDexFile::IsMagicValid(header.magic_) || header.data_size_ == 0u) {
           // Non compact dex does not have shared data section.
           continue;
         }
-        const uint32_t expected_data_off = vdex_dex_shared_data_offset_ -
-            oat_dex_file.dex_file_offset_;
-        if (header.data_off_ != expected_data_off) {
-          PLOG(ERROR) << "Shared data section offset " << header.data_off_
-                      << " does not match expected value " << expected_data_off;
+        const uint8_t* cur_data_begin = raw_data + header.data_off_;
+        if (raw_dex_file_shared_data_begin == nullptr) {
+          raw_dex_file_shared_data_begin = cur_data_begin;
+        } else if (raw_dex_file_shared_data_begin != cur_data_begin) {
+          LOG(ERROR) << "Mismatched shared data sections in raw dex files: "
+                     << static_cast<const void*>(raw_dex_file_shared_data_begin)
+                     << " != " << static_cast<const void*>(cur_data_begin);
           return false;
         }
-        if (oat_dex_file.source_.IsRawData()) {
-          // Figure out the start of the shared data section so we can copy it below.
-          const uint8_t* cur_data_begin = oat_dex_file.source_.GetRawData() + header.data_off_;
-          if (data_begin != nullptr) {
-            CHECK_EQ(data_begin, cur_data_begin);
-          }
-          data_begin = cur_data_begin;
-        }
         // The different dex files currently can have different data sizes since
         // the dex writer writes them one at a time into the shared section.:w
         shared_data_size = std::max(shared_data_size, header.data_size_);
       }
-      // If we are not updating the input vdex, write out the shared data section.
+    }
+    if (shared_data_size != 0u) {
+      // Shared data section is required to be 4 byte aligned.
+      vdex_size_with_dex_files = RoundUp(vdex_size_with_dex_files, 4u);
+    }
+    vdex_dex_shared_data_offset_ = vdex_size_with_dex_files;
+    vdex_size_with_dex_files += shared_data_size;
+
+    // Extend the file and include the full page at the end as we need to write
+    // additional data there and do not want to mmap that page twice.
+    size_t page_aligned_size = RoundUp(vdex_size_with_dex_files, kPageSize);
+    if (!update_input_vdex) {
+      if (file->SetLength(page_aligned_size) != 0) {
+        PLOG(ERROR) << "Failed to resize vdex file " << file->GetPath();
+        return false;
+      }
+    }
+
+    std::string error_msg;
+    MemMap dex_files_map = MemMap::MapFile(
+        page_aligned_size,
+        PROT_READ | PROT_WRITE,
+        MAP_SHARED,
+        file->Fd(),
+        /*start=*/ 0u,
+        /*low_4gb=*/ false,
+        file->GetPath().c_str(),
+        &error_msg);
+    if (!dex_files_map.IsValid()) {
+      LOG(ERROR) << "Failed to mmap() dex files from oat file. File: " << file->GetPath()
+                 << " error: " << error_msg;
+      return false;
+    }
+    vdex_begin_ = dex_files_map.Begin();
+
+    // Write dex files.
+    for (OatDexFile& oat_dex_file : oat_dex_files_) {
+      // Dex files are required to be 4 byte aligned.
+      size_t quickening_table_offset_offset = RoundUp(vdex_size_, 4u);
       if (!update_input_vdex) {
-        const off_t existing_offset = out->Seek(0, kSeekCurrent);
-        if (static_cast<uint32_t>(existing_offset) != vdex_dex_shared_data_offset_) {
-          PLOG(ERROR) << "Expected offset " << vdex_dex_shared_data_offset_ << " but got "
-                      << existing_offset;
-          return false;
-        }
-        if (!out->WriteFully(data_begin, shared_data_size)) {
-          PLOG(ERROR) << "Failed to write shared data!";
-          return false;
-        }
-        if (!out->Flush()) {
-          PLOG(ERROR) << "Failed to flush after writing shared dex section.";
-          return false;
+        // Clear the padding.
+        memset(vdex_begin_ + vdex_size_, 0, quickening_table_offset_offset - vdex_size_);
+        // Initialize the quickening table offset to 0.
+        auto* quickening_table_offset = reinterpret_cast<VdexFile::QuickeningTableOffsetType*>(
+            vdex_begin_ + quickening_table_offset_offset);
+        *quickening_table_offset = 0u;
+      }
+      size_dex_file_alignment_ += quickening_table_offset_offset - vdex_size_;
+      size_quickening_table_offset_ += sizeof(VdexFile::QuickeningTableOffsetType);
+      vdex_size_ = quickening_table_offset_offset + sizeof(VdexFile::QuickeningTableOffsetType);
+      // Write the actual dex file.
+      if (!WriteDexFile(file, &oat_dex_file, update_input_vdex)) {
+        return false;
+      }
+    }
+
+    // Write shared dex file data section and fix up the dex file headers.
+    if (shared_data_size != 0u) {
+      DCHECK_EQ(RoundUp(vdex_size_, 4u), vdex_dex_shared_data_offset_);
+      if (!update_input_vdex) {
+        memset(vdex_begin_ + vdex_size_, 0, vdex_dex_shared_data_offset_ - vdex_size_);
+      }
+      size_dex_file_alignment_ += vdex_dex_shared_data_offset_ - vdex_size_;
+      vdex_size_ = vdex_dex_shared_data_offset_;
+
+      if (dex_container_ != nullptr) {
+        CHECK(!update_input_vdex) << "Update input vdex should have empty dex container";
+        CHECK(compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone);
+        DexContainer::Section* const section = dex_container_->GetDataSection();
+        DCHECK_EQ(shared_data_size, section->Size());
+        memcpy(vdex_begin_ + vdex_size_, section->Begin(), shared_data_size);
+        section->Clear();
+        dex_container_.reset();
+      } else if (!update_input_vdex) {
+        // If we are not updating the input vdex, write out the shared data section.
+        memcpy(vdex_begin_ + vdex_size_, raw_dex_file_shared_data_begin, shared_data_size);
+      }
+      vdex_size_ += shared_data_size;
+      size_dex_file_ += shared_data_size;
+      if (!update_input_vdex) {
+        // Fix up the dex headers to have correct offsets to the data section.
+        for (OatDexFile& oat_dex_file : oat_dex_files_) {
+          DexFile::Header* header =
+              reinterpret_cast<DexFile::Header*>(vdex_begin_ + oat_dex_file.dex_file_offset_);
+          if (!CompactDexFile::IsMagicValid(header->magic_)) {
+            // Non-compact dex file, probably failed to convert due to duplicate methods.
+            continue;
+          }
+          CHECK_GT(vdex_dex_shared_data_offset_, oat_dex_file.dex_file_offset_);
+          // Offset is from the dex file base.
+          header->data_off_ = vdex_dex_shared_data_offset_ - oat_dex_file.dex_file_offset_;
+          // The size should already be what part of the data buffer may be used by the dex.
+          CHECK_LE(header->data_size_, shared_data_size);
         }
       }
     }
-    vdex_size_ += shared_data_size;
-    size_dex_file_ += shared_data_size;
+    opened_dex_files_map->push_back(std::move(dex_files_map));
   } else {
     vdex_dex_shared_data_offset_ = vdex_size_;
   }
@@ -3331,86 +3326,35 @@
   raw_dex_files_.clear();
 }
 
-bool OatWriter::WriteDexFile(OutputStream* out,
-                             File* file,
+bool OatWriter::WriteDexFile(File* file,
                              OatDexFile* oat_dex_file,
                              bool update_input_vdex) {
-  if (!SeekToDexFile(out, file, oat_dex_file)) {
-    return false;
-  }
-  // update_input_vdex disables compact dex and layout.
-  if (profile_compilation_info_ != nullptr ||
-      compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone) {
-    CHECK(!update_input_vdex)
-        << "We should never update the input vdex when doing dexlayout or compact dex";
-    if (!LayoutAndWriteDexFile(out, oat_dex_file)) {
-      return false;
-    }
-  } else if (oat_dex_file->source_.IsZipEntry()) {
+  DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_);
+  if (oat_dex_file->source_.IsZipEntry()) {
     DCHECK(!update_input_vdex);
-    if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) {
+    if (!WriteDexFile(file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) {
       return false;
     }
   } else if (oat_dex_file->source_.IsRawFile()) {
     DCHECK(!update_input_vdex);
-    if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) {
+    if (!WriteDexFile(file, oat_dex_file, oat_dex_file->source_.GetRawFile())) {
       return false;
     }
   } else {
     DCHECK(oat_dex_file->source_.IsRawData());
-    if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData(), update_input_vdex)) {
+    const uint8_t* raw_data = oat_dex_file->source_.GetRawData();
+    if (!WriteDexFile(oat_dex_file, raw_data, update_input_vdex)) {
       return false;
     }
   }
 
   // Update current size and account for the written data.
-  DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_);
   vdex_size_ += oat_dex_file->dex_file_size_;
   size_dex_file_ += oat_dex_file->dex_file_size_;
   return true;
 }
 
-bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) {
-  // Dex files are required to be 4 byte aligned.
-  size_t initial_offset = vdex_size_;
-  size_t start_offset = RoundUp(initial_offset, 4);
-  size_dex_file_alignment_ += start_offset - initial_offset;
-
-  // Leave extra room for the quicken offset table offset.
-  start_offset += sizeof(VdexFile::QuickeningTableOffsetType);
-  // TODO: Not count the offset as part of alignment.
-  size_dex_file_alignment_ += sizeof(VdexFile::QuickeningTableOffsetType);
-
-  size_t file_offset = start_offset;
-
-  // Seek to the start of the dex file and flush any pending operations in the stream.
-  // Verify that, after flushing the stream, the file is at the same offset as the stream.
-  off_t actual_offset = out->Seek(file_offset, kSeekSet);
-  if (actual_offset != static_cast<off_t>(file_offset)) {
-    PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset
-                << " Expected: " << file_offset
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-  if (!out->Flush()) {
-    PLOG(ERROR) << "Failed to flush before writing dex file."
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-  actual_offset = lseek(file->Fd(), 0, SEEK_CUR);
-  if (actual_offset != static_cast<off_t>(file_offset)) {
-    PLOG(ERROR) << "Stream/file position mismatch! Actual: " << actual_offset
-                << " Expected: " << file_offset
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-
-  vdex_size_ = start_offset;
-  oat_dex_file->dex_file_offset_ = start_offset;
-  return true;
-}
-
-bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file) {
+bool OatWriter::LayoutDexFile(OatDexFile* oat_dex_file) {
   TimingLogger::ScopedTiming split("Dex Layout", timings_);
   std::string error_msg;
   std::string location(oat_dex_file->GetLocation());
@@ -3431,8 +3375,8 @@
     dex_file = dex_file_loader.Open(location,
                                     zip_entry->GetCrc32(),
                                     std::move(mem_map),
-                                    /* verify */ true,
-                                    /* verify_checksum */ true,
+                                    /*verify=*/ true,
+                                    /*verify_checksum=*/ true,
                                     &error_msg);
   } else if (oat_dex_file->source_.IsRawFile()) {
     File* raw_file = oat_dex_file->source_.GetRawFile();
@@ -3443,9 +3387,9 @@
     }
     TimingLogger::ScopedTiming extract("Open", timings_);
     dex_file = dex_file_loader.OpenDex(dup_fd, location,
-                                       /* verify */ true,
-                                       /* verify_checksum */ true,
-                                       /* mmap_shared */ false,
+                                       /*verify=*/ true,
+                                       /*verify_checksum=*/ true,
+                                       /*mmap_shared=*/ false,
                                        &error_msg);
   } else {
     // The source data is a vdex file.
@@ -3463,8 +3407,8 @@
                                     location,
                                     oat_dex_file->dex_file_location_checksum_,
                                     nullptr,
-                                    /* verify */ false,
-                                    /* verify_checksum */ false,
+                                    /*verify=*/ false,
+                                    /*verify_checksum=*/ false,
                                     &error_msg);
   }
   if (dex_file == nullptr) {
@@ -3475,7 +3419,6 @@
   options.compact_dex_level_ = compact_dex_level_;
   options.update_checksum_ = true;
   DexLayout dex_layout(options, profile_compilation_info_, /*file*/ nullptr, /*header*/ nullptr);
-  const uint8_t* dex_src = nullptr;
   {
     TimingLogger::ScopedTiming extract("ProcessDexFile", timings_);
     if (dex_layout.ProcessDexFile(location.c_str(),
@@ -3484,163 +3427,48 @@
                                   &dex_container_,
                                   &error_msg)) {
       oat_dex_file->dex_sections_layout_ = dex_layout.GetSections();
+      oat_dex_file->source_.SetDexLayoutData(dex_container_->GetMainSection()->ReleaseData());
       // Dex layout can affect the size of the dex file, so we update here what we have set
       // when adding the dex file as a source.
       const UnalignedDexFileHeader* header =
-          AsUnalignedDexFileHeader(dex_container_->GetMainSection()->Begin());
+          AsUnalignedDexFileHeader(oat_dex_file->source_.GetRawData());
       oat_dex_file->dex_file_size_ = header->file_size_;
-      dex_src = dex_container_->GetMainSection()->Begin();
     } else {
       LOG(WARNING) << "Failed to run dex layout, reason:" << error_msg;
       // Since we failed to convert the dex, just copy the input dex.
-      dex_src = dex_file->Begin();
+      if (dex_container_ != nullptr) {
+        // Clear the main section before processing next dex file in case we have written some data.
+        dex_container_->GetMainSection()->Clear();
+      }
     }
   }
-  {
-    TimingLogger::ScopedTiming extract("WriteDexFile", timings_);
-    if (!WriteDexFile(out, oat_dex_file, dex_src, /* update_input_vdex */ false)) {
-      return false;
-    }
-  }
-  if (dex_container_ != nullptr) {
-    // Clear the main section in case we write more data into the container.
-    dex_container_->GetMainSection()->Clear();
-  }
   CHECK_EQ(oat_dex_file->dex_file_location_checksum_, dex_file->GetLocationChecksum());
   return true;
 }
 
-bool OatWriter::WriteDexFile(OutputStream* out,
-                             File* file,
+bool OatWriter::WriteDexFile(File* file,
                              OatDexFile* oat_dex_file,
                              ZipEntry* dex_file) {
-  size_t start_offset = vdex_size_;
-  DCHECK_EQ(static_cast<off_t>(start_offset), out->Seek(0, kSeekCurrent));
+  uint8_t* raw_output = vdex_begin_ + oat_dex_file->dex_file_offset_;
 
-  // Extract the dex file and get the extracted size.
+  // Extract the dex file.
   std::string error_msg;
-  if (!dex_file->ExtractToFile(*file, &error_msg)) {
+  if (!dex_file->ExtractToMemory(raw_output, &error_msg)) {
     LOG(ERROR) << "Failed to extract dex file from ZIP entry: " << error_msg
                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
     return false;
   }
-  if (file->Flush() != 0) {
-    PLOG(ERROR) << "Failed to flush dex file from ZIP entry."
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-  off_t extracted_end = lseek(file->Fd(), 0, SEEK_CUR);
-  if (extracted_end == static_cast<off_t>(-1)) {
-    PLOG(ERROR) << "Failed get end offset after writing dex file from ZIP entry."
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-  if (extracted_end < static_cast<off_t>(start_offset)) {
-    LOG(ERROR) << "Dex file end position is before start position! End: " << extracted_end
-               << " Start: " << start_offset
-               << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-  uint64_t extracted_size = static_cast<uint64_t>(extracted_end - start_offset);
-  if (extracted_size < sizeof(DexFile::Header)) {
-    LOG(ERROR) << "Extracted dex file is shorter than dex file header. size: "
-               << extracted_size << " File: " << oat_dex_file->GetLocation();
-    return false;
-  }
-
-  // Read the dex file header and extract required data to OatDexFile.
-  off_t actual_offset = lseek(file->Fd(), start_offset, SEEK_SET);
-  if (actual_offset != static_cast<off_t>(start_offset)) {
-    PLOG(ERROR) << "Failed to seek back to dex file header. Actual: " << actual_offset
-                << " Expected: " << start_offset
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-  if (extracted_size < oat_dex_file->dex_file_size_) {
-    LOG(ERROR) << "Extracted truncated dex file. Extracted size: " << extracted_size
-               << " file size from header: " << oat_dex_file->dex_file_size_
-               << " File: " << oat_dex_file->GetLocation();
-    return false;
-  }
-
-  // Seek both file and stream to the end offset.
-  size_t end_offset = start_offset + oat_dex_file->dex_file_size_;
-  actual_offset = lseek(file->Fd(), end_offset, SEEK_SET);
-  if (actual_offset != static_cast<off_t>(end_offset)) {
-    PLOG(ERROR) << "Failed to seek to end of dex file. Actual: " << actual_offset
-                << " Expected: " << end_offset
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-  actual_offset = out->Seek(end_offset, kSeekSet);
-  if (actual_offset != static_cast<off_t>(end_offset)) {
-    PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset
-                << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation();
-    return false;
-  }
-  if (!out->Flush()) {
-    PLOG(ERROR) << "Failed to flush stream after seeking over dex file."
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-
-  // If we extracted more than the size specified in the header, truncate the file.
-  if (extracted_size > oat_dex_file->dex_file_size_) {
-    if (file->SetLength(end_offset) != 0) {
-      PLOG(ERROR) << "Failed to truncate excessive dex file length."
-                  << " File: " << oat_dex_file->GetLocation()
-                  << " Output: " << file->GetPath();
-      return false;
-    }
-  }
 
   return true;
 }
 
-bool OatWriter::WriteDexFile(OutputStream* out,
-                             File* file,
+bool OatWriter::WriteDexFile(File* file,
                              OatDexFile* oat_dex_file,
                              File* dex_file) {
-  size_t start_offset = vdex_size_;
-  DCHECK_EQ(static_cast<off_t>(start_offset), out->Seek(0, kSeekCurrent));
+  uint8_t* raw_output = vdex_begin_ + oat_dex_file->dex_file_offset_;
 
-  off_t input_offset = lseek(dex_file->Fd(), 0, SEEK_SET);
-  if (input_offset != static_cast<off_t>(0)) {
-    PLOG(ERROR) << "Failed to seek to dex file header. Actual: " << input_offset
-                << " Expected: 0"
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-
-  // Copy the input dex file using sendfile().
-  if (!file->Copy(dex_file, 0, oat_dex_file->dex_file_size_)) {
-    PLOG(ERROR) << "Failed to copy dex file to oat file."
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-  if (file->Flush() != 0) {
-    PLOG(ERROR) << "Failed to flush dex file."
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-
-  // Check file position and seek the stream to the end offset.
-  size_t end_offset = start_offset + oat_dex_file->dex_file_size_;
-  off_t actual_offset = lseek(file->Fd(), 0, SEEK_CUR);
-  if (actual_offset != static_cast<off_t>(end_offset)) {
-    PLOG(ERROR) << "Unexpected file position after copying dex file. Actual: " << actual_offset
-                << " Expected: " << end_offset
-                << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
-    return false;
-  }
-  actual_offset = out->Seek(end_offset, kSeekSet);
-  if (actual_offset != static_cast<off_t>(end_offset)) {
-    PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset
-                << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation();
-    return false;
-  }
-  if (!out->Flush()) {
-    PLOG(ERROR) << "Failed to flush stream after seeking over dex file."
+  if (!dex_file->PreadFully(raw_output, oat_dex_file->dex_file_size_, /*offset=*/ 0u)) {
+    PLOG(ERROR) << "Failed to copy dex file to vdex file."
                 << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
     return false;
   }
@@ -3648,29 +3476,20 @@
   return true;
 }
 
-bool OatWriter::WriteDexFile(OutputStream* out,
-                             OatDexFile* oat_dex_file,
+bool OatWriter::WriteDexFile(OatDexFile* oat_dex_file,
                              const uint8_t* dex_file,
                              bool update_input_vdex) {
   // Note: The raw data has already been checked to contain the header
   // and all the data that the header specifies as the file size.
   DCHECK(dex_file != nullptr);
   DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation()));
-  const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file);
+  DCHECK_EQ(oat_dex_file->dex_file_size_, AsUnalignedDexFileHeader(dex_file)->file_size_);
 
   if (update_input_vdex) {
     // The vdex already contains the dex code, no need to write it again.
   } else {
-    if (!out->WriteFully(dex_file, header->file_size_)) {
-      PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation()
-                  << " to " << out->GetLocation();
-      return false;
-    }
-    if (!out->Flush()) {
-      PLOG(ERROR) << "Failed to flush stream after writing dex file."
-                  << " File: " << oat_dex_file->GetLocation();
-      return false;
-    }
+    uint8_t* raw_output = vdex_begin_ + oat_dex_file->dex_file_offset_;
+    memcpy(raw_output, dex_file, oat_dex_file->dex_file_size_);
   }
   return true;
 }
@@ -3678,7 +3497,7 @@
 bool OatWriter::OpenDexFiles(
     File* file,
     bool verify,
-    /*out*/ std::vector<MemMap>* opened_dex_files_map,
+    /*inout*/ std::vector<MemMap>* opened_dex_files_map,
     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
   TimingLogger::ScopedTiming split("OpenDexFiles", timings_);
 
@@ -3688,6 +3507,7 @@
   }
 
   if (!extract_dex_files_into_vdex_) {
+    DCHECK_EQ(opened_dex_files_map->size(), 0u);
     std::vector<std::unique_ptr<const DexFile>> dex_files;
     std::vector<MemMap> maps;
     for (OatDexFile& oat_dex_file : oat_dex_files_) {
@@ -3725,37 +3545,20 @@
   // make it consistent with the case we're not writing the dex files, we close them now.
   CloseSources();
 
-  size_t map_offset = oat_dex_files_[0].dex_file_offset_;
-  size_t length = vdex_size_ - map_offset;
-
-  std::string error_msg;
-  MemMap dex_files_map = MemMap::MapFile(
-      length,
-      PROT_READ | PROT_WRITE,
-      MAP_SHARED,
-      file->Fd(),
-      map_offset,
-      /* low_4gb */ false,
-      file->GetPath().c_str(),
-      &error_msg);
-  if (!dex_files_map.IsValid()) {
-    LOG(ERROR) << "Failed to mmap() dex files from oat file. File: " << file->GetPath()
-               << " error: " << error_msg;
-    return false;
-  }
+  DCHECK_EQ(opened_dex_files_map->size(), 1u);
+  DCHECK(vdex_begin_ == opened_dex_files_map->front().Begin());
   const ArtDexFileLoader dex_file_loader;
   std::vector<std::unique_ptr<const DexFile>> dex_files;
   for (OatDexFile& oat_dex_file : oat_dex_files_) {
-    const uint8_t* raw_dex_file =
-        dex_files_map.Begin() + oat_dex_file.dex_file_offset_ - map_offset;
+    const uint8_t* raw_dex_file = vdex_begin_ + oat_dex_file.dex_file_offset_;
 
     if (kIsDebugBuild) {
       // Sanity check our input files.
       // Note that ValidateDexFileHeader() logs error messages.
       CHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation()))
           << "Failed to verify written dex file header!"
-          << " Output: " << file->GetPath() << " ~ " << std::hex << map_offset
-          << " ~ " << static_cast<const void*>(raw_dex_file);
+          << " Output: " << file->GetPath()
+          << " ~ " << std::hex << static_cast<const void*>(raw_dex_file);
 
       const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file);
       CHECK_EQ(header->file_size_, oat_dex_file.dex_file_size_)
@@ -3765,6 +3568,7 @@
     }
 
     // Now, open the dex file.
+    std::string error_msg;
     dex_files.emplace_back(dex_file_loader.Open(raw_dex_file,
                                                 oat_dex_file.dex_file_size_,
                                                 oat_dex_file.GetLocation(),
@@ -3784,7 +3588,6 @@
     oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_);
   }
 
-  opened_dex_files_map->push_back(std::move(dex_files_map));
   *opened_dex_files = std::move(dex_files);
   return true;
 }
@@ -3935,24 +3738,77 @@
   return true;
 }
 
-bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) {
-  // Write checksums
-  off_t checksums_offset = sizeof(VdexFile::VerifierDepsHeader);
-  off_t actual_offset = vdex_out->Seek(checksums_offset, kSeekSet);
-  if (actual_offset != checksums_offset) {
-    PLOG(ERROR) << "Failed to seek to the checksum location of vdex file. Actual: " << actual_offset
-                << " File: " << vdex_out->GetLocation();
+bool OatWriter::FinishVdexFile(File* vdex_file, verifier::VerifierDeps* verifier_deps) {
+  size_t old_vdex_size = vdex_size_;
+  std::vector<uint8_t> buffer;
+  buffer.reserve(64 * KB);
+  WriteVerifierDeps(verifier_deps, &buffer);
+  DCHECK_EQ(vdex_size_, old_vdex_size + buffer.size());
+  WriteQuickeningInfo(&buffer);
+  DCHECK_EQ(vdex_size_, old_vdex_size + buffer.size());
+
+  // Resize the vdex file.
+  if (vdex_file->SetLength(vdex_size_) != 0) {
+    PLOG(ERROR) << "Failed to resize vdex file " << vdex_file->GetPath();
     return false;
   }
 
-  for (size_t i = 0, size = oat_dex_files_.size(); i != size; ++i) {
-    OatDexFile* oat_dex_file = &oat_dex_files_[i];
-    if (!vdex_out->WriteFully(
-            &oat_dex_file->dex_file_location_checksum_, sizeof(VdexFile::VdexChecksum))) {
-      PLOG(ERROR) << "Failed to write dex file location checksum. File: "
-                  << vdex_out->GetLocation();
+  uint8_t* vdex_begin = vdex_begin_;
+  MemMap extra_map;
+  if (extract_dex_files_into_vdex_) {
+    DCHECK(vdex_begin != nullptr);
+    // Write data to the last already mmapped page of the vdex file.
+    size_t mmapped_vdex_size = RoundUp(old_vdex_size, kPageSize);
+    size_t first_chunk_size = std::min(buffer.size(), mmapped_vdex_size - old_vdex_size);
+    memcpy(vdex_begin + old_vdex_size, buffer.data(), first_chunk_size);
+
+    if (first_chunk_size != buffer.size()) {
+      size_t tail_size = buffer.size() - first_chunk_size;
+      std::string error_msg;
+      extra_map = MemMap::MapFile(
+          tail_size,
+          PROT_READ | PROT_WRITE,
+          MAP_SHARED,
+          vdex_file->Fd(),
+          /*start=*/ mmapped_vdex_size,
+          /*low_4gb=*/ false,
+          vdex_file->GetPath().c_str(),
+          &error_msg);
+      if (!extra_map.IsValid()) {
+        LOG(ERROR) << "Failed to mmap() vdex file tail. File: " << vdex_file->GetPath()
+                   << " error: " << error_msg;
+        return false;
+      }
+      memcpy(extra_map.Begin(), buffer.data() + first_chunk_size, tail_size);
+    }
+  } else {
+    DCHECK(vdex_begin == nullptr);
+    std::string error_msg;
+    extra_map = MemMap::MapFile(
+        vdex_size_,
+        PROT_READ | PROT_WRITE,
+        MAP_SHARED,
+        vdex_file->Fd(),
+        /*start=*/ 0u,
+        /*low_4gb=*/ false,
+        vdex_file->GetPath().c_str(),
+        &error_msg);
+    if (!extra_map.IsValid()) {
+      LOG(ERROR) << "Failed to mmap() vdex file. File: " << vdex_file->GetPath()
+                 << " error: " << error_msg;
       return false;
     }
+    vdex_begin = extra_map.Begin();
+    memcpy(vdex_begin + old_vdex_size, buffer.data(), buffer.size());
+  }
+
+  // Write checksums
+  off_t checksums_offset = sizeof(VdexFile::VerifierDepsHeader);
+  VdexFile::VdexChecksum* checksums_data =
+      reinterpret_cast<VdexFile::VdexChecksum*>(vdex_begin + checksums_offset);
+  for (size_t i = 0, size = oat_dex_files_.size(); i != size; ++i) {
+    OatDexFile* oat_dex_file = &oat_dex_files_[i];
+    checksums_data[i] = oat_dex_file->dex_file_location_checksum_;
     size_vdex_checksums_ += sizeof(VdexFile::VdexChecksum);
   }
 
@@ -3967,37 +3823,44 @@
     size_t dex_shared_data_size = vdex_verifier_deps_offset_ - vdex_dex_shared_data_offset_;
     size_t quickening_info_section_size = vdex_size_ - vdex_quickening_info_offset_;
 
-    VdexFile::DexSectionHeader dex_section_header(dex_section_size,
-                                                  dex_shared_data_size,
-                                                  quickening_info_section_size);
-    if (!vdex_out->WriteFully(&dex_section_header, sizeof(VdexFile::DexSectionHeader))) {
-      PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation();
-      return false;
-    }
+    void* dex_section_header_storage = checksums_data + oat_dex_files_.size();
+    new (dex_section_header_storage) VdexFile::DexSectionHeader(dex_section_size,
+                                                                dex_shared_data_size,
+                                                                quickening_info_section_size);
     size_vdex_header_ += sizeof(VdexFile::DexSectionHeader);
   }
 
-  // Write header.
-  actual_offset = vdex_out->Seek(0, kSeekSet);
-  if (actual_offset != 0) {
-    PLOG(ERROR) << "Failed to seek to the beginning of vdex file. Actual: " << actual_offset
-                << " File: " << vdex_out->GetLocation();
-    return false;
+  {
+    TimingLogger::ScopedTiming split("VDEX flush contents", timings_);
+    // Sync the data to the disk while the header is invalid. We do not want to end up with
+    // a valid header and invalid data if the process is suddenly killed.
+    if (extract_dex_files_into_vdex_) {
+      // Note: We passed the ownership of the vdex dex file MemMap to the caller,
+      // so we need to use msync() for the range explicitly.
+      if (msync(vdex_begin, RoundUp(old_vdex_size, kPageSize), MS_SYNC) != 0) {
+        PLOG(ERROR) << "Failed to sync vdex file contents" << vdex_file->GetPath();
+        return false;
+      }
+    }
+    if (extra_map.IsValid() && !extra_map.Sync()) {
+      PLOG(ERROR) << "Failed to sync vdex file contents" << vdex_file->GetPath();
+      return false;
+    }
   }
 
+  // Write header.
+  // TODO: Use `size_quickening_info_` instead of `verifier_deps_section_size` which
+  // includes `size_quickening_info_alignment_`, adjust code in VdexFile.
   size_t verifier_deps_section_size = vdex_quickening_info_offset_ - vdex_verifier_deps_offset_;
 
-  VdexFile::VerifierDepsHeader deps_header(
+  new (vdex_begin) VdexFile::VerifierDepsHeader(
       oat_dex_files_.size(), verifier_deps_section_size, has_dex_section);
-  if (!vdex_out->WriteFully(&deps_header, sizeof(VdexFile::VerifierDepsHeader))) {
-    PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation();
-    return false;
-  }
   size_vdex_header_ += sizeof(VdexFile::VerifierDepsHeader);
 
-  if (!vdex_out->Flush()) {
-    PLOG(ERROR) << "Failed to flush stream after writing to vdex file."
-                << " File: " << vdex_out->GetLocation();
+  // Note: If `extract_dex_files_into_vdex_`, we passed the ownership of the vdex dex file
+  // MemMap to the caller, so we need to use msync() for the range explicitly.
+  if (msync(vdex_begin, kPageSize, MS_SYNC) != 0) {
+    PLOG(ERROR) << "Failed to sync vdex file header " << vdex_file->GetPath();
     return false;
   }
 
@@ -4037,7 +3900,7 @@
                                   CreateTypeLookupTable create_type_lookup_table,
                                   uint32_t dex_file_location_checksum,
                                   size_t dex_file_size)
-    : source_(source),
+    : source_(std::move(source)),
       create_type_lookup_table_(create_type_lookup_table),
       dex_file_size_(dex_file_size),
       offset_(0),
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index f83f077..8d4f574 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -132,9 +132,7 @@
   // Then the user must call in order
   //   - WriteAndOpenDexFiles()
   //   - StartRoData()
-  //   - WriteVerifierDeps()
-  //   - WriteQuickeningInfo()
-  //   - WriteChecksumsAndVdexHeader()
+  //   - FinishVdexFile()
   //   - PrepareLayout(),
   //   - WriteRodata(),
   //   - WriteCode(),
@@ -185,9 +183,7 @@
   void Initialize(const CompilerDriver* compiler_driver,
                   ImageWriter* image_writer,
                   const std::vector<const DexFile*>& dex_files);
-  bool WriteQuickeningInfo(OutputStream* vdex_out);
-  bool WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps);
-  bool WriteChecksumsAndVdexHeader(OutputStream* vdex_out);
+  bool FinishVdexFile(File* vdex_file, verifier::VerifierDeps* verifier_deps);
 
   // Prepare layout of remaining data.
   void PrepareLayout(MultiOatRelativePatcher* relative_patcher);
@@ -291,32 +287,30 @@
 
   // If `update_input_vdex` is true, then this method won't actually write the dex files,
   // and the compiler will just re-use the existing vdex file.
-  bool WriteDexFiles(OutputStream* out,
-                     File* file,
+  bool WriteDexFiles(File* file,
                      bool update_input_vdex,
-                     CopyOption copy_dex_files);
-  bool WriteDexFile(OutputStream* out,
-                    File* file,
+                     CopyOption copy_dex_files,
+                     /*out*/ std::vector<MemMap>* opened_dex_files_map);
+  bool WriteDexFile(File* file,
                     OatDexFile* oat_dex_file,
                     bool update_input_vdex);
-  bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
-  bool LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file);
-  bool WriteDexFile(OutputStream* out,
-                    File* file,
+  bool LayoutDexFile(OatDexFile* oat_dex_file);
+  bool WriteDexFile(File* file,
                     OatDexFile* oat_dex_file,
                     ZipEntry* dex_file);
-  bool WriteDexFile(OutputStream* out,
-                    File* file,
+  bool WriteDexFile(File* file,
                     OatDexFile* oat_dex_file,
                     File* dex_file);
-  bool WriteDexFile(OutputStream* out,
-                    OatDexFile* oat_dex_file,
+  bool WriteDexFile(OatDexFile* oat_dex_file,
                     const uint8_t* dex_file,
                     bool update_input_vdex);
   bool OpenDexFiles(File* file,
                     bool verify,
-                    /*out*/ std::vector<MemMap>* opened_dex_files_map,
+                    /*inout*/ std::vector<MemMap>* opened_dex_files_map,
                     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
+  void WriteQuickeningInfo(/*out*/std::vector<uint8_t>* buffer);
+  void WriteVerifierDeps(verifier::VerifierDeps* verifier_deps,
+                         /*out*/std::vector<uint8_t>* buffer);
 
   size_t InitOatHeader(uint32_t num_dex_files, SafeMap<std::string, std::string>* key_value_store);
   size_t InitClassOffsets(size_t offset);
@@ -386,6 +380,8 @@
   ImageWriter* image_writer_;
   // Whether the dex files being compiled are going to be extracted to the vdex.
   bool extract_dex_files_into_vdex_;
+  // The start of the vdex file section mmapped for writing dex files.
+  uint8_t* vdex_begin_;
 
   // note OatFile does not take ownership of the DexFiles
   const std::vector<const DexFile*>* dex_files_;
@@ -485,6 +481,7 @@
   uint32_t size_vdex_header_;
   uint32_t size_vdex_checksums_;
   uint32_t size_dex_file_alignment_;
+  uint32_t size_quickening_table_offset_;
   uint32_t size_executable_offset_alignment_;
   uint32_t size_oat_header_;
   uint32_t size_oat_header_key_value_store_;
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 1b27bce..c82b7ad 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -212,15 +212,7 @@
                                       oat_writer.GetBssRootsOffset(),
                                       oat_writer.GetVdexSize());
 
-    std::unique_ptr<BufferedOutputStream> vdex_out =
-        std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file));
-    if (!oat_writer.WriteVerifierDeps(vdex_out.get(), nullptr)) {
-      return false;
-    }
-    if (!oat_writer.WriteQuickeningInfo(vdex_out.get())) {
-      return false;
-    }
-    if (!oat_writer.WriteChecksumsAndVdexHeader(vdex_out.get())) {
+    if (!oat_writer.FinishVdexFile(vdex_file, /*verifier_deps=*/ nullptr)) {
       return false;
     }
 
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
index 0bbb001..c51d709 100644
--- a/dexdump/Android.bp
+++ b/dexdump/Android.bp
@@ -65,4 +65,5 @@
         "art_gtest_defaults",
     ],
     srcs: ["dexdump_test.cc"],
+    required: ["dexdump"],
 }
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index cd40620..82fbf2b 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -15,6 +15,12 @@
 art_cc_defaults {
     name: "libart-dexlayout-defaults",
     defaults: ["art_defaults"],
+    visibility: [
+        // Visibility for prebuilt binaries from the prebuilt of this module.
+        // TODO(b/155921753): Restrict this when prebuilts are in their proper
+        // locations.
+        "//prebuilts:__subpackages__",
+    ],
     host_supported: true,
     srcs: [
         "compact_dex_writer.cc",
@@ -47,7 +53,7 @@
             ],
         },
     },
-    static_libs: ["libz"],
+    shared_libs: ["libz"],
 }
 
 cc_defaults {
@@ -230,6 +236,10 @@
         "libartd-dexlayout",
     ],
     srcs: ["dexlayout_test.cc"],
+    required: [
+        "dexlayoutd",
+        "dexdump",
+    ],
 }
 
 art_cc_binary {
@@ -265,4 +275,5 @@
         "art_gtest_defaults",
     ],
     srcs: ["dexdiag_test.cc"],
+    required: ["dexdiag"],
 }
diff --git a/dexlayout/dex_container.h b/dexlayout/dex_container.h
index 2d742b0..713de1b 100644
--- a/dexlayout/dex_container.h
+++ b/dexlayout/dex_container.h
@@ -46,6 +46,9 @@
     // Clear the container.
     virtual void Clear() = 0;
 
+    // Release the data, clearing the container contents.
+    virtual std::vector<uint8_t> ReleaseData() = 0;
+
     // Returns the end of the memory region.
     uint8_t* End() {
       return Begin() + Size();
@@ -73,6 +76,12 @@
       data_.clear();
     }
 
+    std::vector<uint8_t> ReleaseData() override {
+      std::vector<uint8_t> temp;
+      temp.swap(data_);
+      return temp;
+    }
+
    private:
     std::vector<uint8_t> data_;
   };
diff --git a/dexlist/Android.bp b/dexlist/Android.bp
index 860a5fd..136426b3 100644
--- a/dexlist/Android.bp
+++ b/dexlist/Android.bp
@@ -54,4 +54,5 @@
         "art_gtest_defaults",
     ],
     srcs: ["dexlist_test.cc"],
+    required: ["dexlist"],
 }
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
index 7875dbb..a5a7cca 100644
--- a/dexoptanalyzer/Android.bp
+++ b/dexoptanalyzer/Android.bp
@@ -71,4 +71,8 @@
         "libbacktrace",
     ],
     srcs: ["dexoptanalyzer_test.cc"],
+    required: [
+        "dex2oatd",
+        "dexoptanalyzerd",
+    ],
 }
diff --git a/disassembler/Android.bp b/disassembler/Android.bp
index a59b3c7..064aaea 100644
--- a/disassembler/Android.bp
+++ b/disassembler/Android.bp
@@ -68,6 +68,11 @@
         // For disassembler_arm*.
         "libvixld",
     ],
+
+    apex_available: [
+        "com.android.art.release",
+        "com.android.art.debug",
+    ],
 }
 
 cc_library_headers {
@@ -76,4 +81,9 @@
     export_include_dirs: [
         ".",
     ],
+
+    apex_available: [
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
diff --git a/dt_fd_forward/export/Android.bp b/dt_fd_forward/export/Android.bp
index c0c16fc..4039196 100644
--- a/dt_fd_forward/export/Android.bp
+++ b/dt_fd_forward/export/Android.bp
@@ -19,4 +19,9 @@
     export_include_dirs: ["."],
     host_supported: true,
     device_supported: true,
+
+    apex_available: [
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
diff --git a/imgdiag/Android.bp b/imgdiag/Android.bp
index 7c2f515..185e03e 100644
--- a/imgdiag/Android.bp
+++ b/imgdiag/Android.bp
@@ -85,4 +85,5 @@
         "art_gtest_defaults",
     ],
     srcs: ["imgdiag_test.cc"],
+    required: ["imgdiagd"],
 }
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 8e33752..00ee3b9 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -17,6 +17,12 @@
 cc_defaults {
     name: "libartbase_defaults",
     defaults: ["art_defaults"],
+    visibility: [
+        // Visibility for prebuilt binaries from the prebuilt of this module.
+        // TODO(b/155921753): Restrict this when prebuilts are in their proper
+        // locations.
+        "//prebuilts:__subpackages__",
+    ],
     host_supported: true,
     srcs: [
         "arch/instruction_set.cc",
@@ -56,9 +62,9 @@
             static_libs: [
                 // ZipArchive support, the order matters here to get all symbols.
                 "libziparchive",
-                "libz",
             ],
             shared_libs: [
+                "libz",
                 "liblog",
                 // For ashmem.
                 "libartpalette",
@@ -288,4 +294,9 @@
     export_include_dirs: ["."],
     shared_libs: ["libbase"],
     export_shared_lib_headers: ["libbase"],
+
+    apex_available: [
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h
index e98da24..5d54445 100644
--- a/libartbase/base/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -105,6 +105,7 @@
   // The least significant bit is stored in the smallest memory offset.
   template<typename Result = size_t>
   ATTRIBUTE_NO_SANITIZE_ADDRESS  // We might touch extra bytes due to the alignment.
+  ATTRIBUTE_NO_SANITIZE_HWADDRESS  // The hwasan uses different attribute.
   ALWAYS_INLINE Result LoadBits(size_t bit_offset, size_t bit_length) const {
     static_assert(std::is_integral<Result>::value, "Result must be integral");
     static_assert(std::is_unsigned<Result>::value, "Result must be unsigned");
diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc
index 62535a4..9c35dd4 100644
--- a/libartbase/base/common_art_test.cc
+++ b/libartbase/base/common_art_test.cc
@@ -256,9 +256,21 @@
 void CommonArtTestImpl::SetUp() {
   SetUpAndroidRootEnvVars();
   SetUpAndroidDataDir(android_data_);
+
+  // Re-use the data temporary directory for /system_ext tests
+  android_system_ext_.append(android_data_.c_str());
+  android_system_ext_.append("/system_ext");
+  int mkdir_result = mkdir(android_system_ext_.c_str(), 0700);
+  ASSERT_EQ(mkdir_result, 0);
+  setenv("ANDROID_SYSTEM_EXT", android_system_ext_.c_str(), 1);
+
+  std::string system_ext_framework = android_system_ext_ + "/framework";
+  mkdir_result = mkdir(system_ext_framework.c_str(), 0700);
+  ASSERT_EQ(mkdir_result, 0);
+
   dalvik_cache_.append(android_data_.c_str());
   dalvik_cache_.append("/dalvik-cache");
-  int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700);
+  mkdir_result = mkdir(dalvik_cache_.c_str(), 0700);
   ASSERT_EQ(mkdir_result, 0);
 
   static bool gSlowDebugTestFlag = false;
@@ -394,15 +406,19 @@
   ClearDirectory(dalvik_cache_.c_str());
   int rmdir_cache_result = rmdir(dalvik_cache_.c_str());
   ASSERT_EQ(0, rmdir_cache_result);
+  ClearDirectory(android_system_ext_.c_str(), true);
+  rmdir_cache_result = rmdir(android_system_ext_.c_str());
+  ASSERT_EQ(0, rmdir_cache_result);
   TearDownAndroidDataDir(android_data_, true);
   dalvik_cache_.clear();
+  android_system_ext_.clear();
 }
 
 static std::string GetDexFileName(const std::string& jar_prefix, bool host) {
   std::string prefix(host ? GetAndroidRoot() : "");
-  const char* apexPath = (jar_prefix == "conscrypt")
-    ? kAndroidConscryptApexDefaultPath
-    : kAndroidArtApexDefaultPath;
+  const char* apexPath = (jar_prefix == "conscrypt") ? kAndroidConscryptApexDefaultPath
+    : (jar_prefix == "core-icu4j" ? kAndroidI18nApexDefaultPath
+    : kAndroidArtApexDefaultPath);
   return StringPrintf("%s%s/javalib/%s.jar", prefix.c_str(), apexPath, jar_prefix.c_str());
 }
 
@@ -414,11 +430,11 @@
       // CORE_IMG_JARS modules.
       "core-oj",
       "core-libart",
-      "core-icu4j",
       "okhttp",
       "bouncycastle",
       "apache-xml",
       // Additional modules.
+      "core-icu4j",
       "conscrypt",
   };
 }
@@ -446,7 +462,8 @@
     std::string prefix = GetAndroidBuildTop();
     for (std::string& location : result) {
       CHECK_GT(location.size(), prefix.size());
-      CHECK_EQ(location.compare(0u, prefix.size(), prefix), 0);
+      CHECK_EQ(location.compare(0u, prefix.size(), prefix), 0)
+          << " prefix=" << prefix << " location=" << location;
       location.erase(0u, prefix.size());
     }
   }
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index 07d32d6..1db67e8 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -285,6 +285,7 @@
 
 
   std::string android_data_;
+  std::string android_system_ext_;
   std::string dalvik_cache_;
 
   virtual void SetUp();
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 98b69f3..152900a 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -68,10 +68,13 @@
 static constexpr const char* kApexDefaultPath = "/apex/";
 static constexpr const char* kAndroidRootEnvVar = "ANDROID_ROOT";
 static constexpr const char* kAndroidRootDefaultPath = "/system";
+static constexpr const char* kAndroidSystemExtRootEnvVar = "ANDROID_SYSTEM_EXT";
+static constexpr const char* kAndroidSystemExtRootDefaultPath = "/system_ext";
 static constexpr const char* kAndroidDataEnvVar = "ANDROID_DATA";
 static constexpr const char* kAndroidDataDefaultPath = "/data";
 static constexpr const char* kAndroidArtRootEnvVar = "ANDROID_ART_ROOT";
 static constexpr const char* kAndroidConscryptRootEnvVar = "ANDROID_CONSCRYPT_ROOT";
+static constexpr const char* kAndroidI18nRootEnvVar = "ANDROID_I18N_ROOT";
 
 // Get the "root" directory containing the "lib" directory where this instance
 // of the libartbase library (which contains `GetRootContainingLibartbase`) is
@@ -448,11 +451,23 @@
                             /* subdir= */ "framework/");
 }
 
+bool LocationIsOnSystemExtFramework(const char* full_path) {
+  return IsLocationOnModule(full_path,
+                            kAndroidSystemExtRootEnvVar,
+                            kAndroidSystemExtRootDefaultPath,
+                            /* subdir= */ "framework/");
+}
+
 bool LocationIsOnConscryptModule(const char* full_path) {
   return IsLocationOnModule(
       full_path, kAndroidConscryptRootEnvVar, kAndroidConscryptApexDefaultPath);
 }
 
+bool LocationIsOnI18nModule(const char* full_path) {
+  return IsLocationOnModule(
+      full_path, kAndroidI18nRootEnvVar, kAndroidI18nApexDefaultPath);
+}
+
 bool LocationIsOnApex(const char* full_path) {
   return android::base::StartsWith(full_path, kApexDefaultPath);
 }
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index b1d044a..bcab288 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -29,6 +29,7 @@
 
 static constexpr const char* kAndroidArtApexDefaultPath = "/apex/com.android.art";
 static constexpr const char* kAndroidConscryptApexDefaultPath = "/apex/com.android.conscrypt";
+static constexpr const char* kAndroidI18nApexDefaultPath = "/apex/com.android.i18n";
 
 // These methods return the Android Root, which is the historical location of
 // the Android "system" directory, containing the built Android artifacts. On
@@ -103,12 +104,18 @@
 // Return whether the location is on /apex/com.android.conscrypt
 bool LocationIsOnConscryptModule(const char* location);
 
+// Return whether the location is on /apex/com.android.i18n
+bool LocationIsOnI18nModule(const char* location);
+
 // Return whether the location is on system (i.e. android root).
 bool LocationIsOnSystem(const char* location);
 
 // Return whether the location is on system/framework (i.e. android_root/framework).
 bool LocationIsOnSystemFramework(const char* location);
 
+// Return whether the location is on system_ext/framework
+bool LocationIsOnSystemExtFramework(const char* location);
+
 // Return whether the location is on /apex/.
 bool LocationIsOnApex(const char* location);
 
diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h
index a9a903b..1c17385 100644
--- a/libartbase/base/hiddenapi_flags.h
+++ b/libartbase/base/hiddenapi_flags.h
@@ -97,11 +97,12 @@
     kGreylistMaxO = 3,
     kGreylistMaxP = 4,
     kGreylistMaxQ = 5,
+    kGreylistMaxR = 6,
 
     // Special values
     kInvalid =      (static_cast<uint32_t>(-1) & kValueBitMask),
     kMin =          kWhitelist,
-    kMax =          kGreylistMaxQ,
+    kMax =          kGreylistMaxR,
   };
 
   // Additional bit flags after the first kValueBitSize bits in dex flags.
@@ -137,6 +138,7 @@
     "greylist-max-o",
     "greylist-max-p",
     "greylist-max-q",
+    "greylist-max-r",
   };
 
   // Names corresponding to DomainApis.
@@ -153,6 +155,7 @@
     /* greylist-max-o */ SdkVersion::kO_MR1,
     /* greylist-max-p */ SdkVersion::kP,
     /* greylist-max-q */ SdkVersion::kQ,
+    /* greylist-max-r */ SdkVersion::kR,
   };
 
   explicit ApiList(Value val, uint32_t domain_apis = 0u)
@@ -194,6 +197,7 @@
   static ApiList GreylistMaxO() { return ApiList(Value::kGreylistMaxO); }
   static ApiList GreylistMaxP() { return ApiList(Value::kGreylistMaxP); }
   static ApiList GreylistMaxQ() { return ApiList(Value::kGreylistMaxQ); }
+  static ApiList GreylistMaxR() { return ApiList(Value::kGreylistMaxR); }
   static ApiList CorePlatformApi() { return ApiList(DomainApi::kCorePlatformApi); }
   static ApiList TestApi() { return ApiList(DomainApi::kTestApi); }
 
diff --git a/libartbase/base/memory_tool.h b/libartbase/base/memory_tool.h
index 1a6a9bb..5ed9cda 100644
--- a/libartbase/base/memory_tool.h
+++ b/libartbase/base/memory_tool.h
@@ -66,6 +66,24 @@
 
 #endif
 
+#if __has_feature(hwaddress_sanitizer)
+# define HWADDRESS_SANITIZER
+# define ATTRIBUTE_NO_SANITIZE_HWADDRESS __attribute__((no_sanitize("hwaddress")))
+#else
+# define ATTRIBUTE_NO_SANITIZE_HWADDRESS
+#endif
+
+// Removes the hwasan tag from the pointer (the top eight bits).
+// Those bits are used for verification by hwasan and they are ignored by normal ARM memory ops.
+template<typename PtrType>
+static inline PtrType* HWASanUntag(PtrType* p) {
+#if __has_feature(hwaddress_sanitizer) && defined(__aarch64__)
+  return reinterpret_cast<PtrType*>(reinterpret_cast<uintptr_t>(p) & ((1ULL << 56) - 1));
+#else
+  return p;
+#endif
+}
+
 }  // namespace art
 
 #endif  // ART_LIBARTBASE_BASE_MEMORY_TOOL_H_
diff --git a/libartbase/base/sdk_version.h b/libartbase/base/sdk_version.h
index 219ac86..4a253d2 100644
--- a/libartbase/base/sdk_version.h
+++ b/libartbase/base/sdk_version.h
@@ -34,6 +34,7 @@
   kO_MR1 = 27u,
   kP     = 28u,
   kQ     = 29u,
+  kR     = 30u,
   kMax   = std::numeric_limits<uint32_t>::max(),
 };
 
diff --git a/libartbase/base/utils_test.cc b/libartbase/base/utils_test.cc
index 631a225..e24794b 100644
--- a/libartbase/base/utils_test.cc
+++ b/libartbase/base/utils_test.cc
@@ -108,7 +108,7 @@
 }
 
 TEST_F(UtilsTest, GetProcessStatus) {
-  EXPECT_EQ("utils_test", GetProcessStatus("Name"));
+  EXPECT_EQ("art_libartbase_", GetProcessStatus("Name"));
   EXPECT_EQ("R (running)", GetProcessStatus("State"));
   EXPECT_EQ("<unknown>", GetProcessStatus("tate"));
   EXPECT_EQ("<unknown>", GetProcessStatus("e"));
diff --git a/libartbase/base/zip_archive.cc b/libartbase/base/zip_archive.cc
index c899039..6abcdc5 100644
--- a/libartbase/base/zip_archive.cc
+++ b/libartbase/base/zip_archive.cc
@@ -60,7 +60,7 @@
 
 bool ZipEntry::ExtractToFile(File& file, std::string* error_msg) {
   const int32_t error = ExtractEntryToFile(handle_, zip_entry_, file.Fd());
-  if (error) {
+  if (error != 0) {
     *error_msg = std::string(ErrorCodeString(error));
     return false;
   }
@@ -84,15 +84,23 @@
     return MemMap::Invalid();
   }
 
-  const int32_t error = ExtractToMemory(handle_, zip_entry_, map.Begin(), map.Size());
-  if (error) {
-    *error_msg = std::string(ErrorCodeString(error));
+  DCHECK_EQ(map.Size(), GetUncompressedLength());
+  if (!ExtractToMemory(map.Begin(), error_msg)) {
     return MemMap::Invalid();
   }
 
   return map;
 }
 
+bool ZipEntry::ExtractToMemory(/*out*/uint8_t* buffer, /*out*/std::string* error_msg) {
+  const int32_t error = ::ExtractToMemory(handle_, zip_entry_, buffer, GetUncompressedLength());
+  if (error != 0) {
+    *error_msg = std::string(ErrorCodeString(error));
+    return false;
+  }
+  return true;
+}
+
 MemMap ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* error_msg) {
   const int zip_fd = GetFileDescriptor(handle_);
   const char* entry_filename = entry_name_.c_str();
@@ -227,7 +235,7 @@
 
   ZipArchiveHandle handle;
   const int32_t error = OpenArchive(filename, &handle);
-  if (error) {
+  if (error != 0) {
     *error_msg = std::string(ErrorCodeString(error));
     CloseArchive(handle);
     return nullptr;
@@ -243,7 +251,7 @@
 
   ZipArchiveHandle handle;
   const int32_t error = OpenArchiveFd(fd, filename, &handle);
-  if (error) {
+  if (error != 0) {
     *error_msg = std::string(ErrorCodeString(error));
     CloseArchive(handle);
     return nullptr;
@@ -259,7 +267,7 @@
   // Resist the urge to delete the space. <: is a bigraph sequence.
   std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
   const int32_t error = FindEntry(handle_, name, zip_entry.get());
-  if (error) {
+  if (error != 0) {
     *error_msg = std::string(ErrorCodeString(error));
     return nullptr;
   }
diff --git a/libartbase/base/zip_archive.h b/libartbase/base/zip_archive.h
index fc04ec1..a4e56b5 100644
--- a/libartbase/base/zip_archive.h
+++ b/libartbase/base/zip_archive.h
@@ -41,12 +41,17 @@
 
 class ZipEntry {
  public:
-  bool ExtractToFile(File& file, std::string* error_msg);
+  // Extracts this entry to file.
+  // Returns true on success, false on failure.
+  bool ExtractToFile(File& file, /*out*/std::string* error_msg);
   // Extract this entry to anonymous memory (R/W).
   // Returns null on failure and sets error_msg.
   MemMap ExtractToMemMap(const char* zip_filename,
                          const char* entry_filename,
-                         std::string* error_msg);
+                         /*out*/std::string* error_msg);
+  // Extracts this entry to memory. Stores `GetUncompressedSize()` bytes on success.
+  // Returns true on success, false on failure.
+  bool ExtractToMemory(/*out*/uint8_t* buffer, /*out*/std::string* error_msg);
   // Create a file-backed private (clean, R/W) memory mapping to this entry.
   // 'zip_filename' is used for diagnostics only,
   //   the original file that the ZipArchive was open with is used
diff --git a/libartpalette/Android.bp b/libartpalette/Android.bp
index b4b2e0b..1ccc5e4 100644
--- a/libartpalette/Android.bp
+++ b/libartpalette/Android.bp
@@ -17,6 +17,12 @@
 cc_defaults {
     name: "libartpalette_defaults",
     defaults: ["art_defaults"],
+    visibility: [
+        // Visibility for prebuilt binaries from the prebuilt of this module.
+        // TODO(b/155921753): Restrict this when prebuilts are in their proper
+        // locations.
+        "//prebuilts:__subpackages__",
+    ],
     host_supported: true,
     export_include_dirs: ["include"],
 }
@@ -34,12 +40,13 @@
 art_cc_library {
     name: "libartpalette",
     defaults: ["libartpalette_defaults"],
-    required: ["libartpalette-system"], // libartpalette.so dlopen()'s libartpalette-system.
     header_libs: ["libbase_headers"],
     target: {
         // Targets supporting dlopen build the client library which loads
         // and binds the methods in the libartpalette-system library.
         android: {
+            // libartpalette.so dlopen()'s libartpalette-system.
+            required: ["libartpalette-system"],
             srcs: ["apex/palette.cc"],
             shared_libs: ["liblog"],
             version_script: "libartpalette.map.txt",
@@ -101,5 +108,4 @@
     host_supported: true,
     srcs: ["apex/palette_test.cc"],
     shared_libs: ["libartpalette"],
-    test_per_src: true,
 }
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index 4f29689..c73908d 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -17,6 +17,12 @@
 cc_defaults {
     name: "libdexfile_defaults",
     defaults: ["art_defaults"],
+    visibility: [
+        // Visibility for prebuilt dex2oat(d) from the prebuilt of this module.
+        // TODO(b/155921753): Restrict this when prebuilts are in their proper
+        // locations.
+        "//prebuilts:__subpackages__",
+    ],
     host_supported: true,
     srcs: [
         "dex/art_dex_file_loader.cc",
@@ -43,9 +49,10 @@
         android: {
             static_libs: [
                 "libziparchive",
-                "libz",
             ],
             shared_libs: [
+                // libz provides a stub from platform, shouldn't be statically linked
+                "libz",
                 // For MemMap.
                 "libartpalette",
                 "liblog",
@@ -263,6 +270,12 @@
             enabled: true,
         },
     },
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 // Make dex_instruction_list.h available for tools/jvmti-agents/titrace
@@ -271,6 +284,11 @@
     visibility: ["//art:__subpackages__"],
     host_supported: true,
     export_include_dirs: ["."],
+
+    apex_available: [
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 cc_defaults {
@@ -339,7 +357,6 @@
 art_cc_test {
     name: "art_libdexfile_external_tests",
     host_supported: true,
-    test_per_src: true, // For consistency with other ART gtests.
     test_suites: ["general-tests"],
     srcs: [
         "external/dex_file_ext_c_test.c",
@@ -368,11 +385,16 @@
     shared_libs: ["liblog"],
     header_libs: ["libdexfile_external_headers"],
     export_header_lib_headers: ["libdexfile_external_headers"],
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
-// The same source file is used in two tests here, so unlike other ART gtests it
-// doesn't use test_per_src. Its test target is
-// test-art-{host,target}-gtest-art_libdexfile_support_tests.
+// The same source file is used in two tests here.
+// Its test target is test-art-{host,target}-gtest-art_libdexfile_support_tests.
 art_cc_test {
     name: "art_libdexfile_support_tests",
     defaults: [
@@ -439,9 +461,8 @@
     ],
 }
 
-// The same source file is used in two tests here, so unlike other ART gtests it
-// doesn't use test_per_src. Its test target is
-// test-art-{host,target}-gtest-art_libdexfile_support_static_tests.
+// The same source file is used in two tests here.
+// Its test target is test-art-{host,target}-gtest-art_libdexfile_support_static_tests.
 art_cc_test {
     name: "art_libdexfile_support_static_tests",
     host_supported: true,
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index ae04612..e05771a 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -19,6 +19,12 @@
 
     host_supported: true,
     export_include_dirs: ["include"],
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 cc_library {
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 50bf0ab..fe70a06 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -80,7 +80,6 @@
         "art_defaults",
         "art_test_defaults",
     ],
-    test_per_src: true,
     // TODO(mast): Split up art_gtest_defaults so that it can be used for the
     // following without pulling in lots of libs.
     target: {
@@ -146,7 +145,6 @@
         "art_defaults",
         "art_test_defaults",
     ],
-    test_per_src: true,
     // TODO(mast): Split up art_gtest_defaults so that it can be used for the
     // following without pulling in lots of libs.
     target: {
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 4024d48..645cf64 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -71,6 +71,11 @@
 
 cc_library_headers {
     name: "libnativeloader-headers",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
     visibility: [
         "//art:__subpackages__",
         // TODO(b/133140750): Clean this up.
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
index 57b9001..c5ace61 100644
--- a/libnativeloader/README.md
+++ b/libnativeloader/README.md
@@ -34,7 +34,7 @@
 [APEX](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md),
 some libraries are no longer provided from platform, but from the APEXes which
 have their own linker namespaces. For example, ICU libraries `libicuuc.so` and
-`libicui18n.so` are from the runtime APEX.
+`libicui18n.so` are from the I18n APEX.
 
 The list of public native libraries is not static. The default set of libraries
 are defined in AOSP, but partners can extend it to include their own libraries.
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index d42a4b5..14ba721 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -51,6 +51,7 @@
 constexpr const char* kVndkNamespaceName = "vndk";
 constexpr const char* kVndkProductNamespaceName = "vndk_product";
 constexpr const char* kArtNamespaceName = "com_android_art";
+constexpr const char* kI18nNamespaceName = "com_android_i18n";
 constexpr const char* kNeuralNetworksNamespaceName = "com_android_neuralnetworks";
 constexpr const char* kStatsdNamespaceName = "com_android_os_statsd";
 
@@ -272,6 +273,15 @@
     }
   }
 
+  auto i18n_ns = NativeLoaderNamespace::GetExportedNamespace(kI18nNamespaceName, is_bridged);
+  // i18n APEX does not exist on host, and under certain build conditions.
+  if (i18n_ns.ok()) {
+    linked = app_ns->Link(*i18n_ns, i18n_public_libraries());
+    if (!linked.ok()) {
+      return linked.error();
+    }
+  }
+
   // Give access to NNAPI libraries (apex-updated LLNDK library).
   auto nnapi_ns =
       NativeLoaderNamespace::GetExportedNamespace(kNeuralNetworksNamespaceName, is_bridged);
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index 66d7531..e64e1a5 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -97,7 +97,7 @@
 static std::unordered_map<std::string, Platform::mock_namespace_handle> namespaces = {
     {"system", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("system"))},
     {"default", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("default"))},
-    {"com_android_art", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("com_android_art"))},
+    {"com_android_i18n", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("com_android_i18n"))},
     {"sphal", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("sphal"))},
     {"vndk", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk"))},
     {"vndk_product", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk_product"))},
@@ -355,6 +355,7 @@
   std::string expected_parent_namespace = "system";
   bool expected_link_with_platform_ns = true;
   bool expected_link_with_art_ns = true;
+  bool expected_link_with_i18n_ns = true;
   bool expected_link_with_sphal_ns = !vendor_public_libraries().empty();
   bool expected_link_with_vndk_ns = false;
   bool expected_link_with_vndk_product_ns = false;
@@ -363,6 +364,7 @@
   bool expected_link_with_statsd_ns = true;
   std::string expected_shared_libs_to_platform_ns = default_public_libraries();
   std::string expected_shared_libs_to_art_ns = art_public_libraries();
+  std::string expected_shared_libs_to_i18n_ns = i18n_public_libraries();
   std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries();
   std::string expected_shared_libs_to_vndk_ns = vndksp_libraries_vendor();
   std::string expected_shared_libs_to_vndk_product_ns = vndksp_libraries_product();
@@ -393,6 +395,11 @@
                                               StrEq(expected_shared_libs_to_art_ns)))
           .WillOnce(Return(true));
     }
+    if (expected_link_with_i18n_ns) {
+      EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("com_android_i18n"),
+                                              StrEq(expected_shared_libs_to_i18n_ns)))
+          .WillOnce(Return(true));
+    }
     if (expected_link_with_sphal_ns) {
       EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("sphal"),
                                               StrEq(expected_shared_libs_to_sphal_ns)))
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index 4b56dc2..575ce2d 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -57,11 +57,15 @@
 constexpr const char* kVndkLibrariesFile = "/apex/com.android.vndk.v{}/etc/vndksp.libraries.{}.txt";
 
 const std::vector<const std::string> kArtApexPublicLibraries = {
+    "libnativehelper.so",
+};
+
+const std::vector<const std::string> ki18nApexPublicLibraries = {
     "libicuuc.so",
     "libicui18n.so",
 };
 
-constexpr const char* kArtApexLibPath = "/apex/com.android.art/" LIB;
+constexpr const char* kI18nApexLibPath = "/apex/com.android.i18n/" LIB;
 
 constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so";
 
@@ -194,14 +198,14 @@
     return android::base::Join(*sonames, ':');
   }
 
-  // Remove the public libs in the art namespace.
+  // Remove the public libs in the i18n namespace.
   // These libs are listed in public.android.txt, but we don't want the rest of android
   // in default namespace to dlopen the libs.
   // For example, libicuuc.so is exposed to classloader namespace from art namespace.
   // Unfortunately, it does not have stable C symbols, and default namespace should only use
   // stable symbols in libandroidicu.so. http://b/120786417
-  for (const std::string& lib_name : kArtApexPublicLibraries) {
-    std::string path(kArtApexLibPath);
+  for (const std::string& lib_name : ki18nApexPublicLibraries) {
+    std::string path(kI18nApexLibPath);
     path.append("/").append(lib_name);
 
     struct stat s;
@@ -236,6 +240,12 @@
   return list;
 }
 
+static std::string InitI18nPublicLibraries() {
+  static_assert(sizeof(ki18nApexPublicLibraries) > 0, "ki18nApexPublicLibraries is empty");
+  std::string list = android::base::Join(ki18nApexPublicLibraries, ":");
+  return list;
+}
+
 static std::string InitVendorPublicLibraries() {
   // This file is optional, quietly ignore if the file does not exist.
   auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
@@ -348,6 +358,11 @@
   return list;
 }
 
+const std::string& i18n_public_libraries() {
+  static std::string list = InitI18nPublicLibraries();
+  return list;
+}
+
 const std::string& vendor_public_libraries() {
   static std::string list = InitVendorPublicLibraries();
   return list;
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
index 9b8b2a4..b60a2ef 100644
--- a/libnativeloader/public_libraries.h
+++ b/libnativeloader/public_libraries.h
@@ -36,6 +36,7 @@
 const std::string& statsd_public_libraries();
 const std::string& vendor_public_libraries();
 const std::string& extended_public_libraries();
+const std::string& i18n_public_libraries();
 const std::string& neuralnetworks_public_libraries();
 const std::string& llndk_libraries_product();
 const std::string& llndk_libraries_vendor();
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index 72e8c0f..60c9f3c 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -77,7 +77,6 @@
         "art_defaults",
         "art_test_defaults",
     ],
-    test_per_src: true,
     srcs: [
         "api_test.c",
     ],
diff --git a/libprofile/Android.bp b/libprofile/Android.bp
index 367eefc..23f3241 100644
--- a/libprofile/Android.bp
+++ b/libprofile/Android.bp
@@ -17,6 +17,12 @@
 cc_defaults {
     name: "libprofile_defaults",
     defaults: ["art_defaults"],
+    visibility: [
+        // Visibility for prebuilt binaries from the prebuilt of this module.
+        // TODO(b/155921753): Restrict this when prebuilts are in their proper
+        // locations.
+        "//prebuilts:__subpackages__",
+    ],
     host_supported: true,
     srcs: [
         "profile/profile_boot_info.cc",
@@ -27,11 +33,11 @@
             shared_libs: [
                 "libartpalette",
                 "libbase",
+                "libz",
             ],
             static_libs: [
                 // ZipArchive support, the order matters here to get all symbols.
                 "libziparchive",
-                "libz",
             ],
             export_shared_lib_headers: ["libbase"],
         },
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index e36d9d7..2817c1b 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -143,4 +143,11 @@
         "oatdump_test.cc",
         "oatdump_image_test.cc",
     ],
+    required: [
+        "oatdumpd",
+        "oatdumpds",
+        "dexdump",
+        "dex2oatd",
+        "dex2oatds"
+    ],
 }
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 3acea42..1f843b3 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -44,7 +44,7 @@
 #include "base/unix_file/fd_file.h"
 #include "class_linker-inl.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "compiled_method.h"
 #include "debug/debug_info.h"
 #include "debug/elf_debug_writer.h"
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index a32e738..dca0481 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -21,6 +21,12 @@
     export_header_lib_headers: ["jni_headers"],
     export_include_dirs: ["include"],
     sdk_version: "current",
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 cc_defaults {
diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc
index ec267155..a480ac0 100644
--- a/openjdkjvmti/ti_class_definition.cc
+++ b/openjdkjvmti/ti_class_definition.cc
@@ -34,7 +34,7 @@
 #include "base/array_slice.h"
 #include "base/logging.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/dex_file.h"
 #include "fixed_up_dex_file.h"
 #include "handle.h"
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index 974a710..905ed95 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -29,7 +29,6 @@
 #include "base/mutex.h"
 #include "base/utils.h"
 #include "class_linker.h"
-#include "class_root.h"
 #include "deopt_manager.h"
 #include "dex/primitive.h"
 #include "events-inl.h"
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index a6b597b..05d198b 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -61,7 +61,7 @@
 #include "base/utils.h"
 #include "class_linker-inl.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "class_status.h"
 #include "debugger.h"
 #include "dex/art_dex_file_loader.h"
diff --git a/perfetto_hprof/perfetto_hprof.cc b/perfetto_hprof/perfetto_hprof.cc
index 33fc73e..6ba3bb4 100644
--- a/perfetto_hprof/perfetto_hprof.cc
+++ b/perfetto_hprof/perfetto_hprof.cc
@@ -699,6 +699,12 @@
     if (!runtime->AttachCurrentThread("perfetto_hprof_listener", /*as_daemon=*/ true,
                                       runtime->GetSystemThreadGroup(), /*create_peer=*/ false)) {
       LOG(ERROR) << "failed to attach thread.";
+      {
+        art::MutexLock lk(nullptr, GetStateMutex());
+        g_state = State::kUninitialized;
+        GetStateCV().Broadcast(nullptr);
+      }
+
       return;
     }
     art::Thread* self = art::Thread::Current();
@@ -733,10 +739,6 @@
   });
   th.detach();
 
-  art::MutexLock lk(art::Thread::Current(), GetStateMutex());
-  while (g_state == State::kWaitForListener) {
-    GetStateCV().Wait(art::Thread::Current());
-  }
   return true;
 }
 
@@ -751,10 +753,14 @@
 
   art::Thread* self = art::Thread::Current();
   art::MutexLock lk(self, GetStateMutex());
-  if (g_state != State::kWaitForListener) {
-    g_state = State::kUninitialized;
-    GetStateCV().Broadcast(self);
+  // Wait until after the thread was registered to the runtime. This is so
+  // we do not attempt to register it with the runtime after it had been torn
+  // down (ArtPlugin_Deinitialize gets called in the Runtime dtor).
+  while (g_state == State::kWaitForListener) {
+    GetStateCV().Wait(art::Thread::Current());
   }
+  g_state = State::kUninitialized;
+  GetStateCV().Broadcast(self);
   return true;
 }
 
diff --git a/profman/Android.bp b/profman/Android.bp
index c574fde..a493056 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -99,4 +99,5 @@
         "libprofiled",
     ],
     srcs: ["profile_assistant_test.cc"],
+    required: ["profmand"],
 }
diff --git a/runtime/Android.bp b/runtime/Android.bp
index db2579b..9edd80f 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -60,6 +60,12 @@
 libart_cc_defaults {
     name: "libart_defaults",
     defaults: ["art_defaults"],
+    visibility: [
+        // Visibility for prebuilt binaries from the prebuilt of this module.
+        // TODO(b/155921753): Restrict this when prebuilts are in their proper
+        // locations.
+        "//prebuilts:__subpackages__",
+    ],
     host_supported: true,
     srcs: [
         "aot_class_linker.cc",
@@ -297,8 +303,9 @@
         arm64: {
             srcs: [
                 "interpreter/mterp/mterp.cc",
-                "interpreter/mterp/nterp_stub.cc",
+                "interpreter/mterp/nterp.cc",
                 ":libart_mterp.arm64",
+                ":libart_mterp.arm64ng",
                 "arch/arm64/context_arm64.cc",
                 "arch/arm64/entrypoints_init_arm64.cc",
                 "arch/arm64/jni_entrypoints_arm64.S",
@@ -366,8 +373,6 @@
             ],
             shared_libs: [
                 "libdl_android",
-            ],
-            static_libs: [
                 "libz", // For adler32.
             ],
         },
@@ -674,6 +679,7 @@
         "verifier/method_verifier_test.cc",
         "verifier/reg_type_test.cc",
     ],
+    required: ["dex2oatd"],
     shared_libs: [
         "libbacktrace",
     ],
@@ -691,6 +697,7 @@
         "reflection_test.cc",
         "module_exclusion_test.cc",
     ],
+    required: ["dex2oatd"],
     shared_libs: [
         "libartd-compiler",
         "libvixld",
@@ -702,6 +709,11 @@
     host_supported: true,
     export_include_dirs: ["."],
     sdk_version: "current",
+
+    apex_available: [
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 genrule {
@@ -762,3 +774,18 @@
     ],
     cmd: "$(location interpreter/mterp/gen_mterp.py) $(out) $(in)",
 }
+
+genrule {
+    name: "libart_mterp.arm64ng",
+    out: ["mterp_arm64ng.S"],
+    srcs: [
+        "interpreter/mterp/arm64ng/*.S",
+        "interpreter/mterp/arm64/arithmetic.S",
+        "interpreter/mterp/arm64/floating_point.S",
+    ],
+    tool_files: [
+        "interpreter/mterp/gen_mterp.py",
+        "interpreter/mterp/common/gen_setup.py",
+    ],
+    cmd: "$(location interpreter/mterp/gen_mterp.py) $(out) $(in)",
+}
diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S
index b1e5c86..fd5c852 100644
--- a/runtime/arch/arm64/asm_support_arm64.S
+++ b/runtime/arch/arm64/asm_support_arm64.S
@@ -263,6 +263,22 @@
     DECREASE_FRAME FRAME_SIZE_SAVE_REFS_AND_ARGS
 .endm
 
+.macro SAVE_ALL_CALLEE_SAVES offset
+    // FP callee-saves.
+    stp d8, d9,   [sp, #(0 + \offset)]
+    stp d10, d11, [sp, #(16 + \offset)]
+    stp d12, d13, [sp, #(32 + \offset)]
+    stp d14, d15, [sp, #(48 + \offset)]
+
+    // GP callee-saves
+    SAVE_TWO_REGS x19, x20, (64 + \offset)
+    SAVE_TWO_REGS x21, x22, (80 + \offset)
+    SAVE_TWO_REGS x23, x24, (96 + \offset)
+    SAVE_TWO_REGS x25, x26, (112 + \offset)
+    SAVE_TWO_REGS x27, x28, (128 + \offset)
+    SAVE_TWO_REGS x29, xLR, (144 + \offset)
+.endm
+
     /*
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kSaveAllCalleeSaves)
@@ -283,19 +299,7 @@
 #endif
 
     // Stack alignment filler [sp, #8].
-    // FP callee-saves.
-    stp d8, d9,   [sp, #16]
-    stp d10, d11, [sp, #32]
-    stp d12, d13, [sp, #48]
-    stp d14, d15, [sp, #64]
-
-    // GP callee-saves
-    SAVE_TWO_REGS x19, x20, 80
-    SAVE_TWO_REGS x21, x22, 96
-    SAVE_TWO_REGS x23, x24, 112
-    SAVE_TWO_REGS x25, x26, 128
-    SAVE_TWO_REGS x27, x28, 144
-    SAVE_TWO_REGS x29, xLR, 160
+    SAVE_ALL_CALLEE_SAVES 16
 
     // Store ArtMethod* Runtime::callee_save_methods_[kSaveAllCalleeSaves].
     str xIP0, [sp]
diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h
index 57376d0..887ee02 100644
--- a/runtime/arch/arm64/asm_support_arm64.h
+++ b/runtime/arch/arm64/asm_support_arm64.h
@@ -19,7 +19,9 @@
 
 #include "asm_support.h"
 
-#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVES 176
+#define CALLEE_SAVES_SIZE (12 * 8 + 8 * 8)
+// +8 for the ArtMethod, +8 for alignment.
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVES (CALLEE_SAVES_SIZE + 16)
 #define FRAME_SIZE_SAVE_REFS_ONLY 96
 #define FRAME_SIZE_SAVE_REFS_AND_ARGS 224
 #define FRAME_SIZE_SAVE_EVERYTHING 512
diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h
index 95dac90..5ab63c0 100644
--- a/runtime/arch/arm64/context_arm64.h
+++ b/runtime/arch/arm64/context_arm64.h
@@ -46,6 +46,10 @@
     SetGPR(kPC, new_lr);
   }
 
+  void SetNterpDexPC(uintptr_t dex_pc_ptr) override {
+    SetGPR(X22, dex_pc_ptr);
+  }
+
   void SetArg0(uintptr_t new_arg0_value) override {
     SetGPR(X0, new_arg0_value);
   }
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 2b47cef..772681d 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -21,7 +21,7 @@
 #include "base/callee_save_type.h"
 #include "base/enums.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_runtime_test.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "imt_conflict_table.h"
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index 0c3d26e..858c320 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -97,6 +97,12 @@
 X86FeaturesUniquePtr X86InstructionSetFeatures::FromVariant(
     const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED,
     bool x86_64) {
+  const bool is_runtime_isa =
+      kRuntimeISA == (x86_64 ? InstructionSet::kX86_64 : InstructionSet::kX86);
+  if (is_runtime_isa && variant == "default") {
+    return FromCppDefines(x86_64);
+  }
+
   bool has_SSSE3 = FindVariantInArray(x86_variants_with_ssse3, arraysize(x86_variants_with_ssse3),
                                       variant);
   bool has_SSE4_1 = FindVariantInArray(x86_variants_with_sse4_1,
diff --git a/runtime/arch/x86/instruction_set_features_x86_test.cc b/runtime/arch/x86/instruction_set_features_x86_test.cc
index cdf15af..ce8e9f4 100644
--- a/runtime/arch/x86/instruction_set_features_x86_test.cc
+++ b/runtime/arch/x86/instruction_set_features_x86_test.cc
@@ -21,15 +21,18 @@
 namespace art {
 
 TEST(X86InstructionSetFeaturesTest, X86FeaturesFromDefaultVariant) {
+  const bool is_runtime_isa = kRuntimeISA == InstructionSet::kX86;
   std::string error_msg;
   std::unique_ptr<const InstructionSetFeatures> x86_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kX86, "default", &error_msg));
   ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
   EXPECT_EQ(x86_features->GetInstructionSet(), InstructionSet::kX86);
   EXPECT_TRUE(x86_features->Equals(x86_features.get()));
-  EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
-               x86_features->GetFeatureString().c_str());
-  EXPECT_EQ(x86_features->AsBitmap(), 0U);
+  EXPECT_EQ(x86_features->GetFeatureString(),
+            is_runtime_isa ? X86InstructionSetFeatures::FromCppDefines()->GetFeatureString()
+                    : "-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt");
+  EXPECT_EQ(x86_features->AsBitmap(),
+            is_runtime_isa ? X86InstructionSetFeatures::FromCppDefines()->AsBitmap() : 0);
 }
 
 TEST(X86InstructionSetFeaturesTest, X86FeaturesFromAtomVariant) {
@@ -44,16 +47,6 @@
                x86_features->GetFeatureString().c_str());
   EXPECT_EQ(x86_features->AsBitmap(), 1U);
 
-  // Build features for a 32-bit x86 default processor.
-  std::unique_ptr<const InstructionSetFeatures> x86_default_features(
-      InstructionSetFeatures::FromVariant(InstructionSet::kX86, "default", &error_msg));
-  ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg;
-  EXPECT_EQ(x86_default_features->GetInstructionSet(), InstructionSet::kX86);
-  EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get()));
-  EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
-               x86_default_features->GetFeatureString().c_str());
-  EXPECT_EQ(x86_default_features->AsBitmap(), 0U);
-
   // Build features for a 64-bit x86-64 atom processor.
   std::unique_ptr<const InstructionSetFeatures> x86_64_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "atom", &error_msg));
@@ -65,8 +58,6 @@
   EXPECT_EQ(x86_64_features->AsBitmap(), 1U);
 
   EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
-  EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get()));
-  EXPECT_FALSE(x86_features->Equals(x86_default_features.get()));
 }
 
 TEST(X86InstructionSetFeaturesTest, X86FeaturesFromSandybridgeVariant) {
@@ -81,16 +72,6 @@
                x86_features->GetFeatureString().c_str());
   EXPECT_EQ(x86_features->AsBitmap(), 39U);
 
-  // Build features for a 32-bit x86 default processor.
-  std::unique_ptr<const InstructionSetFeatures> x86_default_features(
-      InstructionSetFeatures::FromVariant(InstructionSet::kX86, "default", &error_msg));
-  ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg;
-  EXPECT_EQ(x86_default_features->GetInstructionSet(), InstructionSet::kX86);
-  EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get()));
-  EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
-               x86_default_features->GetFeatureString().c_str());
-  EXPECT_EQ(x86_default_features->AsBitmap(), 0U);
-
   // Build features for a 64-bit x86-64 sandybridge processor.
   std::unique_ptr<const InstructionSetFeatures> x86_64_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "sandybridge", &error_msg));
@@ -102,8 +83,6 @@
   EXPECT_EQ(x86_64_features->AsBitmap(), 39U);
 
   EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
-  EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get()));
-  EXPECT_FALSE(x86_features->Equals(x86_default_features.get()));
 }
 
 TEST(X86InstructionSetFeaturesTest, X86FeaturesFromSilvermontVariant) {
@@ -118,16 +97,6 @@
                x86_features->GetFeatureString().c_str());
   EXPECT_EQ(x86_features->AsBitmap(), 39U);
 
-  // Build features for a 32-bit x86 default processor.
-  std::unique_ptr<const InstructionSetFeatures> x86_default_features(
-      InstructionSetFeatures::FromVariant(InstructionSet::kX86, "default", &error_msg));
-  ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg;
-  EXPECT_EQ(x86_default_features->GetInstructionSet(), InstructionSet::kX86);
-  EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get()));
-  EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
-               x86_default_features->GetFeatureString().c_str());
-  EXPECT_EQ(x86_default_features->AsBitmap(), 0U);
-
   // Build features for a 64-bit x86-64 silvermont processor.
   std::unique_ptr<const InstructionSetFeatures> x86_64_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "silvermont", &error_msg));
@@ -139,8 +108,6 @@
   EXPECT_EQ(x86_64_features->AsBitmap(), 39U);
 
   EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
-  EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get()));
-  EXPECT_FALSE(x86_features->Equals(x86_default_features.get()));
 }
 
 TEST(X86InstructionSetFeaturesTest, X86FeaturesFromKabylakeVariant) {
@@ -155,16 +122,6 @@
                x86_features->GetFeatureString().c_str());
   EXPECT_EQ(x86_features->AsBitmap(), 63U);
 
-  // Build features for a 32-bit x86 default processor.
-  std::unique_ptr<const InstructionSetFeatures> x86_default_features(
-      InstructionSetFeatures::FromVariant(InstructionSet::kX86, "default", &error_msg));
-  ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg;
-  EXPECT_EQ(x86_default_features->GetInstructionSet(), InstructionSet::kX86);
-  EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get()));
-  EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
-               x86_default_features->GetFeatureString().c_str());
-  EXPECT_EQ(x86_default_features->AsBitmap(), 0U);
-
   // Build features for a 64-bit x86-64 kabylake processor.
   std::unique_ptr<const InstructionSetFeatures> x86_64_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "kabylake", &error_msg));
@@ -176,7 +133,5 @@
   EXPECT_EQ(x86_64_features->AsBitmap(), 63U);
 
   EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
-  EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get()));
-  EXPECT_FALSE(x86_features->Equals(x86_default_features.get()));
-  }
+}
 }  // namespace art
diff --git a/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc b/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc
index 2b307da..3201050 100644
--- a/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc
+++ b/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc
@@ -21,15 +21,18 @@
 namespace art {
 
 TEST(X86_64InstructionSetFeaturesTest, X86Features) {
+  const bool is_runtime_isa = kRuntimeISA == InstructionSet::kX86_64;
   std::string error_msg;
   std::unique_ptr<const InstructionSetFeatures> x86_64_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "default", &error_msg));
   ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
   EXPECT_EQ(x86_64_features->GetInstructionSet(), InstructionSet::kX86_64);
   EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
-  EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
-               x86_64_features->GetFeatureString().c_str());
-  EXPECT_EQ(x86_64_features->AsBitmap(), 0U);
+  EXPECT_EQ(x86_64_features->GetFeatureString(),
+            is_runtime_isa ? X86_64InstructionSetFeatures::FromCppDefines()->GetFeatureString()
+                    : "-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt");
+  EXPECT_EQ(x86_64_features->AsBitmap(),
+            is_runtime_isa ? X86_64InstructionSetFeatures::FromCppDefines()->AsBitmap() : 0);
 }
 
 }  // namespace art
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index d0b6fde..a2a45ce 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -26,7 +26,7 @@
 #include "base/enums.h"
 #include "base/stl_util.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "debugger.h"
 #include "dex/class_accessor-inl.h"
 #include "dex/descriptors_names.h"
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 8f8f4fd..bbf59bc 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -57,7 +57,7 @@
 #include "cha.h"
 #include "class_linker-inl.h"
 #include "class_loader_utils.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "class_table-inl.h"
 #include "compiler_callbacks.h"
 #include "debug_print.h"
@@ -1133,8 +1133,10 @@
     if (!c->IsArrayClass() && !c->IsPrimitive()) {
       StackHandleScope<1> hs(self);
       Handle<mirror::Class> h_class(hs.NewHandle(c));
-      EnsureInitialized(self, h_class, true, true);
-      self->AssertNoPendingException();
+      if (!EnsureInitialized(self, h_class, true, true)) {
+        LOG(FATAL) << "Exception when initializing " << h_class->PrettyClass()
+            << ": " << self->GetException()->Dump();
+      }
     } else {
       DCHECK(c->IsInitialized());
     }
@@ -6430,6 +6432,15 @@
       ArtMethod* m = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);
       m->SetMethodIndex(i);
       if (!m->IsAbstract()) {
+        // If the dex file does not support default methods, throw ClassFormatError.
+        // This check is necessary to protect from odd cases, such as native default
+        // methods, that the dex file verifier permits for old dex file versions. b/157170505
+        if (!m->GetDexFile()->SupportsDefaultMethods()) {
+          ThrowClassFormatError(klass.Get(),
+                                "Dex file does not support default method '%s'",
+                                m->PrettyMethod().c_str());
+          return false;
+        }
         m->SetAccessFlags(m->GetAccessFlags() | kAccDefault);
         has_defaults = true;
       }
@@ -8779,8 +8790,13 @@
       // We normaly should not end up here. However the verifier currently doesn't guarantee
       // the invariant of having the klass in the class table. b/73760543
       klass = ResolveType(method_id.class_idx_, dex_cache, class_loader);
-      DCHECK(!Thread::Current()->IsExceptionPending())
-          << Thread::Current()->GetException()->Dump();
+      if (klass == nullptr) {
+        // This can only happen if the current thread is not allowed to load
+        // classes.
+        DCHECK(!Thread::Current()->CanLoadClasses());
+        DCHECK(Thread::Current()->IsExceptionPending());
+        return nullptr;
+      }
     }
   } else {
     // The method was not in the DexCache, resolve the declaring class.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 931f6df..a7561de 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -26,7 +26,7 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_runtime_test.h"
 #include "dex/dex_file_types.h"
 #include "dex/signature-inl.h"
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index d2119db..fe78ba0 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -26,7 +26,7 @@
 #include "base/stl_util.h"
 #include "class_linker.h"
 #include "class_loader_utils.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index e2e6075..ff466c0 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -23,7 +23,7 @@
 #include "base/dchecked_vector.h"
 #include "base/stl_util.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_runtime_test.h"
 #include "dex/dex_file.h"
 #include "handle_scope-inl.h"
diff --git a/runtime/class_root-inl.h b/runtime/class_root-inl.h
new file mode 100644
index 0000000..d88b8e1
--- /dev/null
+++ b/runtime/class_root-inl.h
@@ -0,0 +1,99 @@
+/*
+ * 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_CLASS_ROOT_INL_H_
+#define ART_RUNTIME_CLASS_ROOT_INL_H_
+
+#include "class_root.h"
+
+#include "class_linker-inl.h"
+#include "mirror/class.h"
+#include "mirror/object_array-inl.h"
+#include "obj_ptr-inl.h"
+#include "runtime.h"
+
+namespace art {
+
+template <ReadBarrierOption kReadBarrierOption>
+inline ObjPtr<mirror::Class> GetClassRoot(ClassRoot class_root,
+                                          ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots) {
+  DCHECK(class_roots != nullptr);
+  if (kReadBarrierOption == kWithReadBarrier) {
+    // With read barrier all references must point to the to-space.
+    // Without read barrier, this check could fail.
+    DCHECK_EQ(class_roots, Runtime::Current()->GetClassLinker()->GetClassRoots());
+  }
+  DCHECK_LT(static_cast<uint32_t>(class_root), static_cast<uint32_t>(ClassRoot::kMax));
+  int32_t index = static_cast<int32_t>(class_root);
+  ObjPtr<mirror::Class> klass =
+      class_roots->GetWithoutChecks<kDefaultVerifyFlags, kReadBarrierOption>(index);
+  DCHECK(klass != nullptr);
+  return klass;
+}
+
+template <ReadBarrierOption kReadBarrierOption>
+inline ObjPtr<mirror::Class> GetClassRoot(ClassRoot class_root, ClassLinker* linker)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return GetClassRoot<kReadBarrierOption>(class_root, linker->GetClassRoots<kReadBarrierOption>());
+}
+
+template <ReadBarrierOption kReadBarrierOption>
+inline ObjPtr<mirror::Class> GetClassRoot(ClassRoot class_root)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return GetClassRoot<kReadBarrierOption>(class_root, Runtime::Current()->GetClassLinker());
+}
+
+namespace detail {
+
+class ClassNotFoundExceptionTag;
+template <class Tag> struct NoMirrorType;
+
+template <class MirrorType>
+struct ClassRootSelector;  // No definition for unspecialized ClassRoot selector.
+
+#define SPECIALIZE_CLASS_ROOT_SELECTOR(name, descriptor, mirror_type) \
+  template <>                                                         \
+  struct ClassRootSelector<mirror_type> {                             \
+    static constexpr ClassRoot value = ClassRoot::name;               \
+  };
+
+CLASS_ROOT_LIST(SPECIALIZE_CLASS_ROOT_SELECTOR)
+
+#undef SPECIALIZE_CLASS_ROOT_SELECTOR
+
+}  // namespace detail
+
+template <class MirrorType, ReadBarrierOption kReadBarrierOption>
+inline ObjPtr<mirror::Class> GetClassRoot(ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return GetClassRoot<kReadBarrierOption>(detail::ClassRootSelector<MirrorType>::value,
+                                          class_roots);
+}
+
+template <class MirrorType, ReadBarrierOption kReadBarrierOption>
+inline ObjPtr<mirror::Class> GetClassRoot(ClassLinker* linker)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return GetClassRoot<kReadBarrierOption>(detail::ClassRootSelector<MirrorType>::value, linker);
+}
+
+template <class MirrorType, ReadBarrierOption kReadBarrierOption>
+inline ObjPtr<mirror::Class> GetClassRoot() REQUIRES_SHARED(Locks::mutator_lock_) {
+  return GetClassRoot<kReadBarrierOption>(detail::ClassRootSelector<MirrorType>::value);
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_CLASS_ROOT_INL_H_
diff --git a/runtime/class_root.cc b/runtime/class_root.cc
index 08820b0..6a6fd26 100644
--- a/runtime/class_root.cc
+++ b/runtime/class_root.cc
@@ -16,10 +16,12 @@
 
 #include "class_root.h"
 
+#include "base/logging.h"
+
 namespace art {
 
 const char* GetClassRootDescriptor(ClassRoot class_root) {
-  static const char* class_roots_descriptors[] = {
+  static const char* const class_roots_descriptors[] = {
 #define CLASS_ROOT_DESCRIPTOR(name, descriptor, mirror_type) descriptor,
       CLASS_ROOT_LIST(CLASS_ROOT_DESCRIPTOR)
 #undef CLASS_ROOT_DESCRIPTOR
diff --git a/runtime/class_root.h b/runtime/class_root.h
index 835ec90..85e074c 100644
--- a/runtime/class_root.h
+++ b/runtime/class_root.h
@@ -17,20 +17,22 @@
 #ifndef ART_RUNTIME_CLASS_ROOT_H_
 #define ART_RUNTIME_CLASS_ROOT_H_
 
-#include "class_linker-inl.h"
-#include "gc_root-inl.h"
-#include "mirror/class.h"
-#include "mirror/object_array-inl.h"
-#include "obj_ptr-inl.h"
-#include "runtime.h"
+#include <stdint.h>
+
+#include "base/locks.h"
+#include "read_barrier_option.h"
 
 namespace art {
 
+class ClassLinker;
+template<class MirrorType> class ObjPtr;
+
 namespace mirror {
 class ArrayElementVarHandle;
 class ByteArrayViewVarHandle;
 class ByteBufferViewVarHandle;
 class CallSite;
+class Class;
 class ClassExt;
 class ClassLoader;
 class Constructor;
@@ -43,6 +45,7 @@
 class MethodHandlesLookup;
 class MethodType;
 class Object;
+template<class T> class ObjectArray;
 class Proxy;
 template<typename T> class PrimitiveArray;
 class Reference;
@@ -121,72 +124,26 @@
 const char* GetClassRootDescriptor(ClassRoot class_root);
 
 template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-inline ObjPtr<mirror::Class> GetClassRoot(
-    ClassRoot class_root,
-    ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots) REQUIRES_SHARED(Locks::mutator_lock_) {
-  DCHECK(class_roots != nullptr);
-  if (kReadBarrierOption == kWithReadBarrier) {
-    // With read barrier all references must point to the to-space.
-    // Without read barrier, this check could fail.
-    DCHECK_EQ(class_roots, Runtime::Current()->GetClassLinker()->GetClassRoots());
-  }
-  DCHECK_LT(static_cast<uint32_t>(class_root), static_cast<uint32_t>(ClassRoot::kMax));
-  int32_t index = static_cast<int32_t>(class_root);
-  ObjPtr<mirror::Class> klass =
-      class_roots->GetWithoutChecks<kDefaultVerifyFlags, kReadBarrierOption>(index);
-  DCHECK(klass != nullptr);
-  return klass;
-}
+ObjPtr<mirror::Class> GetClassRoot(ClassRoot class_root,
+                                   ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 
 template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-inline ObjPtr<mirror::Class> GetClassRoot(ClassRoot class_root, ClassLinker* linker)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  return GetClassRoot<kReadBarrierOption>(class_root, linker->GetClassRoots<kReadBarrierOption>());
-}
+ObjPtr<mirror::Class> GetClassRoot(ClassRoot class_root, ClassLinker* linker)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 
 template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-inline ObjPtr<mirror::Class> GetClassRoot(ClassRoot class_root)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  return GetClassRoot<kReadBarrierOption>(class_root, Runtime::Current()->GetClassLinker());
-}
-
-namespace detail {
-
-class ClassNotFoundExceptionTag;
-template <class Tag> struct NoMirrorType;
-
-template <class MirrorType>
-struct ClassRootSelector;  // No definition for unspecialized ClassRoot selector.
-
-#define SPECIALIZE_CLASS_ROOT_SELECTOR(name, descriptor, mirror_type) \
-  template <>                                                         \
-  struct ClassRootSelector<mirror_type> {                             \
-    static constexpr ClassRoot value = ClassRoot::name;               \
-  };
-
-CLASS_ROOT_LIST(SPECIALIZE_CLASS_ROOT_SELECTOR)
-
-#undef SPECIALIZE_CLASS_ROOT_SELECTOR
-
-}  // namespace detail
+ObjPtr<mirror::Class> GetClassRoot(ClassRoot class_root) REQUIRES_SHARED(Locks::mutator_lock_);
 
 template <class MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-inline ObjPtr<mirror::Class> GetClassRoot(ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  return GetClassRoot<kReadBarrierOption>(detail::ClassRootSelector<MirrorType>::value,
-                                          class_roots);
-}
+ObjPtr<mirror::Class> GetClassRoot(ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 
 template <class MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-inline ObjPtr<mirror::Class> GetClassRoot(ClassLinker* linker)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  return GetClassRoot<kReadBarrierOption>(detail::ClassRootSelector<MirrorType>::value, linker);
-}
+ObjPtr<mirror::Class> GetClassRoot(ClassLinker* linker) REQUIRES_SHARED(Locks::mutator_lock_);
 
 template <class MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-inline ObjPtr<mirror::Class> GetClassRoot() REQUIRES_SHARED(Locks::mutator_lock_) {
-  return GetClassRoot<kReadBarrierOption>(detail::ClassRootSelector<MirrorType>::value);
-}
+ObjPtr<mirror::Class> GetClassRoot() REQUIRES_SHARED(Locks::mutator_lock_);
 
 }  // namespace art
 
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index a846346..c4e83b9 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -650,25 +650,3 @@
 }
 
 }  // namespace art
-
-// Allow other test code to run global initialization/configuration before
-// gtest infra takes over.
-extern "C"
-__attribute__((visibility("default"))) __attribute__((weak))
-void ArtTestGlobalInit() {
-}
-
-int main(int argc, char **argv) {
-  // Gtests can be very noisy. For example, an executable with multiple tests will trigger native
-  // bridge warnings. The following line reduces the minimum log severity to ERROR and suppresses
-  // everything else. In case you want to see all messages, comment out the line.
-  setenv("ANDROID_LOG_TAGS", "*:e", 1);
-
-  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);
-  ArtTestGlobalInit();
-  return RUN_ALL_TESTS();
-}
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 1c9cf18..5a7944c 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -831,7 +831,7 @@
     ScopedLocalRef<jobject> stack_state_val(env, nullptr);
     {
       ScopedObjectAccessUnchecked soa(env);  // TODO: Is this necessary?
-      stack_state_val.reset(soa.Self()->CreateInternalStackTrace<false>(soa));
+      stack_state_val.reset(soa.Self()->CreateInternalStackTrace(soa));
     }
     if (stack_state_val != nullptr) {
       env->SetObjectField(exc.get(),
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index 24b3a3e..6261a93 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -24,7 +24,7 @@
 #include "art_method-inl.h"
 #include "base/sdk_version.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_instruction-inl.h"
 #include "jni/jni_internal.h"
@@ -525,21 +525,13 @@
         PointerSize pointer_size = class_linker->GetImagePointerSize();
         set_object = true;
         if (method->IsConstructor()) {
-          if (pointer_size == PointerSize::k64) {
-            element_object = mirror::Constructor::CreateFromArtMethod<PointerSize::k64,
-                kTransactionActive>(self, method);
-          } else {
-            element_object = mirror::Constructor::CreateFromArtMethod<PointerSize::k32,
-                kTransactionActive>(self, method);
-          }
+          element_object = (pointer_size == PointerSize::k64)
+              ? mirror::Constructor::CreateFromArtMethod<PointerSize::k64>(self, method)
+              : mirror::Constructor::CreateFromArtMethod<PointerSize::k32>(self, method);
         } else {
-          if (pointer_size == PointerSize::k64) {
-            element_object = mirror::Method::CreateFromArtMethod<PointerSize::k64,
-                kTransactionActive>(self, method);
-          } else {
-            element_object = mirror::Method::CreateFromArtMethod<PointerSize::k32,
-                kTransactionActive>(self, method);
-          }
+          element_object = (pointer_size == PointerSize::k64)
+              ? mirror::Method::CreateFromArtMethod<PointerSize::k64>(self, method)
+              : mirror::Method::CreateFromArtMethod<PointerSize::k32>(self, method);
         }
         if (element_object == nullptr) {
           return false;
@@ -561,14 +553,7 @@
           return false;
         }
         set_object = true;
-        PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-        if (pointer_size == PointerSize::k64) {
-          element_object = mirror::Field::CreateFromArtField<PointerSize::k64,
-              kTransactionActive>(self, field, true);
-        } else {
-          element_object = mirror::Field::CreateFromArtField<PointerSize::k32,
-              kTransactionActive>(self, field, true);
-        }
+        element_object = mirror::Field::CreateFromArtField(self, field, true);
         if (element_object == nullptr) {
           return false;
         }
@@ -743,15 +728,9 @@
   ObjPtr<mirror::Class> annotation_member_class =
       WellKnownClasses::ToClass(WellKnownClasses::libcore_reflect_AnnotationMember);
   Handle<mirror::Object> new_member(hs.NewHandle(annotation_member_class->AllocObject(self)));
-  ObjPtr<mirror::Method> method_obj_ptr;
-  DCHECK(!Runtime::Current()->IsActiveTransaction());
-  if (pointer_size == PointerSize::k64) {
-    method_obj_ptr = mirror::Method::CreateFromArtMethod<PointerSize::k64, false>(
-        self, annotation_method);
-  } else {
-    method_obj_ptr = mirror::Method::CreateFromArtMethod<PointerSize::k32, false>(
-        self, annotation_method);
-  }
+  ObjPtr<mirror::Method> method_obj_ptr = (pointer_size == PointerSize::k64)
+      ? mirror::Method::CreateFromArtMethod<PointerSize::k64>(self, annotation_method)
+      : mirror::Method::CreateFromArtMethod<PointerSize::k32>(self, annotation_method);
   Handle<mirror::Method> method_object(hs.NewHandle(method_obj_ptr));
 
   if (new_member == nullptr || string_name == nullptr ||
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index dba4ecc..bf8a756 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -211,7 +211,7 @@
   entry_points_instrumented = instrumented;
 }
 
-void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints, bool is_marking) {
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) {
 #if !defined(__APPLE__) || !defined(__LP64__)
   switch (entry_points_allocator) {
     case gc::kAllocatorTypeDlMalloc: {
@@ -239,12 +239,7 @@
     }
     case gc::kAllocatorTypeRegionTLAB: {
       CHECK(kMovingCollector);
-      if (is_marking) {
-        SetQuickAllocEntryPoints_region_tlab(qpoints, entry_points_instrumented);
-      } else {
-        // Not marking means we need no read barriers and can just use the normal TLAB case.
-        SetQuickAllocEntryPoints_tlab(qpoints, entry_points_instrumented);
-      }
+      SetQuickAllocEntryPoints_region_tlab(qpoints, entry_points_instrumented);
       return;
     }
     default:
@@ -252,7 +247,6 @@
   }
 #else
   UNUSED(qpoints);
-  UNUSED(is_marking);
 #endif
   UNIMPLEMENTED(FATAL);
   UNREACHABLE();
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.h b/runtime/entrypoints/quick/quick_alloc_entrypoints.h
index 937ba8e..c4d8a80 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.h
@@ -23,9 +23,7 @@
 
 namespace art {
 
-// is_marking is only used for CC, if the GC is marking the allocation entrypoint is the marking
-// one.
-void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints, bool is_marking);
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
 
 // Runtime shutdown lock is necessary to prevent races in thread initialization. When the thread is
 // starting it doesn't hold the mutator lock until after it has been added to the thread list.
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index a77bb85..ae9bdd6 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -32,7 +32,7 @@
   jpoints->pDlsymLookupCritical = art_jni_dlsym_lookup_critical_stub;
 
   // Alloc
-  ResetQuickAllocEntryPoints(qpoints, /* is_marking= */ true);
+  ResetQuickAllocEntryPoints(qpoints);
 
   // Resolution and initialization
   qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index b04f26e..8508086 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -19,7 +19,7 @@
 #include "base/enums.h"
 #include "callee_save_frame.h"
 #include "common_throws.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "debug_print.h"
 #include "debugger.h"
 #include "dex/dex_file-inl.h"
@@ -845,7 +845,7 @@
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
   DCHECK(!Runtime::Current()->IsActiveTransaction());
   ObjPtr<mirror::Method> interface_reflect_method =
-      mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), interface_method);
+      mirror::Method::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), interface_method);
   if (interface_reflect_method == nullptr) {
     soa.Self()->AssertPendingOOMException();
     return 0;
@@ -1374,35 +1374,29 @@
                                << invoke_type << " " << orig_called->GetVtableIndex();
     }
 
+    // Static invokes need class initialization check but instance invokes can proceed even if
+    // the class is erroneous, i.e. in the edge case of escaping instances of erroneous classes.
+    bool success = true;
     ObjPtr<mirror::Class> called_class = called->GetDeclaringClass();
     if (NeedsClinitCheckBeforeCall(called) && !called_class->IsVisiblyInitialized()) {
       // Ensure that the called method's class is initialized.
       StackHandleScope<1> hs(soa.Self());
       HandleWrapperObjPtr<mirror::Class> h_called_class(hs.NewHandleWrapper(&called_class));
-      linker->EnsureInitialized(soa.Self(), h_called_class, true, true);
+      success = linker->EnsureInitialized(soa.Self(), h_called_class, true, true);
     }
-    bool force_interpreter = self->IsForceInterpreter() && !called->IsNative();
-    if (called_class->IsInitialized() || called_class->IsInitializing()) {
-      if (UNLIKELY(force_interpreter)) {
-        // If we are single-stepping or the called method is deoptimized (by a
-        // breakpoint, for example), then we have to execute the called method
-        // with the interpreter.
+    if (success) {
+      code = called->GetEntryPointFromQuickCompiledCode();
+      if (linker->IsQuickResolutionStub(code)) {
+        DCHECK_EQ(invoke_type, kStatic);
+        // Go to JIT or oat and grab code.
+        code = linker->GetQuickOatCodeFor(called);
+      }
+      if (linker->ShouldUseInterpreterEntrypoint(called, code)) {
         code = GetQuickToInterpreterBridge();
-      } else {
-        code = called->GetEntryPointFromQuickCompiledCode();
-        if (linker->IsQuickResolutionStub(code)) {
-          DCHECK_EQ(invoke_type, kStatic);
-          // Go to JIT or oat and grab code.
-          code = linker->GetQuickOatCodeFor(called);
-          if (called_class->IsInitialized()) {
-            // Only update the entrypoint once the class is initialized. Other
-            // threads still need to go through the resolution stub.
-            Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(called, code);
-          }
-        }
       }
     } else {
       DCHECK(called_class->IsErroneous());
+      DCHECK(self->IsExceptionPending());
     }
   }
   CHECK_EQ(code == nullptr, self->IsExceptionPending());
diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc
index e66a174..2fc9ee8 100644
--- a/runtime/gc/accounting/mod_union_table_test.cc
+++ b/runtime/gc/accounting/mod_union_table_test.cc
@@ -17,7 +17,7 @@
 #include "mod_union_table-inl.h"
 
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_runtime_test.h"
 #include "gc/space/space-inl.h"
 #include "mirror/array-alloc-inl.h"
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 1f50c27..7a1a505 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -24,7 +24,7 @@
 #include "base/quasi_atomic.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "debugger.h"
 #include "gc/accounting/atomic_stack.h"
 #include "gc/accounting/heap_bitmap-inl.h"
@@ -946,43 +946,6 @@
   Thread* const self_;
 };
 
-void ConcurrentCopying::RemoveThreadMarkStackMapping(Thread* thread,
-                                                     accounting::ObjectStack* tl_mark_stack) {
-  CHECK(tl_mark_stack != nullptr);
-  auto it = thread_mark_stack_map_.find(thread);
-  CHECK(it != thread_mark_stack_map_.end());
-  CHECK(it->second == tl_mark_stack);
-  thread_mark_stack_map_.erase(it);
-}
-
-void ConcurrentCopying::AssertEmptyThreadMarkStackMap() {
-  std::ostringstream oss;
-  auto capture_mappings = [this, &oss] () REQUIRES(mark_stack_lock_) {
-    for (const auto & iter : thread_mark_stack_map_) {
-      oss << "thread:" << iter.first << " mark-stack:" << iter.second << "\n";
-    }
-    return oss.str();
-  };
-  CHECK(thread_mark_stack_map_.empty()) << "thread_mark_stack_map not empty. size:"
-                                        << thread_mark_stack_map_.size()
-                                        << "Mappings:\n"
-                                        << capture_mappings()
-                                        << "pooled_mark_stacks size:"
-                                        << pooled_mark_stacks_.size();
-}
-
-void ConcurrentCopying::AssertNoThreadMarkStackMapping(Thread* thread) {
-  MutexLock mu(Thread::Current(), mark_stack_lock_);
-  CHECK(thread_mark_stack_map_.find(thread) == thread_mark_stack_map_.end());
-}
-
-void ConcurrentCopying::AddThreadMarkStackMapping(Thread* thread,
-                                                  accounting::ObjectStack* tl_mark_stack) {
-  CHECK(tl_mark_stack != nullptr);
-  CHECK(thread_mark_stack_map_.find(thread) == thread_mark_stack_map_.end());
-  thread_mark_stack_map_.insert({thread, tl_mark_stack});
-}
-
 class ConcurrentCopying::RevokeThreadLocalMarkStackCheckpoint : public Closure {
  public:
   RevokeThreadLocalMarkStackCheckpoint(ConcurrentCopying* concurrent_copying,
@@ -1003,7 +966,6 @@
       if (tl_mark_stack != nullptr) {
         concurrent_copying_->revoked_mark_stacks_.push_back(tl_mark_stack);
         thread->SetThreadLocalMarkStack(nullptr);
-        concurrent_copying_->RemoveThreadMarkStackMapping(thread, tl_mark_stack);
       }
     }
     // Disable weak ref access.
@@ -1268,7 +1230,6 @@
   {
     MutexLock mu(thread_running_gc_, mark_stack_lock_);
     CHECK(revoked_mark_stacks_.empty());
-    AssertEmptyThreadMarkStackMap();
     CHECK_EQ(pooled_mark_stacks_.size(), kMarkStackPoolSize);
   }
 
@@ -1831,9 +1792,7 @@
         if (tl_mark_stack != nullptr) {
           // Store the old full stack into a vector.
           revoked_mark_stacks_.push_back(tl_mark_stack);
-          RemoveThreadMarkStackMapping(self, tl_mark_stack);
         }
-        AddThreadMarkStackMapping(self, new_tl_mark_stack);
       } else {
         tl_mark_stack->PushBack(to_ref);
       }
@@ -2060,7 +2019,6 @@
   if (tl_mark_stack != nullptr) {
     CHECK(is_marking_);
     revoked_mark_stacks_.push_back(tl_mark_stack);
-    RemoveThreadMarkStackMapping(thread, tl_mark_stack);
     thread->SetThreadLocalMarkStack(nullptr);
   }
 }
@@ -2110,7 +2068,6 @@
     {
       MutexLock mu(thread_running_gc_, mark_stack_lock_);
       CHECK(revoked_mark_stacks_.empty());
-      AssertEmptyThreadMarkStackMap();
       CHECK_EQ(pooled_mark_stacks_.size(), kMarkStackPoolSize);
     }
     while (true) {
@@ -2138,7 +2095,6 @@
     {
       MutexLock mu(thread_running_gc_, mark_stack_lock_);
       CHECK(revoked_mark_stacks_.empty());
-      AssertEmptyThreadMarkStackMap();
       CHECK_EQ(pooled_mark_stacks_.size(), kMarkStackPoolSize);
     }
     // Process the GC mark stack in the exclusive mode. No need to take the lock.
@@ -2163,10 +2119,6 @@
   if (disable_weak_ref_access) {
     CHECK_EQ(static_cast<uint32_t>(mark_stack_mode_.load(std::memory_order_relaxed)),
              static_cast<uint32_t>(kMarkStackModeShared));
-    // From this point onwards no mutator should require a thread-local mark
-    // stack.
-    MutexLock mu(thread_running_gc_, mark_stack_lock_);
-    AssertEmptyThreadMarkStackMap();
   }
   size_t count = 0;
   std::vector<accounting::AtomicStack<mirror::Object>*> mark_stacks;
@@ -2439,7 +2391,6 @@
     MutexLock mu(thread_running_gc_, mark_stack_lock_);
     CHECK(gc_mark_stack_->IsEmpty());
     CHECK(revoked_mark_stacks_.empty());
-    AssertEmptyThreadMarkStackMap();
     CHECK_EQ(pooled_mark_stacks_.size(), kMarkStackPoolSize);
   }
 }
@@ -3703,7 +3654,6 @@
   {
     MutexLock mu(self, mark_stack_lock_);
     CHECK(revoked_mark_stacks_.empty());
-    AssertEmptyThreadMarkStackMap();
     CHECK_EQ(pooled_mark_stacks_.size(), kMarkStackPoolSize);
   }
   // kVerifyNoMissingCardMarks relies on the region space cards not being cleared to avoid false
@@ -3863,6 +3813,9 @@
      << ") / " << region_space_->GetNumRegions() / 2 << " ("
      << PrettySize(region_space_->GetNumRegions() * space::RegionSpace::kRegionSize / 2)
      << ")\n";
+  if (!young_gen_) {
+    os << "Total madvise time " << PrettyDuration(region_space_->GetMadviseTime()) << "\n";
+  }
 }
 
 }  // namespace collector
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 6482ff7..b2bc5a4 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -371,9 +371,6 @@
   static constexpr size_t kMarkStackPoolSize = 256;
   std::vector<accounting::ObjectStack*> pooled_mark_stacks_
       GUARDED_BY(mark_stack_lock_);
-  // TODO(lokeshgidra b/140119552): remove this after bug fix.
-  std::unordered_map<Thread*, accounting::ObjectStack*> thread_mark_stack_map_
-      GUARDED_BY(mark_stack_lock_);
   Thread* thread_running_gc_;
   bool is_marking_;                       // True while marking is ongoing.
   // True while we might dispatch on the read barrier entrypoints.
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 4499342..b557146 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -393,9 +393,8 @@
       static_assert(kObjectAlignment == space::BumpPointerSpace::kAlignment,
                     "mismatched alignments");
       if (UNLIKELY(self->TlabSize() < alloc_size)) {
-        // kAllocatorTypeTLAB may be the allocator for region space TLAB if the GC is not marking,
-        // that is why the allocator is not passed down.
         return AllocWithNewTLAB(self,
+                                allocator_type,
                                 alloc_size,
                                 kGrow,
                                 bytes_allocated,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index b919cdd..d6c1397 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -42,7 +42,7 @@
 #include "base/systrace.h"
 #include "base/time_utils.h"
 #include "base/utils.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex/dex_file-inl.h"
@@ -1395,7 +1395,12 @@
                allocator_type == kAllocatorTypeRegionTLAB) {
       space = region_space_;
     }
-    if (space != nullptr) {
+
+    // There is no fragmentation info to log for large-object space.
+    if (allocator_type != kAllocatorTypeLOS) {
+      CHECK(space != nullptr) << "allocator_type:" << allocator_type
+                              << " byte_count:" << byte_count
+                              << " total_bytes_free:" << total_bytes_free;
       space->LogFragmentationAllocFailure(oss, byte_count);
     }
   }
@@ -4161,12 +4166,12 @@
 }
 
 mirror::Object* Heap::AllocWithNewTLAB(Thread* self,
+                                       AllocatorType allocator_type,
                                        size_t alloc_size,
                                        bool grow,
                                        size_t* bytes_allocated,
                                        size_t* usable_size,
                                        size_t* bytes_tl_bulk_allocated) {
-  const AllocatorType allocator_type = GetCurrentAllocator();
   if (kUsePartialTlabs && alloc_size <= self->TlabRemainingCapacity()) {
     DCHECK_GT(alloc_size, self->TlabSize());
     // There is enough space if we grow the TLAB. Lets do that. This increases the
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 515dcb3..7dcd5e3 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1051,6 +1051,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   mirror::Object* AllocWithNewTLAB(Thread* self,
+                                   AllocatorType allocator_type,
                                    size_t alloc_size,
                                    bool grow,
                                    size_t* bytes_allocated,
diff --git a/runtime/gc/heap_verification_test.cc b/runtime/gc/heap_verification_test.cc
index 7835c29..ca6a30b 100644
--- a/runtime/gc/heap_verification_test.cc
+++ b/runtime/gc/heap_verification_test.cc
@@ -18,7 +18,7 @@
 
 #include "base/memory_tool.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "handle_scope-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-alloc-inl.h"
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 498013e..2e74edf 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -20,7 +20,7 @@
 #include "base/mutex.h"
 #include "base/time_utils.h"
 #include "base/utils.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "collector/garbage_collector.h"
 #include "jni/java_vm_ext.h"
 #include "mirror/class-inl.h"
diff --git a/runtime/gc/space/dlmalloc_space_random_test.cc b/runtime/gc/space/dlmalloc_space_random_test.cc
index 92b56bd..b653bcf 100644
--- a/runtime/gc/space/dlmalloc_space_random_test.cc
+++ b/runtime/gc/space/dlmalloc_space_random_test.cc
@@ -21,6 +21,7 @@
 namespace art {
 namespace gc {
 namespace space {
+namespace {
 
 MallocSpace* CreateDlMallocSpace(const std::string& name,
                                  size_t initial_size,
@@ -32,6 +33,7 @@
 
 TEST_SPACE_CREATE_FN_RANDOM(DlMallocSpace, CreateDlMallocSpace)
 
+}  // namespace
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/dlmalloc_space_static_test.cc b/runtime/gc/space/dlmalloc_space_static_test.cc
index 550d1bb..74dd765 100644
--- a/runtime/gc/space/dlmalloc_space_static_test.cc
+++ b/runtime/gc/space/dlmalloc_space_static_test.cc
@@ -21,6 +21,7 @@
 namespace art {
 namespace gc {
 namespace space {
+namespace {
 
 MallocSpace* CreateDlMallocSpace(const std::string& name,
                                  size_t initial_size,
@@ -32,6 +33,7 @@
 
 TEST_SPACE_CREATE_FN_STATIC(DlMallocSpace, CreateDlMallocSpace)
 
+}  // namespace
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 9958956..5484f49 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -43,7 +43,7 @@
 #include "base/systrace.h"
 #include "base/time_utils.h"
 #include "base/utils.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file_loader.h"
 #include "exec_utils.h"
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index b08c680..efd82d9 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -60,13 +60,17 @@
   int mkdir_result = mkdir(image_dir.c_str(), 0700);
   ASSERT_EQ(0, mkdir_result);
 
-  // Prepare boot class path variables, exclude conscrypt which is not in the primary boot image.
+  // Prepare boot class path variables, exclude core-icu4j and conscrypt
+  // which are not in the primary boot image.
   std::vector<std::string> bcp = GetLibCoreDexFileNames();
   std::vector<std::string> bcp_locations = GetLibCoreDexLocations();
   CHECK_EQ(bcp.size(), bcp_locations.size());
   ASSERT_NE(std::string::npos, bcp.back().find("conscrypt"));
   bcp.pop_back();
   bcp_locations.pop_back();
+  ASSERT_NE(std::string::npos, bcp.back().find("core-icu4j"));
+  bcp.pop_back();
+  bcp_locations.pop_back();
   std::string base_bcp_string = android::base::Join(bcp, ':');
   std::string base_bcp_locations_string = android::base::Join(bcp_locations, ':');
   std::string base_image_location = GetImageLocation();
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index faeeec0..25c177c 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -110,6 +110,7 @@
       use_generational_cc_(use_generational_cc),
       time_(1U),
       num_regions_(mem_map_.Size() / kRegionSize),
+      madvise_time_(0U),
       num_non_free_regions_(0U),
       num_evac_regions_(0U),
       max_peak_num_non_free_regions_(0U),
@@ -499,8 +500,13 @@
   }
 
   // Madvise the memory ranges.
+  uint64_t start_time = NanoTime();
   for (const auto &iter : madvise_list) {
     ZeroAndProtectRegion(iter.first, iter.second);
+  }
+  madvise_time_ += NanoTime() - start_time;
+
+  for (const auto &iter : madvise_list) {
     if (clear_bitmap) {
       GetLiveBitmap()->ClearRange(
           reinterpret_cast<mirror::Object*>(iter.first),
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index f74abfb..9a3ce94 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -380,6 +380,10 @@
     return num_evac_regions_ * kRegionSize;
   }
 
+  uint64_t GetMadviseTime() const {
+    return madvise_time_;
+  }
+
  private:
   RegionSpace(const std::string& name, MemMap&& mem_map, bool use_generational_cc);
 
@@ -738,6 +742,7 @@
   const bool use_generational_cc_;
   uint32_t time_;                  // The time as the number of collections since the startup.
   size_t num_regions_;             // The number of regions in this space.
+  uint64_t madvise_time_;          // The amount of time spent in madvise for purging pages.
   // The number of non-free regions in this space.
   size_t num_non_free_regions_ GUARDED_BY(region_lock_);
 
diff --git a/runtime/gc/space/rosalloc_space_random_test.cc b/runtime/gc/space/rosalloc_space_random_test.cc
index f0b3231..3010b77 100644
--- a/runtime/gc/space/rosalloc_space_random_test.cc
+++ b/runtime/gc/space/rosalloc_space_random_test.cc
@@ -21,6 +21,7 @@
 namespace art {
 namespace gc {
 namespace space {
+namespace {
 
 MallocSpace* CreateRosAllocSpace(const std::string& name,
                                  size_t initial_size,
@@ -36,6 +37,7 @@
 
 TEST_SPACE_CREATE_FN_RANDOM(RosAllocSpace, CreateRosAllocSpace)
 
+}  // namespace
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/rosalloc_space_static_test.cc b/runtime/gc/space/rosalloc_space_static_test.cc
index d7e7e90..860a461 100644
--- a/runtime/gc/space/rosalloc_space_static_test.cc
+++ b/runtime/gc/space/rosalloc_space_static_test.cc
@@ -21,6 +21,7 @@
 namespace art {
 namespace gc {
 namespace space {
+namespace {
 
 MallocSpace* CreateRosAllocSpace(const std::string& name,
                                  size_t initial_size,
@@ -35,6 +36,7 @@
 
 TEST_SPACE_CREATE_FN_STATIC(RosAllocSpace, CreateRosAllocSpace)
 
+}  // namespace
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/handle.cc b/runtime/handle.cc
index 0028dee..af77e23 100644
--- a/runtime/handle.cc
+++ b/runtime/handle.cc
@@ -38,7 +38,7 @@
 #include "mirror/throwable.h"
 #include "mirror/var_handle.h"
 
-#include "class_root.h"
+#include "class_root-inl.h"
 
 namespace art {
 
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 6ac57b6..21a673c 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -22,7 +22,6 @@
 #include "art_method-inl.h"
 #include "base/dumpable.h"
 #include "base/file_utils.h"
-#include "class_root.h"
 #include "dex/class_accessor-inl.h"
 #include "dex/dex_file_loader.h"
 #include "mirror/class_ext.h"
@@ -94,7 +93,8 @@
   // is set to "/system".
   if (ArtModuleRootDistinctFromAndroidRoot()) {
     if (LocationIsOnArtModule(dex_location.c_str()) ||
-        LocationIsOnConscryptModule(dex_location.c_str())) {
+        LocationIsOnConscryptModule(dex_location.c_str()) ||
+        LocationIsOnI18nModule(dex_location.c_str())) {
       return Domain::kCorePlatform;
     }
 
@@ -107,9 +107,16 @@
     return Domain::kPlatform;
   }
 
+  if (LocationIsOnSystemExtFramework(dex_location.c_str())) {
+    return Domain::kPlatform;
+  }
+
   if (class_loader.IsNull()) {
-    LOG(WARNING) << "DexFile " << dex_location
-        << " is in boot class path but is not in a known location";
+    if (kIsTargetBuild && !kIsTargetLinux) {
+      // This is unexpected only when running on Android.
+      LOG(WARNING) << "DexFile " << dex_location
+          << " is in boot class path but is not in a known location";
+    }
     return Domain::kPlatform;
   }
 
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index b9214ff..16df6d7 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -124,6 +124,7 @@
   runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false);
@@ -136,6 +137,7 @@
   setChangeIdState(kHideMaxtargetsdkQHiddenApis, false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false);
@@ -148,6 +150,7 @@
   setChangeIdState(kHideMaxtargetsdkQHiddenApis, false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), true);
@@ -167,6 +170,7 @@
   setChangeIdState(kHideMaxtargetsdkQHiddenApis, false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), true);
@@ -179,6 +183,20 @@
   setChangeIdState(kHideMaxtargetsdkQHiddenApis, true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxR()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), true);
+
+  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+  runtime_->SetTargetSdkVersion(
+      static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxR().GetMaxAllowedSdkVersion()) + 1);
+  setChangeIdState(kHideMaxtargetsdkPHiddenApis, true);
+  setChangeIdState(kHideMaxtargetsdkQHiddenApis, true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxR()), true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), true);
@@ -190,7 +208,7 @@
 
   runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
   runtime_->SetTargetSdkVersion(
-      static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxQ().GetMaxAllowedSdkVersion()) + 1);
+      static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxR().GetMaxAllowedSdkVersion()) + 1);
 
   // Default case where all TestApis are treated like non-TestApi.
   runtime_->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
@@ -199,6 +217,8 @@
   ASSERT_EQ(
       ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Greylist()), false);
   ASSERT_EQ(
+      ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxR()), true);
+  ASSERT_EQ(
       ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxQ()), true);
   ASSERT_EQ(
       ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxP()), true);
@@ -214,6 +234,8 @@
   ASSERT_EQ(
       ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Greylist()), false);
   ASSERT_EQ(
+      ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxR()), false);
+  ASSERT_EQ(
       ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxQ()), false);
   ASSERT_EQ(
       ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxP()), false);
@@ -586,6 +608,29 @@
   ASSERT_EQ(0, remove(system_location_path.c_str()));
 }
 
+TEST_F(HiddenApiTest, DexDomain_SystemExtDir) {
+  // Load file from a system_ext, non-framework directory and check that it is not flagged as framework.
+  std::string system_ext_location_path = android_system_ext_ + "/foo.jar";
+  ASSERT_FALSE(LocationIsOnSystemExtFramework(system_ext_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("Main"), system_ext_location_path, &error_msg)) << error_msg;
+  ASSERT_TRUE(LoadDexFiles(system_ext_location_path, soa, &dex_files, &class_loader, &error_msg))
+      << error_msg;
+  ASSERT_GE(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader,
+                                       dex_files,
+                                       hiddenapi::Domain::kApplication,
+                                       &error_msg)) << error_msg;
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(system_ext_location_path.c_str()));
+}
+
 TEST_F(HiddenApiTest, DexDomain_SystemFrameworkDir) {
   // Load file from a system/framework directory and check that it is flagged as a framework dex.
   std::string system_framework_location_path = GetAndroidRoot() + "/framework/foo.jar";
@@ -613,6 +658,33 @@
   ASSERT_EQ(0, remove(system_framework_location_path.c_str()));
 }
 
+TEST_F(HiddenApiTest, DexDomain_SystemExtFrameworkDir) {
+  // Load file from a system_ext/framework directory and check that it is flagged as a framework dex.
+  std::string system_ext_framework_location_path = android_system_ext_ + "/framework/foo.jar";
+  ASSERT_TRUE(LocationIsOnSystemExtFramework(system_ext_framework_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("Main"), system_ext_framework_location_path, &error_msg))
+      << error_msg;
+  ASSERT_TRUE(LoadDexFiles(system_ext_framework_location_path,
+                           soa,
+                           &dex_files,
+                           &class_loader,
+                           &error_msg)) << error_msg;
+  ASSERT_GE(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader,
+                                       dex_files,
+                                       hiddenapi::Domain::kPlatform,
+                                       &error_msg)) << error_msg;
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(system_ext_framework_location_path.c_str()));
+}
+
 TEST_F(HiddenApiTest, DexDomain_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";
@@ -662,6 +734,31 @@
   ASSERT_EQ(0, remove(system_multi_location_path.c_str()));
 }
 
+TEST_F(HiddenApiTest, DexDomain_SystemExtDir_MultiDex) {
+  // Load multidex file from a system_ext, non-framework directory and check that it is not flagged
+  // as framework.
+  std::string system_ext_multi_location_path = android_system_ext_ + "/multifoo.jar";
+  ASSERT_FALSE(LocationIsOnSystemExtFramework(system_ext_multi_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("MultiDex"), system_ext_multi_location_path, &error_msg))
+      << error_msg;
+  ASSERT_TRUE(LoadDexFiles(system_ext_multi_location_path, soa, &dex_files, &class_loader, &error_msg))
+      << error_msg;
+  ASSERT_GT(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader,
+                                       dex_files,
+                                       hiddenapi::Domain::kApplication,
+                                       &error_msg)) << error_msg;
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(system_ext_multi_location_path.c_str()));
+}
+
 TEST_F(HiddenApiTest, DexDomain_SystemFrameworkDir_MultiDex) {
   // Load multidex file from a system/framework directory and check that it is flagged as a
   // framework dex.
@@ -691,4 +788,33 @@
   ASSERT_EQ(0, remove(system_framework_multi_location_path.c_str()));
 }
 
+TEST_F(HiddenApiTest, DexDomain_SystemExtFrameworkDir_MultiDex) {
+  // Load multidex file from a system_ext/framework directory and check that it is flagged as a
+  // framework dex.
+  std::string system_ext_framework_multi_location_path = android_system_ext_ + "/framework/multifoo.jar";
+  ASSERT_TRUE(LocationIsOnSystemExtFramework(system_ext_framework_multi_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("MultiDex"),
+                   system_ext_framework_multi_location_path,
+                   &error_msg)) << error_msg;
+  ASSERT_TRUE(LoadDexFiles(system_ext_framework_multi_location_path,
+                           soa,
+                           &dex_files,
+                           &class_loader,
+                           &error_msg)) << error_msg;
+  ASSERT_GT(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader,
+                                       dex_files,
+                                       hiddenapi::Domain::kPlatform,
+                                       &error_msg)) << error_msg;
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(system_ext_framework_multi_location_path.c_str()));
+}
+
 }  // namespace art
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 516c435..27560c8 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -50,7 +50,7 @@
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex/dex_file-inl.h"
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 60e7c9c..8cd94e6 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -843,7 +843,7 @@
 }
 
 static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg ATTRIBUTE_UNUSED) {
-  thread->ResetQuickAllocEntryPointsForThread(kUseReadBarrier && thread->GetIsGcMarking());
+  thread->ResetQuickAllocEntryPointsForThread();
 }
 
 void Instrumentation::SetEntrypointsInstrumented(bool instrumented) {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 4d964f1..726de6e 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -20,7 +20,7 @@
 
 #include "base/casts.h"
 #include "base/enums.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "debugger.h"
 #include "dex/dex_file_types.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index c6d8569..6c272f3 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -39,7 +39,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_dex_operations.h"
 #include "common_throws.h"
 #include "dex/dex_file-inl.h"
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index 863612f..8a83ede 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -1909,7 +1909,7 @@
 };
 
 // Don't inline in ASAN. It would create massive stack frame.
-#ifdef ADDRESS_SANITIZER
+#if defined(ADDRESS_SANITIZER) || defined(HWADDRESS_SANITIZER)
 #define ASAN_NO_INLINE NO_INLINE
 #else
 #define ASAN_NO_INLINE ALWAYS_INLINE
@@ -1957,18 +1957,18 @@
     TraceExecution(shadow_frame, inst, dex_pc);
     uint16_t inst_data = inst->Fetch16(0);
     bool exit = false;
+    bool success;  // Moved outside to keep frames small under asan.
     if (InstructionHandler<do_access_check, transaction_active, Instruction::kInvalidFormat>(
             ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit).
             Preamble()) {
+      DCHECK_EQ(self->IsExceptionPending(), inst->Opcode(inst_data) == Instruction::MOVE_EXCEPTION);
       switch (inst->Opcode(inst_data)) {
 #define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v)                                \
         case OPCODE: {                                                                            \
-          DCHECK_EQ(self->IsExceptionPending(), (OPCODE == Instruction::MOVE_EXCEPTION));         \
           next = inst->RelativeAt(Instruction::SizeInCodeUnits(Instruction::FORMAT));             \
-          bool success = OP_##OPCODE_NAME<do_access_check, transaction_active>(                   \
+          success = OP_##OPCODE_NAME<do_access_check, transaction_active>(                        \
               ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit);     \
           if (success && LIKELY(!interpret_one_instruction)) {                                    \
-            DCHECK(!exit) << NAME;                                                                \
             continue;                                                                             \
           }                                                                                       \
           if (exit) {                                                                             \
diff --git a/runtime/interpreter/mterp/arm64ng/array.S b/runtime/interpreter/mterp/arm64ng/array.S
new file mode 100644
index 0000000..81e3d90
--- /dev/null
+++ b/runtime/interpreter/mterp/arm64ng/array.S
@@ -0,0 +1,161 @@
+%def op_aget(load="ldr", shift="2", data_offset="MIRROR_INT_ARRAY_DATA_OFFSET", wide="0", is_object="0"):
+/*
+ * Array get.  vAA <- vBB[vCC].
+ *
+ * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide, aget-object
+ *
+ */
+    FETCH_B w2, 1, 0                    // w2<- BB
+    lsr     w9, wINST, #8               // w9<- AA
+    FETCH_B w3, 1, 1                    // w3<- CC
+    GET_VREG w0, w2                     // w0<- vBB (array object)
+    GET_VREG w1, w3                     // w1<- vCC (requested index)
+    cbz     x0, common_errNullObject    // bail if null array object.
+    ldr     w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET]    // w3<- arrayObj->length
+    add     x0, x0, w1, uxtw #$shift    // w0<- arrayObj + index*width
+    cmp     w1, w3                      // compare unsigned index, length
+    bcs     common_errArrayIndex        // index >= length, bail
+    FETCH_ADVANCE_INST 2                // advance rPC, load rINST
+    GET_INST_OPCODE x10                 // extract opcode from wINST
+    .if $wide
+    ldr     x2, [x0, #$data_offset]     // x2<- vBB[vCC]
+    SET_VREG_WIDE x2, w9
+    GOTO_OPCODE x10                     // jump to next instruction
+    .elseif $is_object
+    $load   w2, [x0, #$data_offset]     // w2<- vBB[vCC]
+    cbnz wMR, 2f
+1:
+    SET_VREG_OBJECT w2, w9              // vAA<- w2
+    GOTO_OPCODE x10                     // jump to next instruction
+2:
+    bl art_quick_read_barrier_mark_reg02
+    b 1b
+    .else
+    $load   w2, [x0, #$data_offset]     // w2<- vBB[vCC]
+    SET_VREG w2, w9                     // vAA<- w2
+    GOTO_OPCODE x10                     // jump to next instruction
+    .endif
+
+%def op_aget_boolean():
+%  op_aget(load="ldrb", shift="0", data_offset="MIRROR_BOOLEAN_ARRAY_DATA_OFFSET", is_object="0")
+
+%def op_aget_byte():
+%  op_aget(load="ldrsb", shift="0", data_offset="MIRROR_BYTE_ARRAY_DATA_OFFSET", is_object="0")
+
+%def op_aget_char():
+%  op_aget(load="ldrh", shift="1", data_offset="MIRROR_CHAR_ARRAY_DATA_OFFSET", is_object="0")
+
+%def op_aget_object():
+%  op_aget(load="ldr", shift="2", data_offset="MIRROR_OBJECT_ARRAY_DATA_OFFSET", is_object="1")
+
+%def op_aget_short():
+%  op_aget(load="ldrsh", shift="1", data_offset="MIRROR_SHORT_ARRAY_DATA_OFFSET", is_object="0")
+
+%def op_aget_wide():
+%  op_aget(load="ldr", shift="3", data_offset="MIRROR_WIDE_ARRAY_DATA_OFFSET", wide="1", is_object="0")
+
+%def op_aput(store="str", shift="2", data_offset="MIRROR_INT_ARRAY_DATA_OFFSET", wide="0", is_object="0"):
+/*
+ * Array put.  vBB[vCC] <- vAA.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide
+ *
+ */
+    FETCH_B w2, 1, 0                    // w2<- BB
+    lsr     w9, wINST, #8               // w9<- AA
+    FETCH_B w3, 1, 1                    // w3<- CC
+    GET_VREG w0, w2                     // w0<- vBB (array object)
+    GET_VREG w1, w3                     // w1<- vCC (requested index)
+    cbz     w0, common_errNullObject    // bail if null
+    ldr     w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET]     // w3<- arrayObj->length
+    .if !$is_object
+    add     x0, x0, w1, uxtw #$shift    // w0<- arrayObj + index*width
+    .endif
+    cmp     w1, w3                      // compare unsigned index, length
+    bcs     common_errArrayIndex        // index >= length, bail
+    FETCH_ADVANCE_INST 2                // advance rPC, load rINST
+    .if $wide
+    GET_VREG_WIDE x2, w9                // x2<- vAA
+    .else
+    GET_VREG w2, w9                     // w2<- vAA
+    .endif
+    .if $wide
+    $store  x2, [x0, #$data_offset]     // vBB[vCC]<- x2
+    .elseif $is_object
+    EXPORT_PC
+    bl art_quick_aput_obj
+    .else
+    $store  w2, [x0, #$data_offset]     // vBB[vCC]<- w2
+    .endif
+    GET_INST_OPCODE ip                  // extract opcode from rINST
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_aput_boolean():
+%  op_aput(store="strb", shift="0", data_offset="MIRROR_BOOLEAN_ARRAY_DATA_OFFSET", wide="0", is_object="0")
+
+%def op_aput_byte():
+%  op_aput(store="strb", shift="0", data_offset="MIRROR_BYTE_ARRAY_DATA_OFFSET", wide="0", is_object="0")
+
+%def op_aput_char():
+%  op_aput(store="strh", shift="1", data_offset="MIRROR_CHAR_ARRAY_DATA_OFFSET", wide="0", is_object="0")
+
+%def op_aput_short():
+%  op_aput(store="strh", shift="1", data_offset="MIRROR_SHORT_ARRAY_DATA_OFFSET", wide="0", is_object="0")
+
+%def op_aput_wide():
+%  op_aput(store="str", shift="3", data_offset="MIRROR_WIDE_ARRAY_DATA_OFFSET", wide="1", is_object="0")
+
+%def op_aput_object():
+%  op_aput(store="str", shift="2", data_offset="MIRROR_INT_ARRAY_DATA_OFFSET", wide="0", is_object="1")
+
+%def op_array_length():
+    /*
+     * Return the length of an array.
+     */
+    lsr     w1, wINST, #12              // w1<- B
+    ubfx    w2, wINST, #8, #4           // w2<- A
+    GET_VREG w0, w1                     // w0<- vB (object ref)
+    cbz     w0, common_errNullObject    // bail if null
+    FETCH_ADVANCE_INST 1                // advance rPC, load rINST
+    ldr     w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET]    // w3<- array length
+    GET_INST_OPCODE ip                  // extract opcode from rINST
+    SET_VREG w3, w2                     // vB<- length
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_fill_array_data():
+    /* fill-array-data vAA, +BBBBBBBB */
+    EXPORT_PC
+    FETCH   w0, 1                       // x0<- 000000000000bbbb (lo)
+    FETCH_S x1, 2                       // x1<- ssssssssssssBBBB (hi)
+    lsr     w3, wINST, #8               // w3<- AA
+    orr     x0, x0, x1, lsl #16         // x0<- ssssssssBBBBbbbb
+    GET_VREG w1, w3                     // w1<- vAA (array object)
+    add     x0, xPC, x0, lsl #1         // x0<- PC + ssssssssBBBBbbbb*2 (array data off.)
+    bl      art_quick_handle_fill_data
+    FETCH_ADVANCE_INST 3                // advance rPC, load rINST
+    GET_INST_OPCODE ip                  // extract opcode from rINST
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_filled_new_array(helper="nterp_filled_new_array"):
+/*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    EXPORT_PC
+    mov     x0, xSELF
+    ldr     x1, [sp]
+    mov     x2, xFP
+    mov     x3, xPC
+    bl      $helper
+    FETCH_ADVANCE_INST 3                // advance rPC, load rINST
+    GET_INST_OPCODE ip                  // extract opcode from rINST
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_filled_new_array_range():
+%  op_filled_new_array(helper="nterp_filled_new_array_range")
+
+%def op_new_array():
+  b NterpNewArray
diff --git a/runtime/interpreter/mterp/arm64ng/control_flow.S b/runtime/interpreter/mterp/arm64ng/control_flow.S
new file mode 100644
index 0000000..0e0b8dd
--- /dev/null
+++ b/runtime/interpreter/mterp/arm64ng/control_flow.S
@@ -0,0 +1,192 @@
+%def bincmp(condition=""):
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "condition"
+     * fragment that specifies the comparison to perform.
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    lsr     w1, wINST, #12              // w1<- B
+    ubfx    w0, wINST, #8, #4           // w0<- A
+    GET_VREG w3, w1                     // w3<- vB
+    GET_VREG w2, w0                     // w2<- vA
+    cmp     w2, w3                      // compare (vA, vB)
+    b.${condition} 1f
+    FETCH_ADVANCE_INST 2
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    GOTO_OPCODE ip                      // jump to next instruction
+1:
+    FETCH_S wINST, 1                    // wINST<- branch offset, in code units
+    BRANCH
+
+%def zcmp(compare="1", branch=""):
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "condition"
+     * fragment that specifies the comparison to perform.
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    lsr     w0, wINST, #8               // w0<- AA
+    GET_VREG w2, w0                     // w2<- vAA
+    .if ${compare}
+    cmp     w2, #0                      // compare (vA, 0)
+    .endif
+    ${branch} 1f
+    FETCH_ADVANCE_INST 2
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    GOTO_OPCODE ip                      // jump to next instruction
+1:
+    FETCH_S wINST, 1                    // w1<- branch offset, in code units
+    BRANCH
+
+%def op_goto():
+/*
+ * Unconditional branch, 8-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+    /* goto +AA */
+    sbfx    wINST, wINST, #8, #8           // wINST<- ssssssAA (sign-extended)
+    BRANCH
+
+%def op_goto_16():
+/*
+ * Unconditional branch, 16-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+    /* goto/16 +AAAA */
+    FETCH_S wINST, 1                    // wINST<- ssssAAAA (sign-extended)
+    BRANCH
+
+%def op_goto_32():
+/*
+ * Unconditional branch, 32-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ *
+ * Because we need the SF bit set, we'll use an adds
+ * to convert from Dalvik offset to byte offset.
+ */
+    /* goto/32 +AAAAAAAA */
+    FETCH w0, 1                         // w0<- aaaa (lo)
+    FETCH w1, 2                         // w1<- AAAA (hi)
+    orr     wINST, w0, w1, lsl #16      // wINST<- AAAAaaaa
+    BRANCH
+
+%def op_if_eq():
+%  bincmp(condition="eq")
+
+%def op_if_eqz():
+%  zcmp(compare="0", branch="cbz     w2,")
+
+%def op_if_ge():
+%  bincmp(condition="ge")
+
+%def op_if_gez():
+%  zcmp(compare="0", branch="tbz     w2, #31,")
+
+%def op_if_gt():
+%  bincmp(condition="gt")
+
+%def op_if_gtz():
+%  zcmp(branch="b.gt")
+
+%def op_if_le():
+%  bincmp(condition="le")
+
+%def op_if_lez():
+%  zcmp(branch="b.le")
+
+%def op_if_lt():
+%  bincmp(condition="lt")
+
+%def op_if_ltz():
+%  zcmp(compare="0", branch="tbnz    w2, #31,")
+
+%def op_if_ne():
+%  bincmp(condition="ne")
+
+%def op_if_nez():
+%  zcmp(compare="0", branch="cbnz    w2,")
+
+%def op_packed_switch(func="NterpDoPackedSwitch"):
+/*
+ * Handle a packed-switch or sparse-switch instruction.  In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+    /* op vAA, +BBBB */
+    FETCH   w0, 1                       // x0<- 000000000000bbbb (lo)
+    FETCH_S x1, 2                       // x1<- ssssssssssssBBBB (hi)
+    lsr     w3, wINST, #8               // w3<- AA
+    orr     x0, x0, x1, lsl #16         // x0<- ssssssssBBBBbbbb
+    GET_VREG w1, w3                     // w1<- vAA
+    add     x0, xPC, x0, lsl #1         // x0<- PC + ssssssssBBBBbbbb*2
+    bl      $func                       // w0<- code-unit branch offset
+    sxtw    xINST, w0
+    BRANCH
+
+%def op_sparse_switch():
+%  op_packed_switch(func="NterpDoSparseSwitch")
+
+/*
+ * Return a 32-bit value.
+ */
+%def op_return(is_object="0", is_void="0", is_wide="0", is_no_barrier="0"):
+    .if $is_void
+      .if !$is_no_barrier
+      // Thread fence for constructor
+      dmb ishst
+      .endif
+    .else
+      lsr     w2, wINST, #8               // w2<- AA
+      .if $is_wide
+        GET_VREG_WIDE x0, w2                // x0<- vAA
+        // In case we're going back to compiled code, put the
+        // result also in d0
+        fmov d0, x0
+      .else
+        GET_VREG w0, w2                     // r0<- vAA
+        .if !$is_object
+        // In case we're going back to compiled code, put the
+        // result also in s0.
+        fmov s0, w0
+        .endif
+      .endif
+    .endif
+    .cfi_remember_state
+    ldr ip, [xREFS, #-8]
+    mov sp, ip
+    .cfi_def_cfa sp, CALLEE_SAVES_SIZE
+    RESTORE_ALL_CALLEE_SAVES
+    ret
+    .cfi_restore_state
+
+%def op_return_object():
+%  op_return(is_object="1", is_void="0", is_wide="0", is_no_barrier="0")
+
+%def op_return_void():
+%  op_return(is_object="0", is_void="1", is_wide="0", is_no_barrier="0")
+
+%def op_return_void_no_barrier():
+%  op_return(is_object="0", is_void="1", is_wide="0", is_no_barrier="1")
+
+%def op_return_wide():
+%  op_return(is_object="0", is_void="0", is_wide="1", is_no_barrier="0")
+
+%def op_throw():
+  EXPORT_PC
+  lsr      w2, wINST, #8               // r2<- AA
+  GET_VREG w0, w2                      // r0<- vAA (exception object)
+  mov x1, xSELF
+  bl art_quick_deliver_exception
+  brk 0
diff --git a/runtime/interpreter/mterp/arm64ng/invoke.S b/runtime/interpreter/mterp/arm64ng/invoke.S
new file mode 100644
index 0000000..b70d82e
--- /dev/null
+++ b/runtime/interpreter/mterp/arm64ng/invoke.S
@@ -0,0 +1,174 @@
+%def op_invoke_custom():
+   EXPORT_PC
+   FETCH w0, 1 // call_site index, first argument of runtime call.
+   b NterpCommonInvokeCustom
+
+%def op_invoke_custom_range():
+   EXPORT_PC
+   FETCH w0, 1 // call_site index, first argument of runtime call.
+   b NterpCommonInvokeCustomRange
+
+%def invoke_direct_or_super(helper="", range=""):
+   EXPORT_PC
+   // Fast-path which gets the method from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 2f
+1:
+   // Load the first argument (the 'this' pointer).
+   FETCH w1, 2
+   .if !$range
+   and w1, w1, #0xf
+   .endif
+   GET_VREG w1, w1
+   cbz w1, common_errNullObject    // bail if null
+   b $helper
+2:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   bl nterp_get_method
+   tbz x0, #0, 1b
+   and x0, x0, #-2 // Remove the extra bit that marks it's a String.<init> method.
+   .if $range
+   b NterpHandleStringInitRange
+   .else
+   b NterpHandleStringInit
+   .endif
+
+%def op_invoke_direct():
+%  invoke_direct_or_super(helper="NterpCommonInvokeInstance", range="0")
+
+%def op_invoke_direct_range():
+%  invoke_direct_or_super(helper="NterpCommonInvokeInstanceRange", range="1")
+
+%def op_invoke_super():
+%  invoke_direct_or_super(helper="NterpCommonInvokeInstance", range="0")
+
+%def op_invoke_super_range():
+%  invoke_direct_or_super(helper="NterpCommonInvokeInstanceRange", range="1")
+
+%def op_invoke_polymorphic():
+   EXPORT_PC
+   // No need to fetch the target method.
+   // Load the first argument (the 'this' pointer).
+   FETCH w1, 2
+   and w1, w1, #0xf
+   GET_VREG w1, w1
+   cbz w1, common_errNullObject    // bail if null
+   b NterpCommonInvokePolymorphic
+
+%def op_invoke_polymorphic_range():
+   EXPORT_PC
+   // No need to fetch the target method.
+   // Load the first argument (the 'this' pointer).
+   FETCH w1, 2
+   GET_VREG w1, w1
+   cbz w1, common_errNullObject    // bail if null
+   b NterpCommonInvokePolymorphicRange
+
+%def invoke_interface(range=""):
+   EXPORT_PC
+   // Fast-path which gets the method from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 2f
+1:
+   // First argument is the 'this' pointer.
+   FETCH w1, 2
+   .if !$range
+   and w1, w1, #0xf
+   .endif
+   GET_VREG w1, w1
+   // Note: if w1 is null, this will be handled by our SIGSEGV handler.
+   ldr w2, [x1, #MIRROR_OBJECT_CLASS_OFFSET]
+   ldr x2, [x2, #MIRROR_CLASS_IMT_PTR_OFFSET_64]
+   ldr x0, [x2, w0, uxtw #3]
+   .if $range
+   b NterpCommonInvokeInterfaceRange
+   .else
+   b NterpCommonInvokeInterface
+   .endif
+2:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   bl nterp_get_method
+   // For j.l.Object interface calls, the high bit is set. Also the method index is 16bits.
+   tbz w0, #31, 1b
+   and w0, w0, #0xffff
+   .if $range
+   b NterpHandleInvokeInterfaceOnObjectMethodRange
+   .else
+   b NterpHandleInvokeInterfaceOnObjectMethod
+   .endif
+
+%def op_invoke_interface():
+%  invoke_interface(range="0")
+
+%def op_invoke_interface_range():
+%  invoke_interface(range="1")
+
+%def invoke_static(helper=""):
+   EXPORT_PC
+   // Fast-path which gets the method from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 1f
+   b $helper
+1:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   bl nterp_get_method
+   b $helper
+
+%def op_invoke_static():
+%  invoke_static(helper="NterpCommonInvokeStatic")
+
+%def op_invoke_static_range():
+%  invoke_static(helper="NterpCommonInvokeStaticRange")
+
+%def invoke_virtual(helper="", range=""):
+   EXPORT_PC
+   // Fast-path which gets the method from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x2, 2f
+1:
+   FETCH w1, 2
+   .if !$range
+   and w1, w1, #0xf
+   .endif
+   GET_VREG w1, w1
+   // Note: if w1 is null, this will be handled by our SIGSEGV handler.
+   ldr w0, [x1, #MIRROR_OBJECT_CLASS_OFFSET]
+   add w0, w0, #MIRROR_CLASS_VTABLE_OFFSET_64
+   ldr x0, [x0, w2, uxtw #3]
+   b $helper
+2:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   bl nterp_get_method
+   mov x2, x0
+   b 1b
+
+%def op_invoke_virtual():
+%  invoke_virtual(helper="NterpCommonInvokeInstance", range="0")
+
+%def op_invoke_virtual_range():
+%  invoke_virtual(helper="NterpCommonInvokeInstanceRange", range="1")
+
+%def invoke_virtual_quick(helper="", range=""):
+   EXPORT_PC
+   FETCH w2, 1  // offset
+   // First argument is the 'this' pointer.
+   FETCH w1, 2 // arguments
+   .if !$range
+   and w1, w1, #0xf
+   .endif
+   GET_VREG w1, w1
+   // Note: if w1 is null, this will be handled by our SIGSEGV handler.
+   ldr w0, [x1, #MIRROR_OBJECT_CLASS_OFFSET]
+   add w0, w0, #MIRROR_CLASS_VTABLE_OFFSET_64
+   ldr x0, [x0, w2, uxtw #3]
+   b $helper
+
+%def op_invoke_virtual_quick():
+%  invoke_virtual_quick(helper="NterpCommonInvokeInstance", range="0")
+
+%def op_invoke_virtual_range_quick():
+%  invoke_virtual_quick(helper="NterpCommonInvokeInstanceRange", range="1")
diff --git a/runtime/interpreter/mterp/arm64ng/main.S b/runtime/interpreter/mterp/arm64ng/main.S
new file mode 100644
index 0000000..ea04b45
--- /dev/null
+++ b/runtime/interpreter/mterp/arm64ng/main.S
@@ -0,0 +1,2150 @@
+%def header():
+/*
+ * Copyright (C) 2020 The Android Open 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.
+ */
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "asm_support.h"
+#include "arch/arm64/asm_support_arm64.S"
+#include "interpreter/cfi_asm_support.h"
+
+/**
+ * ARM64 Runtime register usage conventions.
+ *
+ *   r0     : w0 is 32-bit return register and x0 is 64-bit.
+ *   r0-r7  : Argument registers.
+ *   r8-r15 : Caller save registers (used as temporary registers).
+ *   r16-r17: Also known as ip0-ip1, respectively. Used as scratch registers by
+ *            the linker, by the trampolines and other stubs (the compiler uses
+ *            these as temporary registers).
+ *   r18    : Reserved for platform (SCS, shadow call stack)
+ *   r19    : Pointer to thread-local storage.
+ *   r20-r29: Callee save registers.
+ *   r30    : (lr) is reserved (the link register).
+ *   rsp    : (sp) is reserved (the stack pointer).
+ *   rzr    : (zr) is reserved (the zero register).
+ *
+ *   Floating-point registers
+ *   v0-v31
+ *
+ *   v0     : s0 is return register for singles (32-bit) and d0 for doubles (64-bit).
+ *            This is analogous to the C/C++ (hard-float) calling convention.
+ *   v0-v7  : Floating-point argument registers in both Dalvik and C/C++ conventions.
+ *            Also used as temporary and codegen scratch registers.
+ *
+ *   v0-v7 and v16-v31 : Caller save registers (used as temporary registers).
+ *   v8-v15 : bottom 64-bits preserved across C calls (d8-d15 are preserved).
+ *
+ *   v16-v31: Used as codegen temp/scratch.
+ *   v8-v15 : Can be used for promotion.
+ *
+ *   Must maintain 16-byte stack alignment.
+ *
+ * Nterp notes:
+ *
+ * The following registers have fixed assignments:
+ *
+ *   reg nick      purpose
+ *   x19  xSELF     self (Thread) pointer
+ *   x20  wMR       marking register
+ *   x29  xFP       interpreted frame pointer, used for accessing locals and args
+ *   x22  xPC       interpreted program counter, used for fetching instructions
+ *   x23  xINST     first 16-bit code unit of current instruction
+ *   x24  xIBASE    interpreted instruction base pointer, used for computed goto
+ *   x25  xREFS     base of object references of dex registers.
+ *   x16  ip        scratch reg
+ *   x17  ip2       scratch reg (used by macros)
+ *
+ * Macros are provided for common operations.  They MUST NOT alter unspecified registers or
+ * condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define xSELF    x19
+#define CFI_DEX  22 // DWARF register number of the register holding dex-pc (xPC).
+#define CFI_TMP  0  // DWARF register number of the first argument register (r0).
+#define xPC      x22
+#define xINST    x23
+#define wINST    w23
+#define xIBASE   x24
+#define xREFS    x25
+#define CFI_REFS 25
+#define ip       x16
+#define ip2      x17
+#define wip      w16
+#define wip2     w17
+
+// To avoid putting ifdefs arond the use of wMR, make sure it's defined.
+// IsNterpSupported returns false for configurations that don't have wMR (typically CMS).
+#ifndef wMR
+#define wMR w20
+#endif
+
+// Temporary registers while setting up a frame.
+#define xNEW_FP   x26
+#define xNEW_REFS x27
+#define CFI_NEW_REFS 27
+
+// +8 for the ArtMethod of the caller.
+#define OFFSET_TO_FIRST_ARGUMENT_IN_STACK (CALLEE_SAVES_SIZE + 8)
+
+/*
+ * Fetch the next instruction from xPC into wINST.  Does not advance xPC.
+ */
+.macro FETCH_INST
+    ldrh    wINST, [xPC]
+.endm
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances xPC
+ * to point to the next instruction.  "count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC.)
+ */
+.macro FETCH_ADVANCE_INST count
+    ldrh    wINST, [xPC, #((\count)*2)]!
+.endm
+
+/*
+ * Similar to FETCH_ADVANCE_INST, but does not update xPC.  Used to load
+ * xINST ahead of possible exception point.  Be sure to manually advance xPC
+ * later.
+ */
+.macro PREFETCH_INST count
+    ldrh    wINST, [xPC, #((\count)*2)]
+.endm
+
+/* Advance xPC by some number of code units. */
+.macro ADVANCE count
+  add  xPC, xPC, #((\count)*2)
+.endm
+
+/*
+ * Fetch the next instruction from an offset specified by "reg" and advance xPC.
+ * xPC to point to the next instruction.  "reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ */
+.macro FETCH_ADVANCE_INST_RB reg
+    add     xPC, xPC, \reg, sxtw
+    ldrh    wINST, [xPC]
+.endm
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "count" value is in 16-bit code units.  Does not advance xPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+.macro FETCH reg, count
+    ldrh    \reg, [xPC, #((\count)*2)]
+.endm
+
+.macro FETCH_S reg, count
+    ldrsh   \reg, [xPC, #((\count)*2)]
+.endm
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+.macro FETCH_B reg, count, byte
+    ldrb     \reg, [xPC, #((\count)*2+(\byte))]
+.endm
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+.macro GET_INST_OPCODE reg
+    and     \reg, xINST, #255
+.endm
+
+/*
+ * Begin executing the opcode in _reg.  Clobbers reg
+ */
+
+.macro GOTO_OPCODE reg
+    add     \reg, xIBASE, \reg, lsl #${handler_size_bits}
+    br      \reg
+.endm
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+.macro GET_VREG reg, vreg
+    ldr     \reg, [xFP, \vreg, uxtw #2]
+.endm
+.macro GET_VREG_OBJECT reg, vreg
+    ldr     \reg, [xREFS, \vreg, uxtw #2]
+.endm
+.macro SET_VREG reg, vreg
+    str     \reg, [xFP, \vreg, uxtw #2]
+    str     wzr, [xREFS, \vreg, uxtw #2]
+.endm
+.macro SET_VREG_OBJECT reg, vreg, tmpreg
+    str     \reg, [xFP, \vreg, uxtw #2]
+    str     \reg, [xREFS, \vreg, uxtw #2]
+.endm
+.macro SET_VREG_FLOAT reg, vreg
+    str     \reg, [xFP, \vreg, uxtw #2]
+    str     wzr, [xREFS, \vreg, uxtw #2]
+.endm
+
+/*
+ * Get/set the 64-bit value from a Dalvik register.
+ */
+.macro GET_VREG_WIDE reg, vreg
+    add     ip2, xFP, \vreg, uxtw #2
+    ldr     \reg, [ip2]
+.endm
+.macro SET_VREG_WIDE reg, vreg
+    add     ip2, xFP, \vreg, uxtw #2
+    str     \reg, [ip2]
+    add     ip2, xREFS, \vreg, uxtw #2
+    str     xzr, [ip2]
+.endm
+.macro GET_VREG_DOUBLE reg, vreg
+    add     ip2, xFP, \vreg, uxtw #2
+    ldr     \reg, [ip2]
+.endm
+.macro SET_VREG_DOUBLE reg, vreg
+    add     ip2, xFP, \vreg, uxtw #2
+    str     \reg, [ip2]
+    add     ip2, xREFS, \vreg, uxtw #2
+    str     xzr, [ip2]
+.endm
+
+/*
+ * Get the 32-bit value from a Dalvik register and sign-extend to 64-bit.
+ * Used to avoid an extra instruction in int-to-long.
+ */
+.macro GET_VREG_S reg, vreg
+    ldrsw   \reg, [xFP, \vreg, uxtw #2]
+.endm
+
+// An assembly entry that has a OatQuickMethodHeader prefix.
+.macro OAT_ENTRY name, end
+    .type \name, #function
+    .hidden \name
+    .global \name
+    .balign 16
+    // Padding of 8 bytes to get 16 bytes alignment of code entry.
+    .long 0
+    .long 0
+    // OatQuickMethodHeader.
+    .long 0
+    .long (\end - \name)
+\name:
+.endm
+
+.macro SIZE name
+    .size \name, .-\name
+.endm
+
+.macro NAME_START name
+    .type \name, #function
+    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
+    .global \name
+    /* Cache alignment for function entry */
+    .balign 16
+\name:
+.endm
+
+.macro NAME_END name
+  SIZE \name
+.endm
+
+// Macro for defining entrypoints into runtime. We don't need to save registers
+// (we're not holding references there), but there is no
+// kDontSave runtime method. So just use the kSaveRefsOnly runtime method.
+.macro NTERP_TRAMPOLINE name, helper
+ENTRY \name
+  SETUP_SAVE_REFS_ONLY_FRAME
+  bl \helper
+  RESTORE_SAVE_REFS_ONLY_FRAME
+  REFRESH_MARKING_REGISTER
+  RETURN_OR_DELIVER_PENDING_EXCEPTION
+END \name
+.endm
+
+.macro CLEAR_STATIC_VOLATILE_MARKER reg
+  and \reg, \reg, #-2
+.endm
+
+.macro CLEAR_INSTANCE_VOLATILE_MARKER reg
+  neg \reg, \reg
+.endm
+
+.macro EXPORT_PC
+    str    xPC, [xREFS, #-16]
+.endm
+
+.macro BRANCH
+    // Update method counter and do a suspend check if the branch is negative.
+    tbnz wINST, #31, 2f
+1:
+    add w2, wINST, wINST                // w2<- byte offset
+    FETCH_ADVANCE_INST_RB w2            // update xPC, load wINST
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    GOTO_OPCODE ip                      // jump to next instruction
+2:
+    ldr x0, [sp]
+    ldrh w2, [x0, #ART_METHOD_HOTNESS_COUNT_OFFSET]
+    add x2, x2, #1
+    and w2, w2, #0xffff
+    strh w2, [x0, #ART_METHOD_HOTNESS_COUNT_OFFSET]
+    // If the counter overflows, handle this in the runtime.
+    cbz w2, NterpHandleHotnessOverflow
+    // Otherwise, do a suspend check.
+    ldr x0, [xSELF, #THREAD_FLAGS_OFFSET]
+    ands x0, x0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
+    b.eq 1b
+    EXPORT_PC
+    bl    art_quick_test_suspend
+    b 1b
+.endm
+
+// Setup the stack to start executing the method. Expects:
+// - x0 to contain the ArtMethod
+//
+// Outputs
+// - ip contains the dex registers size
+// - x13 contains the old stack pointer.
+//
+// Uses ip, ip2, x13, x14 as temporaries.
+.macro SETUP_STACK_FRAME code_item, refs, fp, cfi_refs
+    // Fetch dex register size.
+    ldrh wip, [\code_item, #CODE_ITEM_REGISTERS_SIZE_OFFSET]
+    // Fetch outs size.
+    ldrh wip2, [\code_item, #CODE_ITEM_OUTS_SIZE_OFFSET]
+
+    // Compute required frame size: ((2 * ip) + ip1) * 4 + 24
+    // 24 is for saving the previous frame, pc, and method being executed.
+    add x14, ip, ip
+    add x14, x14, ip2
+    lsl x14, x14, #2
+    add x14, x14, #24
+
+    // Compute new stack pointer in x14
+    sub x14, sp, x14
+    // Alignment
+    and x14, x14, #-16
+
+    // Set reference and dex registers, align to pointer size for previous frame and dex pc.
+    add \refs, x14, ip2, lsl #2
+    add \refs, \refs, 28
+    and \refs, \refs, #(-__SIZEOF_POINTER__)
+    add \fp, \refs, ip, lsl #2
+
+    // Now setup the stack pointer.
+    mov x13, sp
+    .cfi_def_cfa_register x13
+    mov sp, x14
+    str x13, [\refs, #-8]
+    CFI_DEFINE_CFA_DEREF(\cfi_refs, -8, CALLEE_SAVES_SIZE)
+
+    // Put nulls in reference frame.
+    cbz ip, 2f
+    mov ip2, \refs
+1:
+    str xzr, [ip2], #8  // May clear vreg[0].
+    cmp ip2, \fp
+    b.lo 1b
+2:
+    // Save the ArtMethod.
+    str x0, [sp]
+.endm
+
+// Increase method hotness and do suspend check before starting executing the method.
+.macro START_EXECUTING_INSTRUCTIONS
+    ldr x0, [sp]
+    ldrh w2, [x0, #ART_METHOD_HOTNESS_COUNT_OFFSET]
+    add x2, x2, #1
+    and w2, w2, #0xffff
+    strh w2, [x0, #ART_METHOD_HOTNESS_COUNT_OFFSET]
+    // If the counter overflows, handle this in the runtime.
+    cbz w2, 2f
+    ldr x0, [xSELF, #THREAD_FLAGS_OFFSET]
+    tst x0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
+    b.ne 3f
+1:
+    FETCH_INST
+    GET_INST_OPCODE ip
+    GOTO_OPCODE ip
+2:
+    mov x1, xzr
+    mov x2, xFP
+    bl nterp_hot_method
+    b 1b
+3:
+    EXPORT_PC
+    bl art_quick_test_suspend
+    b 1b
+.endm
+
+.macro SPILL_ALL_CALLEE_SAVES
+    INCREASE_FRAME CALLEE_SAVES_SIZE
+    // Note: we technically don't need to save x19 and x20,
+    // but the runtime will expect those values to be there when unwinding
+    // (see Arm64Context::DoLongJump checking for the thread register).
+    SAVE_ALL_CALLEE_SAVES 0
+.endm
+
+.macro RESTORE_ALL_CALLEE_SAVES
+    // FP callee-saves
+    ldp d8, d9, [sp, #0]
+    ldp d10, d11, [sp, #16]
+    ldp d12, d13, [sp, #32]
+    ldp d14, d15, [sp, #48]
+
+    // GP callee-saves.
+    // No need to restore x19 (it's always the thread), and
+    // don't restore x20 (the marking register) as it may have been updated.
+    RESTORE_TWO_REGS x21, x22, 80
+    RESTORE_TWO_REGS x23, x24, 96
+    RESTORE_TWO_REGS x25, x26, 112
+    RESTORE_TWO_REGS x27, x28, 128
+    RESTORE_TWO_REGS x29, lr, 144
+
+    DECREASE_FRAME CALLEE_SAVES_SIZE
+.endm
+
+.macro SPILL_ALL_ARGUMENTS
+    INCREASE_FRAME 128
+    // GP arguments.
+    SAVE_TWO_REGS x0, x1, 0
+    SAVE_TWO_REGS x2, x3, 16
+    SAVE_TWO_REGS x4, x5, 32
+    SAVE_TWO_REGS x6, x7, 48
+
+    // FP arguments
+    stp d0, d1, [sp, #64]
+    stp d2, d3, [sp, #80]
+    stp d4, d5, [sp, #96]
+    stp d6, d7, [sp, #112]
+.endm
+
+.macro RESTORE_ALL_ARGUMENTS
+    // GP arguments.
+    RESTORE_TWO_REGS x0, x1, 0
+    RESTORE_TWO_REGS x2, x3, 16
+    RESTORE_TWO_REGS x4, x5, 32
+    RESTORE_TWO_REGS x6, x7, 48
+
+    // FP arguments
+    ldp d0, d1, [sp, #64]
+    ldp d2, d3, [sp, #80]
+    ldp d4, d5, [sp, #96]
+    ldp d6, d7, [sp, #112]
+    DECREASE_FRAME 128
+.endm
+
+// Helper to setup the stack after doing a nterp to nterp call. This will setup:
+// - xNEW_FP: the new pointer to dex registers
+// - xNEW_REFS: the new pointer to references
+// - xPC: the new PC pointer to execute
+// - x2: value in instruction to decode the number of arguments.
+// - x3: first dex register
+// - x4: top of dex register array
+//
+// The method expects:
+// - x0 to contain the ArtMethod
+// - x8 to contain the code item
+.macro SETUP_STACK_FOR_INVOKE
+   // We do the same stack overflow check as the compiler. See CanMethodUseNterp
+   // in how we limit the maximum nterp frame size.
+   sub x16, sp, #STACK_OVERFLOW_RESERVED_BYTES
+   ldr wzr, [x16]
+
+   // Spill all callee saves to have a consistent stack frame whether we
+   // are called by compiled code or nterp.
+   SPILL_ALL_CALLEE_SAVES
+
+   // Setup the frame.
+   SETUP_STACK_FRAME x8, xNEW_REFS, xNEW_FP, CFI_NEW_REFS
+   // Make x4 point to the top of the dex register array.
+   add x4, xNEW_FP, ip, uxtx #2
+
+   // Fetch instruction information before replacing xPC.
+   // TODO: move this down to the method that uses it, fetching it directly from wINST.
+   FETCH_B w2, 0, 1
+   // TODO: we could avoid this as instance invokes already fetch it.
+   FETCH w3, 2
+
+   // Set the dex pc pointer.
+   add xPC, x8, #CODE_ITEM_INSNS_OFFSET
+   CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
+.endm
+
+// Setup arguments based on a non-range nterp to nterp call, and start executing
+// the method. We expect:
+// - xNEW_FP: the new pointer to dex registers
+// - xNEW_REFS: the new pointer to references
+// - xPC: the new PC pointer to execute
+// - x2: number of arguments (bits 4-7), 5th argument if any (bits 0-3)
+// - x3: first dex register
+// - x4: top of dex register array
+// - x1: receiver if non-static.
+.macro SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
+   // /* op vA, vB, {vC...vG} */
+   asr ip2, x2, #4
+   cbz ip2, 6f
+   mov ip, #-4
+   cmp ip2, #2
+   b.lt 1f
+   b.eq 2f
+   cmp ip2, #4
+   b.lt 3f
+   b.eq 4f
+
+  // We use a decrementing ip to store references relative
+  // to xNEW_FP and dex registers relative to x4
+  //
+  // TODO: We could set up ip as the number of registers (this can be an additional output from
+  // SETUP_STACK_FOR_INVOKE) and then just decrement it by one before copying each arg.
+  // Maybe even introduce macros NEW_VREG_ADDRESS/NEW_VREG_REF_ADDRESS.
+5:
+   and         x2, x2, #15
+   GET_VREG_OBJECT w5, w2
+   str         w5, [xNEW_FP, ip]
+   GET_VREG    w5, w2
+   str         w5, [x4, ip]
+   sub         ip, ip, #4
+4:
+   asr         x2, x3, #12
+   GET_VREG_OBJECT w5, w2
+   str         w5, [xNEW_FP, ip]
+   GET_VREG    w5, w2
+   str         w5, [x4, ip]
+   sub         ip, ip, #4
+3:
+   ubfx        x2, x3, #8, #4
+   GET_VREG_OBJECT w5, w2
+   str         w5, [xNEW_FP, ip]
+   GET_VREG    w5, w2
+   str         w5, [x4, ip]
+   sub         ip, ip, #4
+2:
+   ubfx        x2, x3, #4, #4
+   GET_VREG_OBJECT w5, w2
+   str         w5, [xNEW_FP, ip]
+   GET_VREG    w5, w2
+   str         w5, [x4, ip]
+   .if !\is_string_init
+   sub         ip, ip, #4
+   .endif
+1:
+   .if \is_string_init
+   // Ignore the first argument
+   .elseif \is_static
+   and         x2, x3, #0xf
+   GET_VREG_OBJECT w5, w2
+   str         w5, [xNEW_FP, ip]
+   GET_VREG    w5, w2
+   str         w5, [x4, ip]
+   .else
+   str         w1, [xNEW_FP, ip]
+   str         w1, [x4, ip]
+   .endif
+
+6:
+   // Start executing the method.
+   mov xFP, xNEW_FP
+   mov xREFS, xNEW_REFS
+   CFI_DEFINE_CFA_DEREF(CFI_REFS, -8, CALLEE_SAVES_SIZE)
+   START_EXECUTING_INSTRUCTIONS
+.endm
+
+// Setup arguments based on a range nterp to nterp call, and start executing
+// the method.
+// - xNEW_FP: the new pointer to dex registers
+// - xNEW_REFS: the new pointer to references
+// - xPC: the new PC pointer to execute
+// - x2: number of arguments
+// - x3: first dex register
+// - x4: top of dex register array
+// - x1: receiver if non-static.
+//
+// Uses ip, ip2, x5, x6 as temporaries.
+.macro SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
+   mov ip, #-4
+   .if \is_string_init
+   // Ignore the first argument
+   sub x2, x2, #1
+   add x3, x3, #1
+   .elseif !\is_static
+   sub x2, x2, #1
+   add x3, x3, #1
+   .endif
+
+   cbz x2, 2f
+   add ip2, xREFS, x3, lsl #2  // pointer to first argument in reference array
+   add ip2, ip2, x2, lsl #2    // pointer to last argument in reference array
+   add x5, xFP, x3, lsl #2     // pointer to first argument in register array
+   add x6, x5, x2, lsl #2      // pointer to last argument in register array
+1:
+   ldr  w7, [ip2, #-4]!
+   str  w7, [xNEW_FP, ip]
+   sub  x2, x2, 1
+   ldr  w7, [x6, #-4]!
+   str  w7, [x4, ip]
+   sub ip, ip, 4
+   cbnz x2, 1b
+2:
+   .if \is_string_init
+   // Ignore first argument
+   .elseif !\is_static
+   str w1, [xNEW_FP, ip]
+   str w1, [x4, ip]
+   .endif
+   mov xFP, xNEW_FP
+   mov xREFS, xNEW_REFS
+   CFI_DEFINE_CFA_DEREF(CFI_REFS, -8, CALLEE_SAVES_SIZE)
+   START_EXECUTING_INSTRUCTIONS
+.endm
+
+.macro GET_SHORTY dest, is_interface, is_polymorphic, is_custom
+   stp x0, x1, [sp, #-16]!
+   .if \is_polymorphic
+   ldr x0, [sp, #16]
+   mov x1, xPC
+   bl NterpGetShortyFromInvokePolymorphic
+   .elseif \is_custom
+   ldr x0, [sp, #16]
+   mov x1, xPC
+   bl NterpGetShortyFromInvokeCustom
+   .elseif \is_interface
+   ldr x0, [sp, #16]
+   FETCH w1, 1
+   bl NterpGetShortyFromMethodId
+   .else
+   bl NterpGetShorty
+   .endif
+   mov \dest, x0
+   ldp x0, x1, [sp], #16
+.endm
+
+// Output: x8 contains the code item
+.macro GET_CODE_ITEM
+   // TODO: Get code item in a better way.
+   stp x0, x1, [sp, #-16]!
+   bl NterpGetCodeItem
+   mov x8, x0
+   ldp x0, x1, [sp], #16
+.endm
+
+.macro DO_ENTRY_POINT_CHECK call_compiled_code
+   // On entry, the method is x0, the instance is x1
+   adr x2, ExecuteNterpImpl
+   ldr x3, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64]
+   cmp x2, x3
+   b.ne  \call_compiled_code
+.endm
+
+.macro UPDATE_REGISTERS_FOR_STRING_INIT old_value, new_value
+   mov wip, wzr
+1:
+   GET_VREG_OBJECT wip2, wip
+   cmp wip2, \old_value
+   b.ne 2f
+   SET_VREG_OBJECT \new_value, wip
+2:
+   add wip, wip, #1
+   add ip2, xREFS, wip, uxtw #2
+   cmp ip2, xFP
+   b.ne 1b
+.endm
+
+// Puts the next floating point argument into the expected register,
+// fetching values based on a non-range invoke.
+// Uses ip and ip2.
+.macro LOOP_OVER_SHORTY_LOADING_FPS dreg, sreg, inst, shorty, arg_index, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1         // Load next character in shorty, and increment.
+    cbz wip, \finished              // if (wip == '\0') goto finished
+    cmp wip, #68                    // if (wip == 'D') goto FOUND_DOUBLE
+    b.eq 2f
+    cmp wip, #70                    // if (wip == 'F') goto FOUND_FLOAT
+    b.eq 3f
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    //  Handle extra argument in arg array taken by a long.
+    cmp wip, #74                   // if (wip != 'J') goto LOOP
+    b.ne 1b
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    b 1b                        // goto LOOP
+2:  // FOUND_DOUBLE
+    and ip, \inst, #0xf
+    GET_VREG wip, wip
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    cmp \arg_index, #4
+    b.eq 5f
+    and ip2, \inst, #0xf
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    b 6f
+5:
+    // TODO: Extract from wINST here and below? (Requires using a different register
+    // in the COMMON_INVOKE_NON_RANGE.)
+    FETCH_B wip2, 0, 1
+    and wip2, wip2, #0xf
+6:
+    GET_VREG wip2, wip2
+    add ip, ip, ip2, lsl #32
+    fmov \dreg, ip
+    b 4f
+3:  // FOUND_FLOAT
+    cmp \arg_index, #4
+    b.eq 7f
+    and ip, \inst, #0xf
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    b 8f
+7:
+    FETCH_B wip, 0, 1
+    and wip, wip, #0xf
+8:
+    GET_VREG \sreg, wip
+4:
+.endm
+
+// Puts the next int/long/object argument in the expected register,
+// fetching values based on a non-range invoke.
+// Uses ip and ip2.
+.macro LOOP_OVER_SHORTY_LOADING_GPRS gpr_reg64, gpr_reg32, inst, shorty, arg_index, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1         // Load next character in shorty, and increment.
+    cbz wip, \finished              // if (wip == '\0') goto finished
+    cmp wip, #74                    // if (wip == 'J') goto FOUND_LONG
+    b.eq 2f
+    cmp wip, #70                    // if (wip == 'F') goto SKIP_FLOAT
+    b.eq 3f
+    cmp wip, #68                    // if (wip == 'D') goto SKIP_DOUBLE
+    b.eq 4f
+    cmp \arg_index, #4
+    b.eq 7f
+    and ip, \inst, #0xf
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    b 8f
+7:
+    FETCH_B wip, 0, 1
+    and wip, wip, #0xf
+8:
+    GET_VREG \gpr_reg32, wip
+    b 5f
+2:  // FOUND_LONG
+    and ip, \inst, #0xf
+    GET_VREG wip, wip
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    cmp \arg_index, #4
+    b.eq 9f
+    and ip2, \inst, #0xf
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    b 10f
+9:
+    FETCH_B wip2, 0, 1
+    and wip2, wip2, #0xf
+10:
+    GET_VREG wip2, wip2
+    add \gpr_reg64, ip, ip2, lsl #32
+    b 5f
+3:  // SKIP_FLOAT
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    b 1b
+4:  // SKIP_DOUBLE
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    cmp \arg_index, #4
+    b.eq 1b
+    lsr \inst, \inst, #4
+    add \arg_index, \arg_index, #1
+    b 1b
+5:
+.endm
+
+.macro COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, suffix="", is_string_init=0, is_polymorphic=0, is_custom=0
+   .if \is_polymorphic
+   // We always go to compiled code for polymorphic calls.
+   .elseif \is_custom
+   // We always go to compiled code for custom calls.
+   .else
+     DO_ENTRY_POINT_CHECK .Lcall_compiled_code_\suffix
+     GET_CODE_ITEM
+     .if \is_string_init
+     bl nterp_to_nterp_string_init_non_range
+     .elseif \is_static
+     bl nterp_to_nterp_static_non_range
+     .else
+     bl nterp_to_nterp_instance_non_range
+     .endif
+     b .Ldone_return_\suffix
+   .endif
+
+.Lcall_compiled_code_\suffix:
+   GET_SHORTY xINST, \is_interface, \is_polymorphic, \is_custom
+   // From this point:
+   // - xINST contains shorty (in callee-save to switch over return value after call).
+   // - x0 contains method
+   // - x1 contains 'this' pointer for instance method.
+   add x9, xINST, #1  // shorty + 1  ; ie skip return arg character
+   FETCH w11, 2 // arguments
+   .if \is_string_init
+   lsr x11, x11, #4
+   mov x10, #1       // ignore first argument
+   .elseif \is_static
+   mov x10, xzr      // arg_index
+   .else
+   lsr x11, x11, #4
+   mov x10, #1       // ignore first argument
+   .endif
+   LOOP_OVER_SHORTY_LOADING_FPS d0, s0, x11, x9, x10, .Lxmm_setup_finished_\suffix
+   LOOP_OVER_SHORTY_LOADING_FPS d1, s1, x11, x9, x10, .Lxmm_setup_finished_\suffix
+   LOOP_OVER_SHORTY_LOADING_FPS d2, s2, x11, x9, x10, .Lxmm_setup_finished_\suffix
+   LOOP_OVER_SHORTY_LOADING_FPS d3, s3, x11, x9, x10, .Lxmm_setup_finished_\suffix
+   LOOP_OVER_SHORTY_LOADING_FPS d4, s4, x11, x9, x10, .Lxmm_setup_finished_\suffix
+.Lxmm_setup_finished_\suffix:
+   add x9, xINST, #1  // shorty + 1  ; ie skip return arg character
+   FETCH w11, 2 // arguments
+   .if \is_string_init
+   lsr x11, x11, #4
+   mov x10, #1       // ignore first argument
+   LOOP_OVER_SHORTY_LOADING_GPRS x1, w1, x11, x9, x10, .Lgpr_setup_finished_\suffix
+   .elseif \is_static
+   mov x10, xzr      // arg_index
+   LOOP_OVER_SHORTY_LOADING_GPRS x1, w1, x11, x9, x10, .Lgpr_setup_finished_\suffix
+   .else
+   lsr x11, x11, #4
+   mov x10, #1       // ignore first argument
+   .endif
+   LOOP_OVER_SHORTY_LOADING_GPRS x2, w2, x11, x9, x10, .Lgpr_setup_finished_\suffix
+   LOOP_OVER_SHORTY_LOADING_GPRS x3, w3, x11, x9, x10, .Lgpr_setup_finished_\suffix
+   LOOP_OVER_SHORTY_LOADING_GPRS x4, w4, x11, x9, x10, .Lgpr_setup_finished_\suffix
+   LOOP_OVER_SHORTY_LOADING_GPRS x5, w5, x11, x9, x10, .Lgpr_setup_finished_\suffix
+.Lgpr_setup_finished_\suffix:
+   .if \is_polymorphic
+   bl art_quick_invoke_polymorphic
+   .elseif \is_custom
+   bl art_quick_invoke_custom
+   .else
+      .if \is_interface
+      // Setup hidden argument
+      FETCH wip2, 1
+      .endif
+      ldr lr, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64]
+      blr lr
+   .endif
+   ldrb wip, [xINST]
+   cmp ip, #68       // Test if result type char == 'D'.
+   b.eq .Lreturn_double_\suffix
+   cmp ip, #70
+   b.ne .Ldone_return_\suffix
+.Lreturn_float_\suffix:
+   fmov w0, s0
+   b .Ldone_return_\suffix
+.Lreturn_double_\suffix:
+   fmov x0, d0
+.Ldone_return_\suffix:
+   /* resume execution of caller */
+   .if \is_string_init
+   FETCH w11, 2 // arguments
+   and x11, x11, #0xf
+   GET_VREG w1, w11
+   UPDATE_REGISTERS_FOR_STRING_INIT w1, w0
+   .endif
+
+   .if \is_polymorphic
+   FETCH_ADVANCE_INST 4
+   .else
+   FETCH_ADVANCE_INST 3
+   .endif
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+.endm
+
+// Puts the next floating point argument into the expected register,
+// fetching values based on a range invoke.
+// Uses ip as temporary.
+.macro LOOP_RANGE_OVER_SHORTY_LOADING_FPS dreg, sreg, shorty, arg_index, stack_index, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1         // Load next character in shorty, and increment.
+    cbz wip, \finished              // if (wip == '\0') goto finished
+    cmp wip, #68                    // if (wip == 'D') goto FOUND_DOUBLE
+    b.eq 2f
+    cmp wip, #70                    // if (wip == 'F') goto FOUND_FLOAT
+    b.eq 3f
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+    //  Handle extra argument in arg array taken by a long.
+    cmp wip, #74                    // if (wip != 'J') goto LOOP
+    b.ne 1b
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+    b 1b                        // goto LOOP
+2:  // FOUND_DOUBLE
+    GET_VREG_DOUBLE \dreg, \arg_index
+    add \arg_index, \arg_index, #2
+    add \stack_index, \stack_index, #2
+    b 4f
+3:  // FOUND_FLOAT
+    GET_VREG \sreg, \arg_index
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+4:
+.endm
+
+// Puts the next floating point argument into the expected stack slot,
+// fetching values based on a range invoke.
+// Uses ip as temporary.
+//
+// TODO: We could just copy all the vregs to the stack slots in a simple loop
+// without looking at the shorty at all. (We could also drop
+// the "stack_index" from the macros for loading registers.) We could also do
+// that conditionally if argument word count > 6; otherwise we know that all
+// args fit into registers.
+.macro LOOP_RANGE_OVER_FPs shorty, arg_index, stack_index, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1         // Load next character in shorty, and increment.
+    cbz wip, \finished              // if (wip == '\0') goto finished
+    cmp wip, #68                    // if (wip == 'D') goto FOUND_DOUBLE
+    b.eq 2f
+    cmp wip, #70                    // if (wip == 'F') goto FOUND_FLOAT
+    b.eq 3f
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+    //  Handle extra argument in arg array taken by a long.
+    cmp wip, #74                    // if (wip != 'J') goto LOOP
+    b.ne 1b
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+    b 1b                        // goto LOOP
+2:  // FOUND_DOUBLE
+    GET_VREG_WIDE ip, \arg_index
+    add ip2, sp, \stack_index, uxtw #2
+    str ip, [ip2]
+    add \arg_index, \arg_index, #2
+    add \stack_index, \stack_index, #2
+    b 1b
+3:  // FOUND_FLOAT
+    GET_VREG wip, \arg_index
+    str wip, [sp, \stack_index, uxtw #2]
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+    b 1b
+.endm
+
+// Puts the next int/long/object argument in the expected register,
+// fetching values based on a range invoke.
+// Uses ip as temporary.
+.macro LOOP_RANGE_OVER_SHORTY_LOADING_GPRS reg64, reg32, shorty, arg_index, stack_index, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1         // Load next character in shorty, and increment.
+    cbz wip, \finished              // if (wip == '\0') goto finished
+    cmp wip, #74                    // if (wip == 'J') goto FOUND_LONG
+    b.eq 2f
+    cmp wip, #70                   // if (wip == 'F') goto SKIP_FLOAT
+    b.eq 3f
+    cmp wip, #68                    // if (wip == 'D') goto SKIP_DOUBLE
+    b.eq 4f
+    GET_VREG \reg32, \arg_index
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+    b 5f
+2:  // FOUND_LONG
+    GET_VREG_WIDE \reg64, \arg_index
+    add \arg_index, \arg_index, #2
+    add \stack_index, \stack_index, #2
+    b 5f
+3:  // SKIP_FLOAT
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+    b 1b
+4:  // SKIP_DOUBLE
+    add \arg_index, \arg_index, #2
+    add \stack_index, \stack_index, #2
+    b 1b
+5:
+.endm
+
+// Puts the next int/long/object argument in the expected stack slot,
+// fetching values based on a range invoke.
+// Uses ip as temporary.
+.macro LOOP_RANGE_OVER_INTs shorty, arg_index, stack_index, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1         // Load next character in shorty, and increment.
+    cbz wip, \finished              // if (wip == '\0') goto finished
+    cmp wip, #74                    // if (wip == 'J') goto FOUND_LONG
+    b.eq 2f
+    cmp wip, #70                    // if (wip == 'F') goto SKIP_FLOAT
+    b.eq 3f
+    cmp wip, #68                    // if (wip == 'D') goto SKIP_DOUBLE
+    b.eq 4f
+    GET_VREG wip, \arg_index
+    str wip, [sp, \stack_index, uxtw #2]
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+    b 1b
+2:  // FOUND_LONG
+    GET_VREG_WIDE ip, \arg_index
+    add ip2, sp, \stack_index, uxtw #2
+    str ip, [ip2]
+    add \arg_index, \arg_index, #2
+    add \stack_index, \stack_index, #2
+    b 1b
+3:  // SKIP_FLOAT
+    add \arg_index, \arg_index, #1
+    add \stack_index, \stack_index, #1
+    b 1b
+4:  // SKIP_DOUBLE
+    add \arg_index, \arg_index, #2
+    add \stack_index, \stack_index, #2
+    b 1b
+.endm
+
+.macro COMMON_INVOKE_RANGE is_static=0, is_interface=0, suffix="", is_string_init=0, is_polymorphic=0, is_custom=0
+   .if \is_polymorphic
+   // We always go to compiled code for polymorphic calls.
+   .elseif \is_custom
+   // We always go to compiled code for custom calls.
+   .else
+     DO_ENTRY_POINT_CHECK .Lcall_compiled_code_range_\suffix
+     GET_CODE_ITEM
+     .if \is_string_init
+     bl nterp_to_nterp_string_init_range
+     .elseif \is_static
+     bl nterp_to_nterp_static_range
+     .else
+     bl nterp_to_nterp_instance_range
+     .endif
+     b .Ldone_return_range_\suffix
+   .endif
+
+.Lcall_compiled_code_range_\suffix:
+   GET_SHORTY xINST, \is_interface, \is_polymorphic, \is_custom
+   // From this point:
+   // - xINST contains shorty (in callee-save to switch over return value after call).
+   // - x0 contains method
+   // - x1 contains 'this' pointer for instance method.
+   add x9, xINST, #1  // shorty + 1  ; ie skip return arg character
+   FETCH w10, 2 // arguments
+   .if \is_string_init
+   add x10, x10, #1  // arg start index
+   mov x11, #1       // index in stack
+   .elseif \is_static
+   mov x11, xzr      // index in stack
+   .else
+   add x10, x10, #1  // arg start index
+   mov x11, #1       // index in stack
+   .endif
+   LOOP_RANGE_OVER_SHORTY_LOADING_FPS d0, s0, x9, w10, w11, .Lxmm_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_FPS d1, s1, x9, w10, w11, .Lxmm_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_FPS d2, s2, x9, w10, w11, .Lxmm_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_FPS d3, s3, x9, w10, w11, .Lxmm_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_FPS d4, s4, x9, w10, w11, .Lxmm_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_FPS d5, s5, x9, w10, w11, .Lxmm_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_FPS d6, s6, x9, w10, w11, .Lxmm_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_FPS d7, s7, x9, w10, w11, .Lxmm_setup_finished_range_\suffix
+   // Store in the outs array (stored above the ArtMethod in the stack)
+   add x11, x11, #2 // Add two words for the ArtMethod stored before the outs.
+   LOOP_RANGE_OVER_FPs x9, w10, w11, .Lxmm_setup_finished_range_\suffix
+.Lxmm_setup_finished_range_\suffix:
+   add x9, xINST, #1  // shorty + 1  ; ie skip return arg character
+   FETCH w10, 2 // arguments
+   .if \is_string_init
+   add x10, x10, #1  // arg start index
+   mov x11, #1       // index in stack
+   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS x1, w1, x9, w10, w11, .Lgpr_setup_finished_\suffix
+   .elseif \is_static
+   mov x11, xzr      // index in stack
+   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS x1, w1, x9, w10, w11 .Lgpr_setup_finished_\suffix
+   .else
+   add x10, x10, #1  // arg start index
+   mov x11, #1       // index in stack
+   .endif
+   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS x2, w2, x9, w10, w11, .Lgpr_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS x3, w3, x9, w10, w11, .Lgpr_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS x4, w4, x9, w10, w11, .Lgpr_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS x5, w5, x9, w10, w11, .Lgpr_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS x6, w6, x9, w10, w11, .Lgpr_setup_finished_range_\suffix
+   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS x7, w7, x9, w10, w11, .Lgpr_setup_finished_range_\suffix
+   // Store in the outs array (stored above the ArtMethod in the stack)
+   add x11, x11, #2 // Add two words for the ArtMethod stored before the outs.
+   LOOP_RANGE_OVER_INTs x9, w10, w11, .Lgpr_setup_finished_range_\suffix
+.Lgpr_setup_finished_range_\suffix:
+   .if \is_polymorphic
+   bl art_quick_invoke_polymorphic
+   .elseif \is_custom
+   bl art_quick_invoke_custom
+   .else
+      .if \is_interface
+      // Setup hidden argument
+      FETCH wip2, 1
+      .endif
+      ldr lr, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64]
+      blr lr
+   .endif
+   ldrb wip, [xINST]
+   cmp ip, #68       // Test if result type char == 'D'.
+   b.eq .Lreturn_double_range_\suffix
+   cmp ip, #70
+   b.ne .Ldone_return_\suffix
+.Lreturn_float_range_\suffix:
+   fmov w0, s0
+   b .Ldone_return_range_\suffix
+.Lreturn_double_range_\suffix:
+   fmov x0, d0
+.Ldone_return_range_\suffix:
+   /* resume execution of caller */
+   .if \is_string_init
+   FETCH w11, 2 // arguments
+   GET_VREG w1, w11
+   UPDATE_REGISTERS_FOR_STRING_INIT w1, w0
+   .endif
+
+   .if \is_polymorphic
+    FETCH_ADVANCE_INST 4
+   .else
+   FETCH_ADVANCE_INST 3
+   .endif
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+.endm
+
+// Fetch some information from the thread cache.
+// Uses ip and ip2 as temporaries.
+.macro FETCH_FROM_THREAD_CACHE dest_reg, slow_path
+   add      ip, xSELF, #THREAD_INTERPRETER_CACHE_OFFSET       // cache address
+   ubfx     ip2, xPC, #2, #THREAD_INTERPRETER_CACHE_SIZE_LOG2  // entry index
+   add      ip, ip, ip2, lsl #4            // entry address within the cache
+   ldp      ip, \dest_reg, [ip]           // entry key (pc) and value (offset)
+   cmp      ip, xPC
+   b.ne \slow_path
+.endm
+
+// Helper for static field get.
+.macro OP_SGET load="ldr", volatile_load="ldar", maybe_extend="", wide="0"
+   // Fast-path which gets the field from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 4f
+1:
+   ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET]
+   ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET]
+   cbnz wMR, 3f
+2:
+   lsr w2, wINST, #8              // w2 <- A
+   .if \wide
+   ldr x0, [x0, x1]
+   SET_VREG_WIDE x0, w2          // fp[A] <- value
+   .else
+   \load w0, [x0, x1]
+   SET_VREG w0, w2               // fp[A] <- value
+   .endif
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+3:
+   bl art_quick_read_barrier_mark_reg00
+   b 2b
+4:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   EXPORT_PC
+   bl nterp_get_static_field
+   tbz x0, #0, 1b
+   CLEAR_STATIC_VOLATILE_MARKER x0
+   ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET]
+   ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET]
+   cbnz wMR, 7f
+5:
+   lsr w2, wINST, #8              // w2 <- A
+   add x0, x0, x1
+   .if \wide
+   ldar x0, [x0]
+   SET_VREG_WIDE x0, w2          // fp[A] <- value
+   .else
+   \volatile_load w0, [x0]
+   \maybe_extend
+   SET_VREG w0, w2               // fp[A] <- value
+   .endif
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+7:
+   bl art_quick_read_barrier_mark_reg00
+   b 5b
+.endm
+
+// Helper for static field put.
+.macro OP_SPUT store="str", volatile_store="stlr", wide="0":
+   // Fast-path which gets the field from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 4f
+1:
+   ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET]
+   ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET]
+   cbnz wMR, 3f
+2:
+   lsr w2, wINST, #8              // w2 <- A
+   .if \wide
+   GET_VREG_WIDE x2, w2           // x2 <- v[A]
+   \store    x2, [x0, x1]
+   .else
+   GET_VREG w2, w2                // w2 <- v[A]
+   \store    w2, [x0, x1]
+   .endif
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+3:
+   bl art_quick_read_barrier_mark_reg00
+   b 2b
+4:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   EXPORT_PC
+   bl nterp_get_static_field
+   tbz x0, #0, 1b
+   CLEAR_STATIC_VOLATILE_MARKER x0
+   ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET]
+   ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET]
+   cbnz wMR, 6f
+5:
+   lsr w2, wINST, #8              // w2 <- A
+   add x0, x0, x1
+   .if \wide
+   GET_VREG_WIDE x2, w2           // x2 <- v[A]
+   \volatile_store    x2, [x0]
+   .else
+   GET_VREG w2, w2                // w2 <- v[A]
+   \volatile_store    w2, [x0]
+   .endif
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+6:
+   bl art_quick_read_barrier_mark_reg00
+   b 5b
+.endm
+
+
+// Helper for instance field put.
+.macro OP_IPUT store="str", volatile_store="stlr", wide="0":
+   // Fast-path which gets the field from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 2f
+1:
+   ubfx    w1, wINST, #8, #4           // w1<- A
+   lsr     w2, wINST, #12              // w2<- B
+   GET_VREG w2, w2                     // vB (object we're operating on)
+   cbz w2, common_errNullObject
+   .if \wide
+   GET_VREG_WIDE x1, w1                // x1<- fp[A]/fp[A+1]
+   \store x1, [x2, x0]
+   .else
+   GET_VREG w1, w1                     // w1 <- v[A]
+   \store w1, [x2, x0]
+   .endif
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+2:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   EXPORT_PC
+   bl nterp_get_instance_field_offset
+   tbz w0, #31, 1b
+   CLEAR_INSTANCE_VOLATILE_MARKER w0
+   ubfx    w1, wINST, #8, #4           // w1<- A
+   lsr     w2, wINST, #12              // w2<- B
+   GET_VREG w2, w2                     // vB (object we're operating on)
+   cbz w2, common_errNullObject
+   add x2, x2, x0
+   .if \wide
+   GET_VREG_WIDE x1, w1                // x1<- fp[A]/fp[A+1]
+   \volatile_store x1, [x2]
+   .else
+   GET_VREG w1, w1                     // w1 <- v[A]
+   \volatile_store w1, [x2]
+   .endif
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+.endm
+
+// Helper for instance field get.
+.macro OP_IGET load="ldr", volatile_load="ldar", maybe_extend="", wide="0"
+   // Fast-path which gets the field from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 2f
+1:
+   lsr     w2, wINST, #12              // w2<- B
+   GET_VREG w3, w2                     // w3<- object we're operating on
+   ubfx    w2, wINST, #8, #4           // w2<- A
+   cbz     w3, common_errNullObject    // object was null
+   .if \wide
+   \load x0, [x3, x0]
+   SET_VREG_WIDE x0, w2                // fp[A] <- value
+   .else
+   \load w0, [x3, x0]
+   SET_VREG w0, w2                     // fp[A] <- value
+   .endif
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+2:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   EXPORT_PC
+   bl nterp_get_instance_field_offset
+   tbz w0, #31, 1b
+   CLEAR_INSTANCE_VOLATILE_MARKER w0
+   lsr     w2, wINST, #12              // w2<- B
+   GET_VREG w3, w2                     // w3<- object we're operating on
+   ubfx    w2, wINST, #8, #4           // w2<- A
+   cbz     w3, common_errNullObject    // object was null
+   add x3, x3, x0
+   .if \wide
+   \volatile_load x0, [x3]
+   SET_VREG_WIDE x0, w2                // fp[A] <- value
+   .else
+   \volatile_load w0, [x3]
+   \maybe_extend
+   SET_VREG w0, w2                     // fp[A] <- value
+   .endif
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+.endm
+
+// Puts the next int/long/object parameter passed in physical register
+// in the expected dex register array entry, and in case of object in the
+// expected reference array entry.
+.macro LOOP_OVER_SHORTY_STORING_GPRS gpr_64, gpr_32, shorty, arg_offset, regs, refs, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1       // Load next character in shorty, and increment.
+    cbz wip, \finished            // if (wip == '\0') goto finished
+    cmp wip, #74                  // if (wip == 'J') goto FOUND_LONG
+    b.eq 2f
+    cmp wip, #70                  // if (wip == 'F') goto SKIP_FLOAT
+    b.eq 3f
+    cmp wip, #68                  // if (wip == 'D') goto SKIP_DOUBLE
+    b.eq 4f
+    str \gpr_32, [\regs, \arg_offset]
+    cmp wip, #76                  // if (wip != 'L') goto NOT_REFERENCE
+    b.ne 6f
+    str \gpr_32, [\refs, \arg_offset]
+6:  // NOT_REFERENCE
+    add \arg_offset, \arg_offset, #4
+    b 5f
+2:  // FOUND_LONG
+    str \gpr_64, [\regs, \arg_offset]
+    add \arg_offset, \arg_offset, #8
+    b 5f
+3:  // SKIP_FLOAT
+    add \arg_offset, \arg_offset, #4
+    b 1b
+4:  // SKIP_DOUBLE
+    add \arg_offset, \arg_offset, #8
+    b 1b
+5:
+.endm
+
+// Puts the next floating point parameter passed in physical register
+// in the expected dex register array entry.
+// Uses ip as temporary.
+.macro LOOP_OVER_SHORTY_STORING_FPS dreg, sreg, shorty, arg_offset, fp, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1                 // Load next character in shorty, and increment.
+    cbz wip, \finished                      // if (wip == '\0') goto finished
+    cmp wip, #68                            // if (wip == 'D') goto FOUND_DOUBLE
+    b.eq 2f
+    cmp wip, #70                            // if (wip == 'F') goto FOUND_FLOAT
+    b.eq 3f
+    add \arg_offset, \arg_offset, #4
+    //  Handle extra argument in arg array taken by a long.
+    cmp wip, #74                            // if (wip != 'J') goto LOOP
+    b.ne 1b
+    add \arg_offset, \arg_offset, #4
+    b 1b                        // goto LOOP
+2:  // FOUND_DOUBLE
+    str \dreg, [\fp, \arg_offset]
+    add \arg_offset, \arg_offset, #8
+    b 4f
+3:  // FOUND_FLOAT
+    str \sreg, [\fp, \arg_offset]
+    add \arg_offset, \arg_offset, #4
+4:
+.endm
+
+// Puts the next floating point parameter passed in stack
+// in the expected dex register array entry.
+// Uses ip as temporary.
+//
+// TODO: Or we could just spill regs to the reserved slots in the caller's
+// frame and copy all regs in a simple loop. This time, however, we would
+// need to look at the shorty anyway to look for the references.
+// (The trade-off is different for passing arguments and receiving them.)
+.macro LOOP_OVER_FPs shorty, arg_offset, regs, stack_ptr, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1                 // Load next character in shorty, and increment.
+    cbz wip, \finished                      // if (wip == '\0') goto finished
+    cmp wip, #68                            // if (wip == 'D') goto FOUND_DOUBLE
+    b.eq 2f
+    cmp wip, #70                            // if (wip == 'F') goto FOUND_FLOAT
+    b.eq 3f
+    add \arg_offset, \arg_offset, #4
+    //  Handle extra argument in arg array taken by a long.
+    cmp wip, #74                            // if (wip != 'J') goto LOOP
+    b.ne 1b
+    add \arg_offset, \arg_offset, #4
+    b 1b                        // goto LOOP
+2:  // FOUND_DOUBLE
+    add ip, \stack_ptr, \arg_offset
+    ldr ip, [ip,  #OFFSET_TO_FIRST_ARGUMENT_IN_STACK]
+    str ip, [\regs, \arg_offset]
+    add \arg_offset, \arg_offset, #8
+    b 1b
+3:  // FOUND_FLOAT
+    add ip, \stack_ptr, \arg_offset
+    ldr wip, [ip,  #OFFSET_TO_FIRST_ARGUMENT_IN_STACK]
+    str wip, [\regs, \arg_offset]
+    add \arg_offset, \arg_offset, #4
+    b 1b
+.endm
+
+// Puts the next int/long/object parameter passed in stack
+// in the expected dex register array entry, and in case of object in the
+// expected reference array entry.
+// Uses ip and ip2 as temporary.
+.macro LOOP_OVER_INTs shorty, arg_offset, regs, refs, stack_ptr, finished
+1: // LOOP
+    ldrb wip, [\shorty], #1       // Load next character in shorty, and increment.
+    cbz wip, \finished            // if (wip == '\0') goto finished
+    cmp wip, #74                  // if (wip == 'J') goto FOUND_LONG
+    b.eq 2f
+    cmp wip, #70                  // if (wip == 'F') goto SKIP_FLOAT
+    b.eq 3f
+    cmp wip, #68                  // if (wip == 'D') goto SKIP_DOUBLE
+    b.eq 4f
+    add ip2, \stack_ptr, \arg_offset
+    ldr wip2, [ip2,  #OFFSET_TO_FIRST_ARGUMENT_IN_STACK]
+    str wip2, [\regs, \arg_offset]
+    cmp wip, #76                  // if (wip != 'L') goto loop
+    b.ne 3f
+    str wip2, [\refs, \arg_offset]
+    add \arg_offset, \arg_offset, #4
+    b 1b
+2:  // FOUND_LONG
+    add ip, \stack_ptr, \arg_offset
+    ldr ip, [ip,  #OFFSET_TO_FIRST_ARGUMENT_IN_STACK]
+    str ip, [\regs, \arg_offset]
+    add \arg_offset, \arg_offset, #8
+    b 1b
+3:  // SKIP_FLOAT
+    add \arg_offset, \arg_offset, #4
+    b 1b
+4:  // SKIP_DOUBLE
+    add \arg_offset, \arg_offset, #8
+    b 1b
+.endm
+
+%def entry():
+/*
+ * ArtMethod entry point.
+ *
+ * On entry:
+ *  x0   ArtMethod* callee
+ *  rest  method parameters
+ */
+
+OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
+    .cfi_startproc
+    sub x16, sp, #STACK_OVERFLOW_RESERVED_BYTES
+    ldr wzr, [x16]
+    /* Spill callee save regs */
+    SPILL_ALL_CALLEE_SAVES
+
+    // TODO: Get shorty in a better way and remove below
+    SPILL_ALL_ARGUMENTS
+
+    // Save method in callee-save xINST
+    mov xINST, x0
+    bl NterpGetShorty
+    // Save shorty in callee-save xIBASE.
+    mov xIBASE, x0
+    mov x0, xINST
+    bl NterpGetCodeItem
+    mov xPC, x0
+
+    RESTORE_ALL_ARGUMENTS
+
+    // Setup the stack for executing the method.
+    SETUP_STACK_FRAME xPC, xREFS, xFP, CFI_REFS
+
+    // Setup the parameters
+    ldrh wip2, [xPC, #CODE_ITEM_INS_SIZE_OFFSET]
+    cbz wip2, .Lxmm_setup_finished
+
+    sub ip2, ip, ip2
+    lsl x8, ip2, #2 // x8 is now the offset for inputs into the registers array.
+
+    // Setup shorty, pointer to inputs in FP and pointer to inputs in REFS
+    add x9, xIBASE, #1  // shorty + 1  ; ie skip return arg character
+    add x10, xFP, x8
+    add x11, xREFS, x8
+
+    ldr wip, [x0, #ART_METHOD_ACCESS_FLAGS_OFFSET]
+    // TODO: could be TBNZ but we'd need a constant for log2(ART_METHOD_IS_STATIC_FLAG).
+    tst wip, #ART_METHOD_IS_STATIC_FLAG
+    b.ne .Lhandle_static_method
+    str w1, [x10], #4
+    str w1, [x11], #4
+    add x13, x13, #4
+    mov x12, #0
+    b .Lcontinue_setup_gprs
+.Lhandle_static_method:
+    mov x12, #0
+    LOOP_OVER_SHORTY_STORING_GPRS x1, w1, x9, x12, x10, x11, .Lgpr_setup_finished
+.Lcontinue_setup_gprs:
+    LOOP_OVER_SHORTY_STORING_GPRS x2, w2, x9, x12, x10, x11, .Lgpr_setup_finished
+    LOOP_OVER_SHORTY_STORING_GPRS x3, w3, x9, x12, x10, x11, .Lgpr_setup_finished
+    LOOP_OVER_SHORTY_STORING_GPRS x4, w4, x9, x12, x10, x11, .Lgpr_setup_finished
+    LOOP_OVER_SHORTY_STORING_GPRS x5, w5, x9, x12, x10, x11, .Lgpr_setup_finished
+    LOOP_OVER_SHORTY_STORING_GPRS x6, w6, x9, x12, x10, x11, .Lgpr_setup_finished
+    LOOP_OVER_SHORTY_STORING_GPRS x7, w7, x9, x12, x10, x11, .Lgpr_setup_finished
+    LOOP_OVER_INTs x9, x12, x10, x11, x13, .Lgpr_setup_finished
+.Lgpr_setup_finished:
+    add x9, xIBASE, #1  // shorty + 1  ; ie skip return arg character
+    mov x12, #0  // reset counter
+    LOOP_OVER_SHORTY_STORING_FPS d0, s0, x9, x12, x10, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_STORING_FPS d1, s1, x9, x12, x10, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_STORING_FPS d2, s2, x9, x12, x10, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_STORING_FPS d3, s3, x9, x12, x10, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_STORING_FPS d4, s4, x9, x12, x10, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_STORING_FPS d5, s5, x9, x12, x10, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_STORING_FPS d6, s6, x9, x12, x10, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_STORING_FPS d7, s7, x9, x12, x10, .Lxmm_setup_finished
+    LOOP_OVER_FPs x9, x12, x10, x13, .Lxmm_setup_finished
+.Lxmm_setup_finished:
+    // Set the dex pc pointer.
+    add xPC, xPC, #CODE_ITEM_INSNS_OFFSET
+    CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
+
+    // Set rIBASE
+    adr xIBASE, artNterpAsmInstructionStart
+    /* start executing the instruction at xPC */
+    START_EXECUTING_INSTRUCTIONS
+    /* NOTE: no fallthrough */
+    // cfi info continues, and covers the whole nterp implementation.
+    SIZE ExecuteNterpImpl
+
+%def opcode_pre():
+
+%def helpers():
+
+%def footer():
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+// Note: mterp also uses the common_* names below for helpers, but that's OK
+// as the C compiler compiled each interpreter separately.
+common_errDivideByZero:
+    EXPORT_PC
+    bl art_quick_throw_div_zero
+
+// Expect array in w0, index in w1, length in w2
+common_errArrayIndex:
+    EXPORT_PC
+    mov x0, x1
+    mov x1, x3
+    bl art_quick_throw_array_bounds
+
+common_errNullObject:
+    EXPORT_PC
+    bl art_quick_throw_null_pointer_exception
+
+NterpCommonInvokeStatic:
+    COMMON_INVOKE_NON_RANGE is_static=1, suffix="invokeStatic"
+
+NterpCommonInvokeStaticRange:
+    COMMON_INVOKE_RANGE is_static=1, suffix="invokeStatic"
+
+NterpCommonInvokeInstance:
+    COMMON_INVOKE_NON_RANGE suffix="invokeInstance"
+
+NterpCommonInvokeInstanceRange:
+    COMMON_INVOKE_RANGE suffix="invokeInstance"
+
+NterpCommonInvokeInterface:
+    COMMON_INVOKE_NON_RANGE is_interface=1, suffix="invokeInterface"
+
+NterpCommonInvokeInterfaceRange:
+    COMMON_INVOKE_RANGE is_interface=1, suffix="invokeInterface"
+
+NterpCommonInvokePolymorphic:
+    COMMON_INVOKE_NON_RANGE is_polymorphic=1, suffix="invokePolymorphic"
+
+NterpCommonInvokePolymorphicRange:
+    COMMON_INVOKE_RANGE is_polymorphic=1, suffix="invokePolymorphic"
+
+NterpCommonInvokeCustom:
+    COMMON_INVOKE_NON_RANGE is_static=1, is_custom=1, suffix="invokeCustom"
+
+NterpCommonInvokeCustomRange:
+    COMMON_INVOKE_RANGE is_static=1, is_custom=1, suffix="invokeCustom"
+
+NterpHandleStringInit:
+   COMMON_INVOKE_NON_RANGE is_string_init=1, suffix="stringInit"
+
+NterpHandleStringInitRange:
+   COMMON_INVOKE_RANGE is_string_init=1, suffix="stringInit"
+
+NterpNewInstance:
+   EXPORT_PC
+   // Fast-path which gets the class from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 2f
+   cbnz wMR, 3f
+4:
+   ldr lr, [xSELF, #THREAD_ALLOC_OBJECT_ENTRYPOINT_OFFSET]
+   blr lr
+1:
+   lsr w1, wINST, #8                    // w1 <- A
+   SET_VREG_OBJECT w0, w1               // fp[A] <- value
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+2:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   bl nterp_get_class_or_allocate_object
+   b 1b
+3:
+   bl art_quick_read_barrier_mark_reg00
+   b 4b
+
+NterpNewArray:
+   /* new-array vA, vB, class@CCCC */
+   EXPORT_PC
+   // Fast-path which gets the class from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 2f
+   cbnz wMR, 3f
+1:
+   lsr     w1, wINST, #12              // w1<- B
+   GET_VREG w1, w1                     // w1<- vB (array length)
+   ldr lr, [xSELF, #THREAD_ALLOC_ARRAY_ENTRYPOINT_OFFSET]
+   blr lr
+   ubfx    w1, wINST, #8, #4           // w1<- A
+   SET_VREG_OBJECT w0, w1
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+2:
+   mov x0, xSELF
+   ldr x1, [sp, 0]
+   mov x2, xPC
+   bl nterp_get_class_or_allocate_object
+   b 1b
+3:
+   bl art_quick_read_barrier_mark_reg00
+   b 1b
+
+NterpPutObjectInstanceField:
+   // Fast-path which gets the field from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 3f
+1:
+   ubfx    w1, wINST, #8, #4           // w1<- A
+   lsr     w2, wINST, #12              // w2<- B
+   GET_VREG w2, w2                     // vB (object we're operating on)
+   cbz w2, common_errNullObject        // is object null?
+   GET_VREG w1, w1                     // w1 <- v[A]
+   str w1, [x2, x0]
+   cbz w1, 2f
+   ldr x1, [xSELF, #THREAD_CARD_TABLE_OFFSET]
+   lsr w3, w2, #CARD_TABLE_CARD_SHIFT
+   strb w1, [x1, x3]
+2:
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+3:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   EXPORT_PC
+   bl nterp_get_instance_field_offset
+   tbz w0, #31, 1b
+   CLEAR_INSTANCE_VOLATILE_MARKER w0
+   ubfx    w1, wINST, #8, #4           // w1<- A
+   lsr     w2, wINST, #12              // w2<- B
+   GET_VREG w2, w2                     // vB (object we're operating on)
+   cbz w2, common_errNullObject        // is object null?
+   GET_VREG w1, w1                     // w1 <- v[A]
+   add x3, x2, x0
+   stlr w1, [x3]
+   cbz w1, 2b
+   ldr x1, [xSELF, #THREAD_CARD_TABLE_OFFSET]
+   lsr w3, w2, #CARD_TABLE_CARD_SHIFT
+   strb w1, [x1, x3]
+   b 2b
+
+NterpGetObjectInstanceField:
+   // Fast-path which gets the field from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 4f
+1:
+   ubfx    w1, wINST, #8, #4           // w1<- A
+   lsr     w2, wINST, #12              // w2<- B
+   GET_VREG w2, w2                     // vB (object we're operating on)
+   cbz w2, common_errNullObject
+   ldr w0, [x2, x0]
+   cbnz wMR, 3f
+2:
+   SET_VREG_OBJECT w0, w1              // fp[A] <- value
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+3:
+   bl art_quick_read_barrier_mark_reg00
+   b 2b
+4:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   EXPORT_PC
+   bl nterp_get_instance_field_offset
+   tbz w0, #31, 1b
+   CLEAR_INSTANCE_VOLATILE_MARKER w0
+   ubfx    w1, wINST, #8, #4           // w1<- A
+   lsr     w2, wINST, #12              // w2<- B
+   GET_VREG w2, w2                     // vB (object we're operating on)
+   cbz w2, common_errNullObject
+   add x2, x2, x0
+   ldar w0, [x2]
+   cbnz wMR, 6f
+5:
+   SET_VREG_OBJECT w0, w1              // fp[A] <- value
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+6:
+   bl art_quick_read_barrier_mark_reg00
+   b 5b
+
+NterpPutObjectStaticField:
+   // Fast-path which gets the field from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 5f
+1:
+   ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET]
+   ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET]
+   cbnz wMR, 4f
+2:
+   lsr w2, wINST, #8                    // w2 <- A
+   GET_VREG w2, w2
+   str w2, [x0, x1]
+   cbz w2, 3f
+   ldr x1, [xSELF, #THREAD_CARD_TABLE_OFFSET]
+   lsr w3, w0, #CARD_TABLE_CARD_SHIFT
+   strb w1, [x1, x3]
+3:
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+4:
+   bl art_quick_read_barrier_mark_reg00
+   b 2b
+5:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   EXPORT_PC
+   bl nterp_get_static_field
+   tbz x0, #0, 1b
+   CLEAR_STATIC_VOLATILE_MARKER x0
+   ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET]
+   ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET]
+   cbnz wMR, 7f
+6:
+   lsr w2, wINST, #8                    // w1 <- A
+   GET_VREG w2, w2
+   add ip, x0, x1
+   stlr w2, [ip]
+   cbz w2, 3b
+   ldr x1, [xSELF, #THREAD_CARD_TABLE_OFFSET]
+   lsr w3, w0, #CARD_TABLE_CARD_SHIFT
+   strb w1, [x1, x3]
+   b 3b
+7:
+   bl art_quick_read_barrier_mark_reg00
+   b 6b
+
+NterpGetObjectStaticField:
+   // Fast-path which gets the field from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 4f
+1:
+   ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET]
+   ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET]
+   cbnz wMR, 3f
+   ldr w0, [x0, x1]
+   // No need to check the marking register, we know it's not set here.
+2:
+   lsr w1, wINST, #8                    // w1 <- A
+   SET_VREG_OBJECT w0, w1               // fp[A] <- value
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+3:
+   bl art_quick_read_barrier_mark_reg00
+   ldr w0, [x0, x1]
+   // Here, we know the marking register is set.
+   bl art_quick_read_barrier_mark_reg00
+   b 2b
+4:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   EXPORT_PC
+   bl nterp_get_static_field
+   tbz x0, #0, 1b
+   CLEAR_STATIC_VOLATILE_MARKER x0
+   ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET]
+   ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET]
+   cbnz wMR, 7f
+5:
+   add x0, x0, x1
+   ldar w0, [x0]
+   cbnz wMR, 8f
+6:
+   lsr w1, wINST, #8                    // w1 <- A
+   SET_VREG_OBJECT w0, w1               // fp[A] <- value
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+7:
+   bl art_quick_read_barrier_mark_reg00
+   b 5b
+8:
+   bl art_quick_read_barrier_mark_reg00
+   b 6b
+
+NterpGetBooleanStaticField:
+  OP_SGET load="ldrb", volatile_load="ldarb", maybe_extend="", wide=0
+
+NterpGetByteStaticField:
+  OP_SGET load="ldrsb", volatile_load="ldarb", maybe_extend="sbfx w0, w0, #0, #8", wide=0
+
+NterpGetCharStaticField:
+  OP_SGET load="ldrh", volatile_load="ldarh", maybe_extend="", wide=0
+
+NterpGetShortStaticField:
+  OP_SGET load="ldrsh", volatile_load="ldarh", maybe_extend="sbfx w0, w0, #0, #16", wide=0
+
+NterpGetWideStaticField:
+  OP_SGET load="ldr", volatile_load="ldar", maybe_extend="", wide=1
+
+NterpGetIntStaticField:
+  OP_SGET load="ldr", volatile_load="ldar", maybe_extend="", wide=0
+
+NterpPutStaticField:
+  OP_SPUT store="str", volatile_store="stlr", wide=0
+
+NterpPutBooleanStaticField:
+NterpPutByteStaticField:
+  OP_SPUT store="strb", volatile_store="stlrb", wide=0
+
+NterpPutCharStaticField:
+NterpPutShortStaticField:
+  OP_SPUT store="strh", volatile_store="stlrh", wide=0
+
+NterpPutWideStaticField:
+  OP_SPUT store="str", volatile_store="stlr", wide=1
+
+NterpPutInstanceField:
+  OP_IPUT store="str", volatile_store="stlr", wide=0
+
+NterpPutBooleanInstanceField:
+NterpPutByteInstanceField:
+  OP_IPUT store="strb", volatile_store="stlrb", wide=0
+
+NterpPutCharInstanceField:
+NterpPutShortInstanceField:
+  OP_IPUT store="strh", volatile_store="stlrh", wide=0
+
+NterpPutWideInstanceField:
+  OP_IPUT store="str", volatile_store="stlr", wide=1
+
+NterpGetBooleanInstanceField:
+  OP_IGET load="ldrb", volatile_load="ldarb", maybe_extend="", wide=0
+
+NterpGetByteInstanceField:
+  OP_IGET load="ldrsb", volatile_load="ldarb", maybe_extend="sbfx w0, w0, #0, #8", wide=0
+
+NterpGetCharInstanceField:
+  OP_IGET load="ldrh", volatile_load="ldarh", maybe_extend="", wide=0
+
+NterpGetShortInstanceField:
+  OP_IGET load="ldrsh", volatile_load="ldarh", maybe_extend="sbfx w0, w0, #0, #16", wide=0
+
+NterpGetWideInstanceField:
+  OP_IGET load="ldr", volatile_load="ldar", maybe_extend="", wide=1
+
+NterpGetInstanceField:
+  OP_IGET load="ldr", volatile_load="ldar", maybe_extend="", wide=0
+
+NterpInstanceOf:
+    /* instance-of vA, vB, class@CCCC */
+   // Fast-path which gets the class from thread-local cache.
+   EXPORT_PC
+   FETCH_FROM_THREAD_CACHE x1, 3f
+   cbnz wMR, 4f
+1:
+   lsr     w2, wINST, #12              // w2<- B
+   GET_VREG w0, w2                     // w0<- vB (object)
+   cbz w0, 2f
+   bl artInstanceOfFromCode
+2:
+   ubfx    w1, wINST, #8, #4           // w1<- A
+   SET_VREG w0, w1
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+3:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   bl nterp_get_class_or_allocate_object
+   mov x1, x0
+   b 1b
+4:
+   bl art_quick_read_barrier_mark_reg01
+   b 1b
+
+NterpCheckCast:
+   // Fast-path which gets the class from thread-local cache.
+   EXPORT_PC
+   FETCH_FROM_THREAD_CACHE x1, 3f
+   cbnz wMR, 4f
+1:
+   lsr     w2, wINST, #8               // w2<- A
+   GET_VREG w0, w2                     // w2<- vA (object)
+   cbz w0, 2f
+   bl art_quick_check_instance_of
+2:
+   FETCH_ADVANCE_INST 2
+   GET_INST_OPCODE ip
+   GOTO_OPCODE ip
+3:
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   bl nterp_get_class_or_allocate_object
+   mov x1, x0
+   b 1b
+4:
+   bl art_quick_read_barrier_mark_reg01
+   b 1b
+
+NterpHandleHotnessOverflow:
+    add x1, xPC, xINST, lsl #1
+    mov x2, xFP
+    bl nterp_hot_method
+    cbnz x0, 1f
+    add w2, wINST, wINST                // w2<- byte offset
+    FETCH_ADVANCE_INST_RB w2            // update xPC, load wINST
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    GOTO_OPCODE ip                      // jump to next instruction
+1:
+    // Drop the current frame.
+    ldr ip, [xREFS, #-8]
+    mov sp, ip
+    .cfi_def_cfa sp, CALLEE_SAVES_SIZE
+
+    // The transition frame of type SaveAllCalleeSaves saves x19 and x20,
+    // but not managed ABI. So we need to restore callee-saves of the nterp frame,
+    // and save managed ABI callee saves, which will be restored by the callee upon
+    // return.
+    RESTORE_ALL_CALLEE_SAVES
+    INCREASE_FRAME ((CALLEE_SAVES_SIZE) - 16)
+
+    // FP callee-saves
+    stp d8, d9, [sp, #0]
+    stp d10, d11, [sp, #16]
+    stp d12, d13, [sp, #32]
+    stp d14, d15, [sp, #48]
+
+    // GP callee-saves.
+    SAVE_TWO_REGS x21, x22, 64
+    SAVE_TWO_REGS x23, x24, 80
+    SAVE_TWO_REGS x25, x26, 96
+    SAVE_TWO_REGS x27, x28, 112
+    SAVE_TWO_REGS x29, lr, 128
+
+    // Setup the new frame
+    ldr x1, [x0, #OSR_DATA_FRAME_SIZE]
+    // Given stack size contains all callee saved registers, remove them.
+    sub x1, x1, #(CALLEE_SAVES_SIZE - 16)
+
+    // We know x1 cannot be 0, as it at least contains the ArtMethod.
+
+    // Remember CFA in a callee-save register.
+    mov xINST, sp
+    .cfi_def_cfa_register xINST
+
+    sub sp, sp, x1
+
+    add x2, x0, #OSR_DATA_MEMORY
+2:
+    sub x1, x1, #8
+    ldr ip, [x2, x1]
+    str ip, [sp, x1]
+    cbnz x1, 2b
+
+    // Fetch the native PC to jump to and save it in a callee-save register.
+    ldr xFP, [x0, #OSR_DATA_NATIVE_PC]
+
+    // Free the memory holding OSR Data.
+    bl free
+
+    // Jump to the compiled code.
+    br xFP
+
+NterpHandleInvokeInterfaceOnObjectMethodRange:
+   // First argument is the 'this' pointer.
+   FETCH w1, 2
+   GET_VREG w1, w1
+   // Note: x1 is null, this will be handled by our SIGSEGV handler.
+   ldr w2, [x1, #MIRROR_OBJECT_CLASS_OFFSET]
+   add w2, w2, #MIRROR_CLASS_VTABLE_OFFSET_64
+   ldr x0, [x2, w0, sxtw #3]
+   b NterpCommonInvokeInstanceRange
+
+NterpHandleInvokeInterfaceOnObjectMethod:
+   // First argument is the 'this' pointer.
+   FETCH w1, 2
+   and w1, w1, #0xf
+   GET_VREG w1, w1
+   // Note: x1 is null, this will be handled by our SIGSEGV handler.
+   ldr w2, [x1, #MIRROR_OBJECT_CLASS_OFFSET]
+   add w2, w2, #MIRROR_CLASS_VTABLE_OFFSET_64
+   ldr x0, [x2, w0, sxtw #3]
+   b NterpCommonInvokeInstance
+
+// This is the logical end of ExecuteNterpImpl, where the frame info applies.
+// EndExecuteNterpImpl includes the methods below as we want the runtime to
+// see them as part of the Nterp PCs.
+.cfi_endproc
+
+nterp_to_nterp_static_non_range:
+    .cfi_startproc
+    SETUP_STACK_FOR_INVOKE
+    SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=1, is_string_init=0
+    .cfi_endproc
+
+nterp_to_nterp_string_init_non_range:
+    .cfi_startproc
+    SETUP_STACK_FOR_INVOKE
+    SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=1
+    .cfi_endproc
+
+nterp_to_nterp_instance_non_range:
+    .cfi_startproc
+    SETUP_STACK_FOR_INVOKE
+    SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
+    .cfi_endproc
+
+nterp_to_nterp_static_range:
+    .cfi_startproc
+    SETUP_STACK_FOR_INVOKE
+    SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=1
+    .cfi_endproc
+
+nterp_to_nterp_instance_range:
+    .cfi_startproc
+    SETUP_STACK_FOR_INVOKE
+    SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0
+    .cfi_endproc
+
+nterp_to_nterp_string_init_range:
+    .cfi_startproc
+    SETUP_STACK_FOR_INVOKE
+    SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=1
+    .cfi_endproc
+
+// This is the end of PCs contained by the OatQuickMethodHeader created for the interpreter
+// entry point.
+    .type EndExecuteNterpImpl, #function
+    .hidden EndExecuteNterpImpl
+    .global EndExecuteNterpImpl
+EndExecuteNterpImpl:
+
+// Entrypoints into runtime.
+NTERP_TRAMPOLINE nterp_get_static_field, NterpGetStaticField
+NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
+NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
+NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
+NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
+NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
+NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
+NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
+
+// gen_mterp.py will inline the following definitions
+// within [ExecuteNterpImpl, EndExecuteNterpImpl).
+%def instruction_end():
+
+    .type artNterpAsmInstructionEnd, #function
+    .hidden artNterpAsmInstructionEnd
+    .global artNterpAsmInstructionEnd
+artNterpAsmInstructionEnd:
+    // artNterpAsmInstructionEnd is used as landing pad for exception handling.
+    FETCH_INST
+    GET_INST_OPCODE ip
+    GOTO_OPCODE ip
+
+%def instruction_start():
+
+    .type artNterpAsmInstructionStart, #function
+    .hidden artNterpAsmInstructionStart
+    .global artNterpAsmInstructionStart
+artNterpAsmInstructionStart = .L_op_nop
+    .text
+
+%def opcode_start():
+    NAME_START nterp_${opcode}
+%def opcode_end():
+    NAME_END nterp_${opcode}
+%def helper_start(name):
+    NAME_START ${name}
+%def helper_end(name):
+    NAME_END ${name}
diff --git a/runtime/interpreter/mterp/arm64ng/object.S b/runtime/interpreter/mterp/arm64ng/object.S
new file mode 100644
index 0000000..aa171cc
--- /dev/null
+++ b/runtime/interpreter/mterp/arm64ng/object.S
@@ -0,0 +1,192 @@
+%def op_check_cast():
+   b NterpCheckCast
+
+%def op_iget_boolean():
+   b NterpGetBooleanInstanceField
+
+%def op_iget_boolean_quick():
+%  op_iget_quick(load="ldrb")
+
+%def op_iget_byte():
+   b NterpGetByteInstanceField
+
+%def op_iget_byte_quick():
+%  op_iget_quick(load="ldrsb")
+
+%def op_iget_char():
+   b NterpGetCharInstanceField
+
+%def op_iget_char_quick():
+%  op_iget_quick(load="ldrh")
+
+%def op_iget_object():
+   b NterpGetObjectInstanceField
+
+%def op_iget_object_quick():
+   /* For: iget-object-quick */
+   /* op vA, vB, offset//CCCC */
+   lsr     w2, wINST, #12              // w2<- B
+   FETCH w1, 1                         // w1<- field byte offset
+   GET_VREG w0, w2                     // w0<- object we're operating on
+   cbz     x0, common_errNullObject    // bail if null object.
+   ldr     w0, [x0, x1]
+   cbnz wMR, 2f
+1:
+   ubfx    w2, wINST, #8, #4           // w2<- A
+   PREFETCH_INST 2
+   SET_VREG_OBJECT w0, w2              // fp[A]<- w0
+   ADVANCE 2                           // advance rPC
+   GET_INST_OPCODE ip                  // extract opcode from wINST
+   GOTO_OPCODE ip                      // jump to next instruction
+2:
+   bl art_quick_read_barrier_mark_reg00
+   b 1b
+
+%def op_iget_quick(load="ldr", wide="0"):
+   /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */
+   /* op vA, vB, offset@CCCC */
+   lsr     w2, wINST, #12              // w2<- B
+   FETCH w1, 1                         // w1<- field byte offset
+   GET_VREG w0, w2                     // w0<- object we're operating on
+   cbz     x0, common_errNullObject    // bail if null object.
+   ubfx    w2, wINST, #8, #4           // w2<- A
+   PREFETCH_INST 2
+   .if $wide
+   ${load} x0, [x0, x1]
+   SET_VREG_WIDE x0, w2                // fp[A]<- x0
+   .else
+   ${load} w0, [x0, x1]
+   SET_VREG w0, w2                     // fp[A]<- w0
+   .endif
+   ADVANCE 2                           // advance rPC
+   GET_INST_OPCODE ip                  // extract opcode from wINST
+   GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_iget_short():
+   b NterpGetShortInstanceField
+
+%def op_iget_short_quick():
+%  op_iget_quick(load="ldrsh")
+
+%def op_iget_wide():
+   b NterpGetWideInstanceField
+
+%def op_iget_wide_quick():
+%  op_iget_quick(load="ldr", wide="1")
+
+%def op_instance_of():
+   b NterpInstanceOf
+
+%def op_iget():
+   b NterpGetInstanceField
+
+%def op_iput():
+   b NterpPutInstanceField
+
+%def op_iput_boolean():
+   b NterpPutBooleanInstanceField
+
+%def op_iput_boolean_quick():
+%  op_iput_quick(store="strb")
+
+%def op_iput_byte():
+   b NterpPutByteInstanceField
+
+%def op_iput_byte_quick():
+%  op_iput_quick(store="strb")
+
+%def op_iput_char():
+   b NterpPutCharInstanceField
+
+%def op_iput_char_quick():
+%  op_iput_quick(store="strh")
+
+%def op_iput_object():
+    b NterpPutObjectInstanceField
+
+%def op_iput_quick(store="str", wide="0", is_object="0"):
+   /* op vA, vB, offset@CCCC */
+   lsr     w2, wINST, #12              // w2<- B
+   FETCH w1, 1                         // w1<- field byte offset
+   GET_VREG w3, w2                     // w3<- fp[B], the object pointer
+   ubfx    w2, wINST, #8, #4           // w2<- A
+   cbz     w3, common_errNullObject    // object was null
+   .if $wide
+   GET_VREG_WIDE x0, w2                // x0<- fp[A]
+   FETCH_ADVANCE_INST 2                // advance rPC, load rINST
+   $store     x0, [x3, x1]             // obj.field<- x0
+   .else
+   GET_VREG w0, w2                     // w0<- fp[A]
+   FETCH_ADVANCE_INST 2                // advance rPC, load rINST
+   $store     w0, [x3, x1]             // obj.field<- w0
+   .endif
+   .if $is_object
+   cbz w0, 1f
+   ldr x1, [xSELF, #THREAD_CARD_TABLE_OFFSET]
+   lsr w2, w3, #CARD_TABLE_CARD_SHIFT
+   strb w1, [x1, x2]
+1:
+   .endif
+   GET_INST_OPCODE ip                  // extract opcode from rINST
+   GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_iput_object_quick():
+%  op_iput_quick(store="str", wide="0", is_object="1")
+
+%def op_iput_short():
+   b NterpPutShortInstanceField
+
+%def op_iput_short_quick():
+%  op_iput_quick(store="strh")
+
+%def op_iput_wide():
+   b NterpPutWideInstanceField
+
+%def op_iput_wide_quick():
+%  op_iput_quick(store="str", wide="1", is_object="0")
+
+%def op_sget(load="ldr", wide="0"):
+   b NterpGetIntStaticField
+
+%def op_sget_boolean():
+   b NterpGetBooleanStaticField
+
+%def op_sget_byte():
+   b NterpGetByteStaticField
+
+%def op_sget_char():
+   b NterpGetCharStaticField
+
+%def op_sget_object():
+   b NterpGetObjectStaticField
+
+%def op_sget_short():
+   b NterpGetShortStaticField
+
+%def op_sget_wide():
+   b NterpGetWideStaticField
+
+%def op_sput():
+   b NterpPutStaticField
+
+%def op_sput_boolean():
+   b NterpPutBooleanStaticField
+
+%def op_sput_byte():
+   b NterpPutByteStaticField
+
+%def op_sput_char():
+   b NterpPutCharStaticField
+
+%def op_sput_object():
+   b NterpPutObjectStaticField
+
+%def op_sput_short():
+   b NterpPutShortStaticField
+
+%def op_sput_wide():
+   b NterpPutWideStaticField
+
+%def op_new_instance():
+   // The routine is too big to fit in a handler, so jump to it.
+   b NterpNewInstance
diff --git a/runtime/interpreter/mterp/arm64ng/other.S b/runtime/interpreter/mterp/arm64ng/other.S
new file mode 100644
index 0000000..d7efe28
--- /dev/null
+++ b/runtime/interpreter/mterp/arm64ng/other.S
@@ -0,0 +1,336 @@
+%def unused():
+    brk 42
+
+%def op_const():
+    /* const vAA, #+BBBBbbbb */
+    lsr     w3, wINST, #8               // w3<- AA
+    FETCH w0, 1                         // w0<- bbbb (low)
+    FETCH w1, 2                         // w1<- BBBB (high)
+    FETCH_ADVANCE_INST 3                // advance rPC, load wINST
+    orr     w0, w0, w1, lsl #16         // w0<- BBBBbbbb
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    SET_VREG w0, w3                     // vAA<- w0
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_const_16():
+    /* const/16 vAA, #+BBBB */
+    FETCH_S w0, 1                       // w0<- ssssBBBB (sign-extended)
+    lsr     w3, wINST, #8               // w3<- AA
+    FETCH_ADVANCE_INST 2                // advance xPC, load wINST
+    SET_VREG w0, w3                     // vAA<- w0
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_const_4():
+    /* const/4 vA, #+B */
+    sbfx    w1, wINST, #12, #4          // w1<- sssssssB
+    ubfx    w0, wINST, #8, #4           // w0<- A
+    FETCH_ADVANCE_INST 1                // advance xPC, load wINST
+    GET_INST_OPCODE ip                  // ip<- opcode from xINST
+    SET_VREG w1, w0                     // fp[A]<- w1
+    GOTO_OPCODE ip                      // execute next instruction
+
+%def op_const_high16():
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH   w0, 1                       // r0<- 0000BBBB (zero-extended)
+    lsr     w3, wINST, #8               // r3<- AA
+    lsl     w0, w0, #16                 // r0<- BBBB0000
+    FETCH_ADVANCE_INST 2                // advance rPC, load rINST
+    SET_VREG w0, w3                     // vAA<- r0
+    GET_INST_OPCODE ip                  // extract opcode from rINST
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_const_object(jumbo="0", helper="nterp_load_object"):
+   // Fast-path which gets the object from thread-local cache.
+   FETCH_FROM_THREAD_CACHE x0, 2f
+   cbnz wMR, 3f
+1:
+   lsr     w1, wINST, #8               // w1<- AA
+   .if $jumbo
+   FETCH_ADVANCE_INST 3                // advance rPC, load wINST
+   .else
+   FETCH_ADVANCE_INST 2                // advance rPC, load wINST
+   .endif
+   GET_INST_OPCODE ip                  // extract opcode from wINST
+   SET_VREG_OBJECT w0, w1              // vAA <- value
+   GOTO_OPCODE ip                      // jump to next instruction
+2:
+   EXPORT_PC
+   mov x0, xSELF
+   ldr x1, [sp]
+   mov x2, xPC
+   bl $helper
+   b 1b
+3:
+   bl art_quick_read_barrier_mark_reg00
+   b 1b
+
+%def op_const_class():
+%  op_const_object(jumbo="0", helper="nterp_get_class_or_allocate_object")
+
+%def op_const_method_handle():
+%  op_const_object(jumbo="0")
+
+%def op_const_method_type():
+%  op_const_object(jumbo="0")
+
+%def op_const_string():
+   /* const/string vAA, String@BBBB */
+%  op_const_object(jumbo="0")
+
+%def op_const_string_jumbo():
+   /* const/string vAA, String@BBBBBBBB */
+%  op_const_object(jumbo="1")
+
+%def op_const_wide():
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH w0, 1                         // w0<- bbbb (low)
+    FETCH w1, 2                         // w1<- BBBB (low middle)
+    FETCH w2, 3                         // w2<- hhhh (high middle)
+    FETCH w3, 4                         // w3<- HHHH (high)
+    lsr     w4, wINST, #8               // r4<- AA
+    FETCH_ADVANCE_INST 5                // advance rPC, load wINST
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    orr     w0, w0, w1, lsl #16         // w0<-         BBBBbbbb
+    orr     x0, x0, x2, lsl #32         // w0<-     hhhhBBBBbbbb
+    orr     x0, x0, x3, lsl #48         // w0<- HHHHhhhhBBBBbbbb
+    SET_VREG_WIDE x0, w4
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_const_wide_16():
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S x0, 1                       // x0<- ssssssssssssBBBB (sign-extended)
+    lsr     w3, wINST, #8               // w3<- AA
+    FETCH_ADVANCE_INST 2                // advance rPC, load rINST
+    GET_INST_OPCODE ip                  // extract opcode from rINST
+    SET_VREG_WIDE x0, w3
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_const_wide_32():
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH   w0, 1                       // x0<- 000000000000bbbb (low)
+    lsr     w3, wINST, #8               // w3<- AA
+    FETCH_S x2, 2                       // x2<- ssssssssssssBBBB (high)
+    FETCH_ADVANCE_INST 3                // advance rPC, load wINST
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    orr     x0, x0, x2, lsl #16         // x0<- ssssssssBBBBbbbb
+    SET_VREG_WIDE x0, w3
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_const_wide_high16():
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH w0, 1                         // w0<- 0000BBBB (zero-extended)
+    lsr     w1, wINST, #8               // w1<- AA
+    FETCH_ADVANCE_INST 2                // advance rPC, load wINST
+    lsl     x0, x0, #48
+    SET_VREG_WIDE x0, w1
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_monitor_enter():
+/*
+ * Synchronize on an object.
+ */
+    /* monitor-enter vAA */
+    EXPORT_PC
+    lsr      w2, wINST, #8               // w2<- AA
+    GET_VREG w0, w2
+    bl art_quick_lock_object
+    FETCH_ADVANCE_INST 1
+    GET_INST_OPCODE ip                   // extract opcode from rINST
+    GOTO_OPCODE ip                       // jump to next instruction
+
+%def op_monitor_exit():
+/*
+ * Unlock an object.
+ *
+ * Exceptions that occur when unlocking a monitor need to appear as
+ * if they happened at the following instruction.  See the Dalvik
+ * instruction spec.
+ */
+    /* monitor-exit vAA */
+    EXPORT_PC
+    lsr      w2, wINST, #8               // w2<- AA
+    GET_VREG w0, w2
+    bl art_quick_unlock_object
+    FETCH_ADVANCE_INST 1
+    GET_INST_OPCODE ip                   // extract opcode from rINST
+    GOTO_OPCODE ip                       // jump to next instruction
+
+%def op_move(is_object="0"):
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    lsr     w1, wINST, #12              // x1<- B from 15:12
+    ubfx    w0, wINST, #8, #4           // x0<- A from 11:8
+    FETCH_ADVANCE_INST 1                // advance rPC, load wINST
+    GET_VREG w2, w1                     // x2<- fp[B]
+    GET_INST_OPCODE ip                  // ip<- opcode from wINST
+    .if $is_object
+    SET_VREG_OBJECT w2, w0              // fp[A]<- x2
+    .else
+    SET_VREG w2, w0                     // fp[A]<- x2
+    .endif
+    GOTO_OPCODE ip                      // execute next instruction
+
+%def op_move_16(is_object="0"):
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH w1, 2                         // w1<- BBBB
+    FETCH w0, 1                         // w0<- AAAA
+    FETCH_ADVANCE_INST 3                // advance xPC, load xINST
+    GET_VREG w2, w1                     // w2<- fp[BBBB]
+    GET_INST_OPCODE ip                  // extract opcode from xINST
+    .if $is_object
+    SET_VREG_OBJECT w2, w0              // fp[AAAA]<- w2
+    .else
+    SET_VREG w2, w0                     // fp[AAAA]<- w2
+    .endif
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_move_exception():
+    /* move-exception vAA */
+    lsr     w2, wINST, #8               // w2<- AA
+    ldr     x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
+    FETCH_ADVANCE_INST 1                // advance rPC, load rINST
+    SET_VREG_OBJECT w3, w2              // fp[AA]<- exception obj
+    GET_INST_OPCODE ip                  // extract opcode from rINST
+    str     xzr, [xSELF, #THREAD_EXCEPTION_OFFSET]  // clear exception
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_move_from16(is_object="0"):
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH w1, 1                         // r1<- BBBB
+    lsr     w0, wINST, #8               // r0<- AA
+    FETCH_ADVANCE_INST 2                // advance rPC, load wINST
+    GET_VREG w2, w1                     // r2<- fp[BBBB]
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    .if $is_object
+    SET_VREG_OBJECT w2, w0              // fp[AA]<- r2
+    .else
+    SET_VREG w2, w0                     // fp[AA]<- r2
+    .endif
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_move_object():
+%  op_move(is_object="1")
+
+%def op_move_object_16():
+%  op_move_16(is_object="1")
+
+%def op_move_object_from16():
+%  op_move_from16(is_object="1")
+
+%def op_move_result(is_object="0"):
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    lsr     w2, wINST, #8               // r2<- AA
+    FETCH_ADVANCE_INST 1                // advance rPC, load wINST
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    .if $is_object
+    SET_VREG_OBJECT w0, w2, w1          // fp[AA]<- r0
+    .else
+    SET_VREG w0, w2                     // fp[AA]<- r0
+    .endif
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_move_result_object():
+%  op_move_result(is_object="1")
+
+%def op_move_result_wide():
+    /* for: move-result-wide */
+    /* op vAA */
+    lsr     w2, wINST, #8               // r2<- AA
+    FETCH_ADVANCE_INST 1                // advance rPC, load wINST
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    SET_VREG_WIDE x0, w2                // fp[AA]<- r0
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_move_wide():
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    lsr     w3, wINST, #12              // w3<- B
+    ubfx    w2, wINST, #8, #4           // w2<- A
+    GET_VREG_WIDE  x3, w3
+    FETCH_ADVANCE_INST 1                // advance rPC, load wINST
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    SET_VREG_WIDE  x3, w2
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_move_wide_16():
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH w3, 2                         // w3<- BBBB
+    FETCH w2, 1                         // w2<- AAAA
+    GET_VREG_WIDE x3, w3
+    FETCH_ADVANCE_INST 3                // advance rPC, load rINST
+    SET_VREG_WIDE x3, w2
+    GET_INST_OPCODE ip                  // extract opcode from rINST
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_move_wide_from16():
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH w3, 1                         // w3<- BBBB
+    lsr     w2, wINST, #8               // w2<- AA
+    GET_VREG_WIDE x3, w3
+    FETCH_ADVANCE_INST 2                // advance rPC, load wINST
+    GET_INST_OPCODE ip                  // extract opcode from wINST
+    SET_VREG_WIDE x3, w2
+    GOTO_OPCODE ip                      // jump to next instruction
+
+%def op_nop():
+    FETCH_ADVANCE_INST 1                // advance to next instr, load rINST
+    GET_INST_OPCODE ip                  // ip<- opcode from rINST
+    GOTO_OPCODE ip                      // execute it
+
+%def op_unused_3e():
+%  unused()
+
+%def op_unused_3f():
+%  unused()
+
+%def op_unused_40():
+%  unused()
+
+%def op_unused_41():
+%  unused()
+
+%def op_unused_42():
+%  unused()
+
+%def op_unused_43():
+%  unused()
+
+%def op_unused_79():
+%  unused()
+
+%def op_unused_7a():
+%  unused()
+
+%def op_unused_f3():
+%  unused()
+
+%def op_unused_f4():
+%  unused()
+
+%def op_unused_f5():
+%  unused()
+
+%def op_unused_f6():
+%  unused()
+
+%def op_unused_f7():
+%  unused()
+
+%def op_unused_f8():
+%  unused()
+
+%def op_unused_f9():
+%  unused()
+
+%def op_unused_fc():
+%  unused()
+
+%def op_unused_fd():
+%  unused()
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index d51b0ae..b025e3d4 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -482,7 +482,7 @@
 static mirror::Object* DoFilledNewArray(Thread* self,
                                         ArtMethod* caller,
                                         uint16_t* dex_pc_ptr,
-                                        int32_t* regs,
+                                        uint32_t* regs,
                                         bool is_range)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   const Instruction* inst = Instruction::At(dex_pc_ptr);
@@ -555,7 +555,7 @@
 
 extern "C" mirror::Object* NterpFilledNewArray(Thread* self,
                                                ArtMethod* caller,
-                                               int32_t* registers,
+                                               uint32_t* registers,
                                                uint16_t* dex_pc_ptr)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   return DoFilledNewArray(self, caller, dex_pc_ptr, registers, /* is_range= */ false);
@@ -563,7 +563,7 @@
 
 extern "C" mirror::Object* NterpFilledNewArrayRange(Thread* self,
                                                     ArtMethod* caller,
-                                                    int32_t* registers,
+                                                    uint32_t* registers,
                                                     uint16_t* dex_pc_ptr)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   return DoFilledNewArray(self, caller, dex_pc_ptr, registers, /* is_range= */ true);
@@ -573,7 +573,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ScopedAssertNoThreadSuspension sants("In nterp");
   jit::Jit* jit = Runtime::Current()->GetJit();
-  if (jit != nullptr) {
+  if (jit != nullptr && jit->UseJitCompilation()) {
     // Nterp passes null on entry where we don't want to OSR.
     if (dex_pc_ptr != nullptr) {
       // This could be a loop back edge, check if we can OSR.
@@ -593,12 +593,14 @@
 extern "C" ssize_t MterpDoPackedSwitch(const uint16_t* switchData, int32_t testVal);
 extern "C" ssize_t NterpDoPackedSwitch(const uint16_t* switchData, int32_t testVal)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedAssertNoThreadSuspension sants("In nterp");
   return MterpDoPackedSwitch(switchData, testVal);
 }
 
 extern "C" ssize_t MterpDoSparseSwitch(const uint16_t* switchData, int32_t testVal);
 extern "C" ssize_t NterpDoSparseSwitch(const uint16_t* switchData, int32_t testVal)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedAssertNoThreadSuspension sants("In nterp");
   return MterpDoSparseSwitch(switchData, testVal);
 }
 
diff --git a/runtime/interpreter/mterp/x86_64ng/array.S b/runtime/interpreter/mterp/x86_64ng/array.S
index baf5f30..c8dd8b0 100644
--- a/runtime/interpreter/mterp/x86_64ng/array.S
+++ b/runtime/interpreter/mterp/x86_64ng/array.S
@@ -93,6 +93,7 @@
 %  op_aput(rINST_reg="rINSTq", store="movq", shift="8", data_offset="MIRROR_WIDE_ARRAY_DATA_OFFSET", wide="1")
 
 %def op_aput_object():
+    EXPORT_PC                               # for the art_quick_aput_obj call
     movzbq  2(rPC), %rax                    # rax <- BB
     movzbq  3(rPC), %rcx                    # rcx <- CC
     GET_VREG %edi, %rax                     # edi <- vBB (array object)
diff --git a/runtime/interpreter/mterp/x86_64ng/main.S b/runtime/interpreter/mterp/x86_64ng/main.S
index aeacec4..a890245 100644
--- a/runtime/interpreter/mterp/x86_64ng/main.S
+++ b/runtime/interpreter/mterp/x86_64ng/main.S
@@ -288,10 +288,13 @@
     leaq -24(%rsp), %r10
     subq %r11, %r10
     // Alignment
+    // Note: There may be two pieces of alignment but there is no need to align
+    // out args to `kPointerSize` separately before aligning to kStackAlignment.
     andq $$-16, %r10
 
-    // Set reference and dex registers.
-    leaq 24(%r10, \refs, 4), \refs
+    // Set reference and dex registers, align to pointer size for previous frame and dex pc.
+    leaq 24 + 4(%r10, \refs, 4), \refs
+    andq LITERAL(-__SIZEOF_POINTER__), \refs
     leaq (\refs, %rbx, 4), \fp
 
     // Now setup the stack pointer.
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 5986982..99cd547 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -48,7 +48,7 @@
 #include "mirror/array-inl.h"
 #include "mirror/class-alloc-inl.h"
 #include "mirror/executable-inl.h"
-#include "mirror/field-inl.h"
+#include "mirror/field.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-alloc-inl.h"
@@ -360,26 +360,7 @@
                            klass->PrettyDescriptor().c_str());
     return;
   }
-  Runtime* runtime = Runtime::Current();
-  PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
-  ObjPtr<mirror::Field> field;
-  if (runtime->IsActiveTransaction()) {
-    if (pointer_size == PointerSize::k64) {
-      field = mirror::Field::CreateFromArtField<PointerSize::k64, true>(
-          self, found, true);
-    } else {
-      field = mirror::Field::CreateFromArtField<PointerSize::k32, true>(
-          self, found, true);
-    }
-  } else {
-    if (pointer_size == PointerSize::k64) {
-      field = mirror::Field::CreateFromArtField<PointerSize::k64, false>(
-          self, found, true);
-    } else {
-      field = mirror::Field::CreateFromArtField<PointerSize::k32, false>(
-          self, found, true);
-    }
-  }
+  ObjPtr<mirror::Field> field = mirror::Field::CreateFromArtField(self, found, true);
   result->SetL(field);
 }
 
@@ -395,28 +376,13 @@
   ObjPtr<mirror::String> name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
   ObjPtr<mirror::ObjectArray<mirror::Class>> args =
       shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray<mirror::Class>();
-  Runtime* runtime = Runtime::Current();
-  bool transaction = runtime->IsActiveTransaction();
-  PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
+  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   auto fn_hiddenapi_access_context = GetHiddenapiAccessContextFunction(shadow_frame);
-  ObjPtr<mirror::Method> method;
-  if (transaction) {
-    if (pointer_size == PointerSize::k64) {
-      method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
-          self, klass, name, args, fn_hiddenapi_access_context);
-    } else {
-      method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
-          self, klass, name, args, fn_hiddenapi_access_context);
-    }
-  } else {
-    if (pointer_size == PointerSize::k64) {
-      method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
-          self, klass, name, args, fn_hiddenapi_access_context);
-    } else {
-      method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, false>(
-          self, klass, name, args, fn_hiddenapi_access_context);
-    }
-  }
+  ObjPtr<mirror::Method> method = (pointer_size == PointerSize::k64)
+      ? mirror::Class::GetDeclaredMethodInternal<PointerSize::k64>(
+            self, klass, name, args, fn_hiddenapi_access_context)
+      : mirror::Class::GetDeclaredMethodInternal<PointerSize::k32>(
+            self, klass, name, args, fn_hiddenapi_access_context);
   if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) {
     method = nullptr;
   }
@@ -433,27 +399,10 @@
   }
   ObjPtr<mirror::ObjectArray<mirror::Class>> args =
       shadow_frame->GetVRegReference(arg_offset + 1)->AsObjectArray<mirror::Class>();
-  Runtime* runtime = Runtime::Current();
-  bool transaction = runtime->IsActiveTransaction();
-  PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
-  ObjPtr<mirror::Constructor> constructor;
-  if (transaction) {
-    if (pointer_size == PointerSize::k64) {
-      constructor = mirror::Class::GetDeclaredConstructorInternal<PointerSize::k64,
-                                                                  true>(self, klass, args);
-    } else {
-      constructor = mirror::Class::GetDeclaredConstructorInternal<PointerSize::k32,
-                                                                  true>(self, klass, args);
-    }
-  } else {
-    if (pointer_size == PointerSize::k64) {
-      constructor = mirror::Class::GetDeclaredConstructorInternal<PointerSize::k64,
-                                                                  false>(self, klass, args);
-    } else {
-      constructor = mirror::Class::GetDeclaredConstructorInternal<PointerSize::k32,
-                                                                  false>(self, klass, args);
-    }
-  }
+  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+  ObjPtr<mirror::Constructor> constructor = (pointer_size == PointerSize::k64)
+      ? mirror::Class::GetDeclaredConstructorInternal<PointerSize::k64>(self, klass, args)
+      : mirror::Class::GetDeclaredConstructorInternal<PointerSize::k32>(self, klass, args);
   if (constructor != nullptr &&
       ShouldDenyAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
     constructor = nullptr;
@@ -1885,11 +1834,7 @@
     Thread* self, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED,
     uint32_t* args ATTRIBUTE_UNUSED, JValue* result) {
   ScopedObjectAccessUnchecked soa(self);
-  if (Runtime::Current()->IsActiveTransaction()) {
-    result->SetL(soa.Decode<mirror::Object>(self->CreateInternalStackTrace<true>(soa)));
-  } else {
-    result->SetL(soa.Decode<mirror::Object>(self->CreateInternalStackTrace<false>(soa)));
-  }
+  result->SetL(soa.Decode<mirror::Object>(self->CreateInternalStackTrace(soa)));
 }
 
 void UnstartedRuntime::UnstartedJNIByteOrderIsLittleEndian(
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 4429f63..792444a 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -23,7 +23,7 @@
 #include "base/enums.h"
 #include "base/memory_tool.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_runtime_test.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_instruction.h"
@@ -1322,19 +1322,13 @@
   Handle<mirror::String> input = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "abd"));
 
   // Find the constructor.
-  ArtMethod* throw_cons = throw_class->FindConstructor(
-      "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
+  PointerSize pointer_size = class_linker->GetImagePointerSize();
+  ArtMethod* throw_cons = throw_class->FindConstructor("(Ljava/lang/String;)V", pointer_size);
   ASSERT_TRUE(throw_cons != nullptr);
-  Handle<mirror::Constructor> cons;
-  if (class_linker->GetImagePointerSize() == PointerSize::k64) {
-     cons = hs.NewHandle(
-        mirror::Constructor::CreateFromArtMethod<PointerSize::k64, false>(self, throw_cons));
-    ASSERT_TRUE(cons != nullptr);
-  } else {
-    cons = hs.NewHandle(
-        mirror::Constructor::CreateFromArtMethod<PointerSize::k32, false>(self, throw_cons));
-    ASSERT_TRUE(cons != nullptr);
-  }
+  Handle<mirror::Constructor> cons = hs.NewHandle((pointer_size == PointerSize::k64)
+      ? mirror::Constructor::CreateFromArtMethod<PointerSize::k64>(self, throw_cons)
+      : mirror::Constructor::CreateFromArtMethod<PointerSize::k32>(self, throw_cons));
+  ASSERT_TRUE(cons != nullptr);
 
   Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle(
       mirror::ObjectArray<mirror::Object>::Alloc(
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index f94c51e..2ef1cb4 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -27,7 +27,7 @@
 #include "base/runtime_debug.h"
 #include "base/scoped_flock.h"
 #include "base/utils.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "debugger.h"
 #include "dex/type_lookup_table.h"
 #include "gc/space/image_space.h"
@@ -879,6 +879,12 @@
   ZygoteVerificationTask() {}
 
   void Run(Thread* self) override {
+    // We are going to load class and run verification, which may also need to load
+    // classes. If the thread cannot load classes (typically when the runtime is
+    // debuggable), then just return.
+    if (!self->CanLoadClasses()) {
+      return;
+    }
     Runtime* runtime = Runtime::Current();
     ClassLinker* linker = runtime->GetClassLinker();
     const std::vector<const DexFile*>& boot_class_path =
@@ -908,7 +914,12 @@
           continue;
         }
         ++number_of_classes;
-        linker->VerifyClass(self, klass);
+        if (linker->VerifyClass(self, klass) == verifier::FailureKind::kHardFailure) {
+          DCHECK(self->IsExceptionPending());
+          LOG(FATAL) << "Methods in the boot classpath failed to verify: "
+                     << self->GetException()->Dump();
+        }
+        CHECK(!self->IsExceptionPending());
       }
     }
     LOG(INFO) << "Verified "
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 29951a7..cf07fe5 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -965,8 +965,8 @@
 
   const uint8_t* code;
   const uint8_t* data;
-  // We might need to try the allocation twice (with GC in between to free up memory).
-  for (int i = 0; i < 2; i++) {
+  while (true) {
+    bool at_max_capacity = false;
     {
       ScopedThreadSuspension sts(self, kSuspended);
       MutexLock mu(self, *Locks::jit_lock_);
@@ -974,18 +974,23 @@
       ScopedCodeCacheWrite ccw(*region);
       code = region->AllocateCode(code_size);
       data = region->AllocateData(data_size);
+      at_max_capacity = IsAtMaxCapacity();
     }
-    if (code == nullptr || data == nullptr) {
-      Free(self, region, code, data);
-      if (i == 0) {
-        GarbageCollectCache(self);
-        continue;  // Retry after GC.
-      } else {
-        return false;  // Fail.
-      }
+    if (code != nullptr && data != nullptr) {
+      break;
     }
-    break;  // Success.
+    Free(self, region, code, data);
+    if (at_max_capacity) {
+      VLOG(jit) << "JIT failed to allocate code of size "
+                << PrettySize(code_size)
+                << ", and data of size "
+                << PrettySize(data_size);
+      return false;
+    }
+    // Run a code cache collection and try again.
+    GarbageCollectCache(self);
   }
+
   *reserved_code = ArrayRef<const uint8_t>(code, code_size);
   *reserved_data = ArrayRef<const uint8_t>(data, data_size);
 
@@ -1095,8 +1100,12 @@
   }
 }
 
+bool JitCodeCache::IsAtMaxCapacity() const {
+  return private_region_.GetCurrentCapacity() == private_region_.GetMaxCapacity();
+}
+
 bool JitCodeCache::ShouldDoFullCollection() {
-  if (private_region_.GetCurrentCapacity() == private_region_.GetMaxCapacity()) {
+  if (IsAtMaxCapacity()) {
     // Always do a full collection when the code cache is full.
     return true;
   } else if (private_region_.GetCurrentCapacity() < kReservedCapacity) {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 50e1e2b..cefbf25 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -466,6 +466,9 @@
   // Notify all waiting threads that a collection is done.
   void NotifyCollectionDone(Thread* self) REQUIRES(Locks::jit_lock_);
 
+  // Return whether the code cache's capacity is at its maximum.
+  bool IsAtMaxCapacity() const REQUIRES(Locks::jit_lock_);
+
   // Return whether we should do a full collection given the current state of the cache.
   bool ShouldDoFullCollection()
       REQUIRES(Locks::jit_lock_)
diff --git a/runtime/jni/check_jni.cc b/runtime/jni/check_jni.cc
index 4a8453a..3206011 100644
--- a/runtime/jni/check_jni.cc
+++ b/runtime/jni/check_jni.cc
@@ -31,7 +31,7 @@
 #include "base/time_utils.h"
 #include "class_linker-inl.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
 #include "gc/space/space.h"
diff --git a/runtime/jni/jni_id_manager.cc b/runtime/jni/jni_id_manager.cc
index 8070505..402259b 100644
--- a/runtime/jni/jni_id_manager.cc
+++ b/runtime/jni/jni_id_manager.cc
@@ -27,6 +27,7 @@
 #include "base/globals.h"
 #include "base/locks.h"
 #include "base/mutex.h"
+#include "class_root-inl.h"
 #include "gc/allocation_listener.h"
 #include "gc/heap.h"
 #include "jni/jni_internal.h"
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index b228d99..c178c38 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -31,7 +31,7 @@
 #include "base/safe_map.h"
 #include "base/stl_util.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/dex_file-inl.h"
 #include "dex/utf.h"
 #include "fault_handler.h"
@@ -47,7 +47,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache-inl.h"
-#include "mirror/field-inl.h"
+#include "mirror/field.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-alloc-inl.h"
@@ -531,11 +531,10 @@
     ArtMethod* m = jni::DecodeArtMethod(mid);
     ObjPtr<mirror::Executable> method;
     DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
-    DCHECK(!Runtime::Current()->IsActiveTransaction());
     if (m->IsConstructor()) {
-      method = mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), m);
+      method = mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), m);
     } else {
-      method = mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), m);
+      method = mirror::Method::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), m);
     }
     return soa.AddLocalReference<jobject>(method);
   }
@@ -545,7 +544,7 @@
     ScopedObjectAccess soa(env);
     ArtField* f = jni::DecodeArtField(fid);
     return soa.AddLocalReference<jobject>(
-        mirror::Field::CreateFromArtField<kRuntimePointerSize>(soa.Self(), f, true));
+        mirror::Field::CreateFromArtField(soa.Self(), f, true));
   }
 
   static jclass GetObjectClass(JNIEnv* env, jobject java_object) {
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 2dc9f67..78e7561 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -18,7 +18,7 @@
 
 #include "android-base/stringprintf.h"
 
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_dex_operations.h"
 #include "common_throws.h"
 #include "interpreter/shadow_frame-inl.h"
diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc
index 688febc..eb3f2ad 100644
--- a/runtime/method_handles_test.cc
+++ b/runtime/method_handles_test.cc
@@ -17,7 +17,7 @@
 #include "method_handles.h"
 
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_runtime_test.h"
 #include "handle_scope-inl.h"
 #include "jvalue-inl.h"
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 34925f5..6d52d44 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -22,6 +22,7 @@
 #include <android-base/logging.h>
 
 #include "base/bit_utils.h"
+#include "base/casts.h"
 #include "class.h"
 #include "obj_ptr-inl.h"
 #include "runtime.h"
@@ -227,22 +228,30 @@
 
 template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
 inline T PointerArray::GetElementPtrSize(uint32_t idx) {
-  // C style casts here since we sometimes have T be a pointer, or sometimes an integer
-  // (for stack traces).
   if (kPointerSize == PointerSize::k64) {
-    return (T)static_cast<uintptr_t>(AsLongArray<kVerifyFlags>()->GetWithoutChecks(idx));
+    DCHECK(IsLongArray<kVerifyFlags>());
+  } else {
+    DCHECK(IsIntArray<kVerifyFlags>());
   }
-  return (T)static_cast<uintptr_t>(AsIntArray<kVerifyFlags>()->GetWithoutChecks(idx));
+  return GetElementPtrSizeUnchecked<T, kPointerSize, kVerifyFlags>(idx);
 }
+
 template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
 inline T PointerArray::GetElementPtrSizeUnchecked(uint32_t idx) {
   // C style casts here since we sometimes have T be a pointer, or sometimes an integer
   // (for stack traces).
+  using ConversionType = typename std::conditional_t<std::is_pointer_v<T>, uintptr_t, T>;
   if (kPointerSize == PointerSize::k64) {
-    return (T)static_cast<uintptr_t>(AsLongArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+    uint64_t value =
+        static_cast<uint64_t>(AsLongArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+    return (T) dchecked_integral_cast<ConversionType>(value);
+  } else {
+    uint32_t value =
+        static_cast<uint32_t>(AsIntArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+    return (T) dchecked_integral_cast<ConversionType>(value);
   }
-  return (T)static_cast<uintptr_t>(AsIntArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
 }
+
 template<typename T, VerifyObjectFlags kVerifyFlags>
 inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
   if (ptr_size == PointerSize::k64) {
@@ -251,23 +260,22 @@
   return GetElementPtrSize<T, PointerSize::k32, kVerifyFlags>(idx);
 }
 
-template<bool kTransactionActive, bool kUnchecked>
+template<bool kTransactionActive, bool kCheckTransaction, bool kUnchecked>
 inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, PointerSize ptr_size) {
   if (ptr_size == PointerSize::k64) {
     (kUnchecked ? ObjPtr<LongArray>::DownCast(ObjPtr<Object>(this)) : AsLongArray())->
-        SetWithoutChecks<kTransactionActive>(idx, element);
+        SetWithoutChecks<kTransactionActive, kCheckTransaction>(idx, element);
   } else {
-    DCHECK_LE(element, static_cast<uint64_t>(0xFFFFFFFFu));
+    uint32_t element32 = dchecked_integral_cast<uint32_t>(element);
     (kUnchecked ? ObjPtr<IntArray>::DownCast(ObjPtr<Object>(this)) : AsIntArray())
-        ->SetWithoutChecks<kTransactionActive>(idx, static_cast<uint32_t>(element));
+        ->SetWithoutChecks<kTransactionActive, kCheckTransaction>(idx, element32);
   }
 }
 
-template<bool kTransactionActive, bool kUnchecked, typename T>
+template<bool kTransactionActive, bool kCheckTransaction, bool kUnchecked, typename T>
 inline void PointerArray::SetElementPtrSize(uint32_t idx, T* element, PointerSize ptr_size) {
-  SetElementPtrSize<kTransactionActive, kUnchecked>(idx,
-                                                    reinterpret_cast<uintptr_t>(element),
-                                                    ptr_size);
+  SetElementPtrSize<kTransactionActive, kCheckTransaction, kUnchecked>(
+      idx, reinterpret_cast<uintptr_t>(element), ptr_size);
 }
 
 template <VerifyObjectFlags kVerifyFlags, typename Visitor>
@@ -278,7 +286,9 @@
     void* ptr = GetElementPtrSize<void*, kVerifyFlags>(i, pointer_size);
     void* new_ptr = visitor(ptr);
     if (ptr != new_ptr) {
-      dest->SetElementPtrSize<false, true>(i, new_ptr, pointer_size);
+      dest->SetElementPtrSize</*kActiveTransaction=*/ false,
+                              /*kCheckTransaction=*/ true,
+                              /*kUnchecked=*/ true>(i, new_ptr, pointer_size);
     }
   }
 }
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index e011e1c..bd9f9ca 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -21,7 +21,7 @@
 #include "class-inl.h"
 #include "class.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_throws.h"
 #include "dex/dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 19f9a92..4c172f2 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -241,10 +241,13 @@
                                     static_cast<size_t>(ptr_size) * index);
   }
 
-  template<bool kTransactionActive = false, bool kUnchecked = false>
+  template<bool kTransactionActive = false, bool kCheckTransaction = true, bool kUnchecked = false>
   void SetElementPtrSize(uint32_t idx, uint64_t element, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  template<bool kTransactionActive = false, bool kUnchecked = false, typename T>
+  template<bool kTransactionActive = false,
+           bool kCheckTransaction = true,
+           bool kUnchecked = false,
+           typename T>
   void SetElementPtrSize(uint32_t idx, T* element, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index a2d76cd..33ce785 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -32,7 +32,7 @@
 #include "class_ext-inl.h"
 #include "class_linker-inl.h"
 #include "class_loader.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_annotations.h"
@@ -1453,7 +1453,7 @@
   return false;
 }
 
-template <PointerSize kPointerSize, bool kTransactionActive>
+template <PointerSize kPointerSize>
 ObjPtr<Method> Class::GetDeclaredMethodInternal(
     Thread* self,
     ObjPtr<Class> klass,
@@ -1494,7 +1494,7 @@
     bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
     if (!m_hidden && !m.IsSynthetic()) {
       // Non-hidden, virtual, non-synthetic. Best possible result, exit early.
-      return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+      return Method::CreateFromArtMethod<kPointerSize>(self, &m);
     } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
       // Remember as potential result.
       result = &m;
@@ -1533,7 +1533,7 @@
         // Non-hidden, direct, non-synthetic. Any virtual result could only have been
         // hidden, therefore this is the best possible match. Exit now.
         DCHECK((result == nullptr) || result_hidden);
-        return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+        return Method::CreateFromArtMethod<kPointerSize>(self, &m);
       } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
         // Remember as potential result.
         result = &m;
@@ -1543,40 +1543,26 @@
   }
 
   return result != nullptr
-      ? Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, result)
+      ? Method::CreateFromArtMethod<kPointerSize>(self, result)
       : nullptr;
 }
 
 template
-ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k32, false>(
+ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k32>(
     Thread* self,
     ObjPtr<Class> klass,
     ObjPtr<String> name,
     ObjPtr<ObjectArray<Class>> args,
     const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
 template
-ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
-    Thread* self,
-    ObjPtr<Class> klass,
-    ObjPtr<String> name,
-    ObjPtr<ObjectArray<Class>> args,
-    const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
-template
-ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
-    Thread* self,
-    ObjPtr<Class> klass,
-    ObjPtr<String> name,
-    ObjPtr<ObjectArray<Class>> args,
-    const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
-template
-ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
+ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64>(
     Thread* self,
     ObjPtr<Class> klass,
     ObjPtr<String> name,
     ObjPtr<ObjectArray<Class>> args,
     const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
 
-template <PointerSize kPointerSize, bool kTransactionActive>
+template <PointerSize kPointerSize>
 ObjPtr<Constructor> Class::GetDeclaredConstructorInternal(
     Thread* self,
     ObjPtr<Class> klass,
@@ -1584,29 +1570,19 @@
   StackHandleScope<1> hs(self);
   ArtMethod* result = klass->GetDeclaredConstructor(self, hs.NewHandle(args), kPointerSize);
   return result != nullptr
-      ? Constructor::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, result)
+      ? Constructor::CreateFromArtMethod<kPointerSize>(self, result)
       : nullptr;
 }
 
 // Constructor::CreateFromArtMethod<kTransactionActive>(self, result)
 
 template
-ObjPtr<Constructor> Class::GetDeclaredConstructorInternal<PointerSize::k32, false>(
+ObjPtr<Constructor> Class::GetDeclaredConstructorInternal<PointerSize::k32>(
     Thread* self,
     ObjPtr<Class> klass,
     ObjPtr<ObjectArray<Class>> args);
 template
-ObjPtr<Constructor> Class::GetDeclaredConstructorInternal<PointerSize::k32, true>(
-    Thread* self,
-    ObjPtr<Class> klass,
-    ObjPtr<ObjectArray<Class>> args);
-template
-ObjPtr<Constructor> Class::GetDeclaredConstructorInternal<PointerSize::k64, false>(
-    Thread* self,
-    ObjPtr<Class> klass,
-    ObjPtr<ObjectArray<Class>> args);
-template
-ObjPtr<Constructor> Class::GetDeclaredConstructorInternal<PointerSize::k64, true>(
+ObjPtr<Constructor> Class::GetDeclaredConstructorInternal<PointerSize::k64>(
     Thread* self,
     ObjPtr<Class> klass,
     ObjPtr<ObjectArray<Class>> args);
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 48e1c3c..9b6390e 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -740,7 +740,7 @@
         PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template <PointerSize kPointerSize, bool kTransactionActive>
+  template <PointerSize kPointerSize>
   static ObjPtr<Method> GetDeclaredMethodInternal(
       Thread* self,
       ObjPtr<Class> klass,
@@ -749,7 +749,7 @@
       const std::function<hiddenapi::AccessContext()>& fn_get_access_context)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template <PointerSize kPointerSize, bool kTransactionActive>
+  template <PointerSize kPointerSize>
   static ObjPtr<Constructor> GetDeclaredConstructorInternal(Thread* self,
                                                             ObjPtr<Class> klass,
                                                             ObjPtr<ObjectArray<Class>> args)
diff --git a/runtime/mirror/class_ext-inl.h b/runtime/mirror/class_ext-inl.h
index 99f7f49..b8493c1 100644
--- a/runtime/mirror/class_ext-inl.h
+++ b/runtime/mirror/class_ext-inl.h
@@ -23,7 +23,6 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "base/globals.h"
-#include "class_root.h"
 #include "handle_scope.h"
 #include "jni/jni_internal.h"
 #include "jni_id_type.h"
diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc
index ba1ae5f..7543ab6 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -22,7 +22,7 @@
 #include "base/utils.h"
 #include "class-alloc-inl.h"
 #include "class-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "mirror/object.h"
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
index cfdab8f..19e40dc 100644
--- a/runtime/mirror/emulated_stack_frame.cc
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -19,7 +19,7 @@
 #include "array-alloc-inl.h"
 #include "array-inl.h"
 #include "class-alloc-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "handle.h"
 #include "jvalue-inl.h"
 #include "method_handles-inl.h"
diff --git a/runtime/mirror/executable.cc b/runtime/mirror/executable.cc
index d2a2ec5..3996f49 100644
--- a/runtime/mirror/executable.cc
+++ b/runtime/mirror/executable.cc
@@ -22,22 +22,24 @@
 namespace art {
 namespace mirror {
 
-template <PointerSize kPointerSize, bool kTransactionActive>
-bool Executable::CreateFromArtMethod(ArtMethod* method) {
+template <PointerSize kPointerSize>
+void Executable::InitializeFromArtMethod(ArtMethod* method) {
+  // We're initializing a newly allocated object, so we do not need to record that under
+  // a transaction. If the transaction is aborted, the whole object shall be unreachable.
   auto* interface_method = method->GetInterfaceMethodIfProxy(kPointerSize);
-  SetArtMethod<kTransactionActive>(method);
-  SetFieldObject<kTransactionActive>(DeclaringClassOffset(), method->GetDeclaringClass());
-  SetFieldObject<kTransactionActive>(
+  SetArtMethod</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(method);
+  SetFieldObject</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+      DeclaringClassOffset(), method->GetDeclaringClass());
+  SetFieldObject</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
       DeclaringClassOfOverriddenMethodOffset(), interface_method->GetDeclaringClass());
-  SetField32<kTransactionActive>(AccessFlagsOffset(), method->GetAccessFlags());
-  SetField32<kTransactionActive>(DexMethodIndexOffset(), method->GetDexMethodIndex());
-  return true;
+  SetField32</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+      AccessFlagsOffset(), method->GetAccessFlags());
+  SetField32</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+      DexMethodIndexOffset(), method->GetDexMethodIndex());
 }
 
-template bool Executable::CreateFromArtMethod<PointerSize::k32, false>(ArtMethod* method);
-template bool Executable::CreateFromArtMethod<PointerSize::k32, true>(ArtMethod* method);
-template bool Executable::CreateFromArtMethod<PointerSize::k64, false>(ArtMethod* method);
-template bool Executable::CreateFromArtMethod<PointerSize::k64, true>(ArtMethod* method);
+template void Executable::InitializeFromArtMethod<PointerSize::k32>(ArtMethod* method);
+template void Executable::InitializeFromArtMethod<PointerSize::k64>(ArtMethod* method);
 
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/executable.h b/runtime/mirror/executable.h
index 750a167..6e072af 100644
--- a/runtime/mirror/executable.h
+++ b/runtime/mirror/executable.h
@@ -32,11 +32,6 @@
 // C++ mirror of java.lang.reflect.Executable.
 class MANAGED Executable : public AccessibleObject {
  public:
-  // Called from Constructor::CreateFromArtMethod, Method::CreateFromArtMethod.
-  template <PointerSize kPointerSize, bool kTransactionActive>
-  bool CreateFromArtMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!Roles::uninterruptible_);
-
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ArtMethod* GetArtMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
     return reinterpret_cast64<ArtMethod*>(GetField64<kVerifyFlags>(ArtMethodOffset()));
@@ -56,6 +51,13 @@
     return MemberOffset(OFFSETOF_MEMBER(Executable, art_method_));
   }
 
+ protected:
+  // Called from Constructor::CreateFromArtMethod, Method::CreateFromArtMethod.
+  template <PointerSize kPointerSize>
+  void InitializeFromArtMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
+
+
  private:
   uint16_t has_real_parameter_data_;
   HeapReference<mirror::Class> declaring_class_;
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index 8a9cec4..f1f8b25 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -21,8 +21,7 @@
 
 #include "art_field-inl.h"
 #include "class-alloc-inl.h"
-#include "class_root.h"
-#include "dex_cache-inl.h"
+#include "class_root-inl.h"
 #include "object-inl.h"
 
 namespace art {
@@ -41,72 +40,14 @@
   return GetFieldObject<mirror::Class>(OFFSET_OF_OBJECT_MEMBER(Field, type_));
 }
 
-template <PointerSize kPointerSize, bool kTransactionActive>
-inline ObjPtr<mirror::Field> Field::CreateFromArtField(Thread* self,
-                                                       ArtField* field,
-                                                       bool force_resolve) {
-  StackHandleScope<2> hs(self);
-  // Try to resolve type before allocating since this is a thread suspension point.
-  Handle<mirror::Class> type = hs.NewHandle(field->ResolveType());
-
-  if (type == nullptr) {
-    if (force_resolve) {
-      if (kIsDebugBuild) {
-        self->AssertPendingException();
-      }
-      return nullptr;
-    } else {
-      // Can't resolve, clear the exception if it isn't OOME and continue with a null type.
-      mirror::Throwable* exception = self->GetException();
-      if (exception->GetClass()->DescriptorEquals("Ljava/lang/OutOfMemoryError;")) {
-        return nullptr;
-      }
-      self->ClearException();
-    }
-  }
-  auto ret = hs.NewHandle(ObjPtr<Field>::DownCast(GetClassRoot<Field>()->AllocObject(self)));
-  if (UNLIKELY(ret == nullptr)) {
-    self->AssertPendingOOMException();
-    return nullptr;
-  }
-  auto dex_field_index = field->GetDexFieldIndex();
-  auto* resolved_field = field->GetDexCache()->GetResolvedField(dex_field_index, kPointerSize);
-  if (field->GetDeclaringClass()->IsProxyClass()) {
-    DCHECK(field->IsStatic());
-    DCHECK_LT(dex_field_index, 2U);
-    // The two static fields (interfaces, throws) of all proxy classes
-    // share the same dex file indices 0 and 1. So, we can't resolve
-    // them in the dex cache.
-  } else {
-    if (resolved_field != nullptr) {
-      DCHECK_EQ(resolved_field, field);
-    } else {
-      // We rely on the field being resolved so that we can back to the ArtField
-      // (i.e. FromReflectedMethod).
-      field->GetDexCache()->SetResolvedField(dex_field_index, field, kPointerSize);
-    }
-  }
-  ret->SetType<kTransactionActive>(type.Get());
-  ret->SetDeclaringClass<kTransactionActive>(field->GetDeclaringClass());
-  ret->SetAccessFlags<kTransactionActive>(field->GetAccessFlags());
-  auto iter_range = field->IsStatic() ? field->GetDeclaringClass()->GetSFields()
-                                      : field->GetDeclaringClass()->GetIFields();
-  auto position = std::find_if(
-      iter_range.begin(), iter_range.end(), [&](const auto& f) { return &f == field; });
-  DCHECK(position != iter_range.end());
-  ret->SetArtFieldIndex<kTransactionActive>(std::distance(iter_range.begin(), position));
-  ret->SetOffset<kTransactionActive>(field->GetOffset().Int32Value());
-  return ret.Get();
+template<bool kTransactionActive, bool kCheckTransaction>
+inline void Field::SetDeclaringClass(ObjPtr<Class> c) {
+  SetFieldObject<kTransactionActive, kCheckTransaction>(DeclaringClassOffset(), c);
 }
 
-template<bool kTransactionActive>
-inline void Field::SetDeclaringClass(ObjPtr<mirror::Class> c) {
-  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, declaring_class_), c);
-}
-
-template<bool kTransactionActive>
-inline void Field::SetType(ObjPtr<mirror::Class> type) {
-  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, type_), type);
+template<bool kTransactionActive, bool kCheckTransaction>
+inline void Field::SetType(ObjPtr<Class> type) {
+  SetFieldObject<kTransactionActive, kCheckTransaction>(TypeOffset(), type);
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc
index e9669b8..7faa55c 100644
--- a/runtime/mirror/field.cc
+++ b/runtime/mirror/field.cc
@@ -45,14 +45,58 @@
 
 ArtField* Field::GetArtField() {
   ObjPtr<mirror::Class> declaring_class = GetDeclaringClass();
-  DCHECK_LT(GetArtFieldIndex(),
-            IsStatic() ? declaring_class->NumStaticFields() : declaring_class->NumInstanceFields());
   if (IsStatic()) {
+    DCHECK_LT(GetArtFieldIndex(), declaring_class->NumStaticFields());
     return declaring_class->GetStaticField(GetArtFieldIndex());
   } else {
+    DCHECK_LT(GetArtFieldIndex(), declaring_class->NumInstanceFields());
     return declaring_class->GetInstanceField(GetArtFieldIndex());
   }
 }
 
+ObjPtr<mirror::Field> Field::CreateFromArtField(Thread* self,
+                                                ArtField* field,
+                                                bool force_resolve) {
+  StackHandleScope<2> hs(self);
+  // Try to resolve type before allocating since this is a thread suspension point.
+  Handle<mirror::Class> type = hs.NewHandle(field->ResolveType());
+
+  if (type == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    if (force_resolve) {
+      return nullptr;
+    } else {
+      // Can't resolve, clear the exception if it isn't OOME and continue with a null type.
+      mirror::Throwable* exception = self->GetException();
+      if (exception->GetClass()->DescriptorEquals("Ljava/lang/OutOfMemoryError;")) {
+        return nullptr;
+      }
+      self->ClearException();
+    }
+  }
+  auto ret = hs.NewHandle(ObjPtr<Field>::DownCast(GetClassRoot<Field>()->AllocObject(self)));
+  if (UNLIKELY(ret == nullptr)) {
+    self->AssertPendingOOMException();
+    return nullptr;
+  }
+  // We're initializing a newly allocated object, so we do not need to record that under
+  // a transaction. If the transaction is aborted, the whole object shall be unreachable.
+  ret->SetType</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(type.Get());
+  ret->SetDeclaringClass</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+      field->GetDeclaringClass());
+  ret->SetAccessFlags</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+      field->GetAccessFlags());
+  auto iter_range = field->IsStatic() ? field->GetDeclaringClass()->GetSFields()
+                                      : field->GetDeclaringClass()->GetIFields();
+  auto position = std::find_if(
+      iter_range.begin(), iter_range.end(), [&](const auto& f) { return &f == field; });
+  DCHECK(position != iter_range.end());
+  ret->SetArtFieldIndex</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+      std::distance(iter_range.begin(), position));
+  ret->SetOffset</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+      field->GetOffset().Int32Value());
+  return ret.Get();
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h
index dd5ee76..63aac15 100644
--- a/runtime/mirror/field.h
+++ b/runtime/mirror/field.h
@@ -42,12 +42,6 @@
   ALWAYS_INLINE uint32_t GetArtFieldIndex() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetField32(OFFSET_OF_OBJECT_MEMBER(Field, art_field_index_));
   }
-  // Public for use by class redefinition code.
-  template<bool kTransactionActive>
-  void SetArtFieldIndex(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_) {
-    SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, art_field_index_), idx);
-  }
-
 
   ObjPtr<mirror::Class> GetDeclaringClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -77,7 +71,6 @@
 
   ArtField* GetArtField() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template <PointerSize kPointerSize, bool kTransactionActive = false>
   static ObjPtr<mirror::Field> CreateFromArtField(Thread* self,
                                                   ArtField* field,
                                                   bool force_resolve)
@@ -98,20 +91,45 @@
   int32_t art_field_index_;
   int32_t offset_;
 
-  template<bool kTransactionActive>
-  void SetDeclaringClass(ObjPtr<mirror::Class> c) REQUIRES_SHARED(Locks::mutator_lock_);
-
-  template<bool kTransactionActive>
-  void SetType(ObjPtr<mirror::Class> type) REQUIRES_SHARED(Locks::mutator_lock_);
-
-  template<bool kTransactionActive>
-  void SetAccessFlags(uint32_t flags) REQUIRES_SHARED(Locks::mutator_lock_) {
-    SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, access_flags_), flags);
+  static constexpr MemberOffset DeclaringClassOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Field, declaring_class_);
   }
 
-  template<bool kTransactionActive>
+  static constexpr MemberOffset TypeOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Field, type_);
+  }
+
+  static constexpr MemberOffset AccessFlagsOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Field, access_flags_);
+  }
+
+  static constexpr MemberOffset ArtFieldIndexOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Field, art_field_index_);
+  }
+
+  static constexpr MemberOffset OffsetOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Field, offset_);
+  }
+
+  template<bool kTransactionActive, bool kCheckTransaction = true>
+  void SetDeclaringClass(ObjPtr<Class> c) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true>
+  void SetType(ObjPtr<Class> type) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true>
+  void SetAccessFlags(uint32_t access_flags) REQUIRES_SHARED(Locks::mutator_lock_) {
+    SetField32<kTransactionActive, kCheckTransaction>(AccessFlagsOffset(), access_flags);
+  }
+
+  template<bool kTransactionActive, bool kCheckTransaction = true>
+  void SetArtFieldIndex(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_) {
+    SetField32<kTransactionActive, kCheckTransaction>(ArtFieldIndexOffset(), idx);
+  }
+
+  template<bool kTransactionActive, bool kCheckTransaction = true>
   void SetOffset(uint32_t offset) REQUIRES_SHARED(Locks::mutator_lock_) {
-    SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, offset_), offset);
+    SetField32<kTransactionActive, kCheckTransaction>(OffsetOffset(), offset);
   }
 
   friend struct art::FieldOffsets;  // for verifying offset information
diff --git a/runtime/mirror/method.cc b/runtime/mirror/method.cc
index 20a6979..f83f03c 100644
--- a/runtime/mirror/method.cc
+++ b/runtime/mirror/method.cc
@@ -17,7 +17,7 @@
 #include "method.h"
 
 #include "art_method.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "mirror/class-alloc-inl.h"
 #include "mirror/object-inl.h"
 #include "obj_ptr-inl.h"
@@ -25,43 +25,35 @@
 namespace art {
 namespace mirror {
 
-template <PointerSize kPointerSize, bool kTransactionActive>
+template <PointerSize kPointerSize>
 ObjPtr<Method> Method::CreateFromArtMethod(Thread* self, ArtMethod* method) {
   DCHECK(!method->IsConstructor()) << method->PrettyMethod();
   ObjPtr<Method> ret = ObjPtr<Method>::DownCast(GetClassRoot<Method>()->AllocObject(self));
   if (LIKELY(ret != nullptr)) {
-    ret->Executable::CreateFromArtMethod<kPointerSize, kTransactionActive>(method);
+    ret->InitializeFromArtMethod<kPointerSize>(method);
   }
   return ret;
 }
 
-template ObjPtr<Method> Method::CreateFromArtMethod<PointerSize::k32, false>(
+template ObjPtr<Method> Method::CreateFromArtMethod<PointerSize::k32>(
     Thread* self, ArtMethod* method);
-template ObjPtr<Method> Method::CreateFromArtMethod<PointerSize::k32, true>(
-    Thread* self, ArtMethod* method);
-template ObjPtr<Method> Method::CreateFromArtMethod<PointerSize::k64, false>(
-    Thread* self, ArtMethod* method);
-template ObjPtr<Method> Method::CreateFromArtMethod<PointerSize::k64, true>(
+template ObjPtr<Method> Method::CreateFromArtMethod<PointerSize::k64>(
     Thread* self, ArtMethod* method);
 
-template <PointerSize kPointerSize, bool kTransactionActive>
+template <PointerSize kPointerSize>
 ObjPtr<Constructor> Constructor::CreateFromArtMethod(Thread* self, ArtMethod* method) {
   DCHECK(method->IsConstructor()) << method->PrettyMethod();
   ObjPtr<Constructor> ret =
       ObjPtr<Constructor>::DownCast(GetClassRoot<Constructor>()->AllocObject(self));
   if (LIKELY(ret != nullptr)) {
-    ret->Executable::CreateFromArtMethod<kPointerSize, kTransactionActive>(method);
+    ret->InitializeFromArtMethod<kPointerSize>(method);
   }
   return ret;
 }
 
-template ObjPtr<Constructor> Constructor::CreateFromArtMethod<PointerSize::k32, false>(
+template ObjPtr<Constructor> Constructor::CreateFromArtMethod<PointerSize::k32>(
     Thread* self, ArtMethod* method);
-template ObjPtr<Constructor> Constructor::CreateFromArtMethod<PointerSize::k32, true>(
-    Thread* self, ArtMethod* method);
-template ObjPtr<Constructor> Constructor::CreateFromArtMethod<PointerSize::k64, false>(
-    Thread* self, ArtMethod* method);
-template ObjPtr<Constructor> Constructor::CreateFromArtMethod<PointerSize::k64, true>(
+template ObjPtr<Constructor> Constructor::CreateFromArtMethod<PointerSize::k64>(
     Thread* self, ArtMethod* method);
 
 }  // namespace mirror
diff --git a/runtime/mirror/method.h b/runtime/mirror/method.h
index a73cd45..93d61b6 100644
--- a/runtime/mirror/method.h
+++ b/runtime/mirror/method.h
@@ -30,7 +30,7 @@
 // C++ mirror of java.lang.reflect.Method.
 class MANAGED Method : public Executable {
  public:
-  template <PointerSize kPointerSize, bool kTransactionActive>
+  template <PointerSize kPointerSize>
   static ObjPtr<Method> CreateFromArtMethod(Thread* self, ArtMethod* method)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
 
@@ -41,7 +41,7 @@
 // C++ mirror of java.lang.reflect.Constructor.
 class MANAGED Constructor: public Executable {
  public:
-  template <PointerSize kPointerSize, bool kTransactionActive>
+  template <PointerSize kPointerSize>
   static ObjPtr<Constructor> CreateFromArtMethod(Thread* self, ArtMethod* method)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
 
diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc
index dd25fc9..4f1a18b 100644
--- a/runtime/mirror/method_handle_impl.cc
+++ b/runtime/mirror/method_handle_impl.cc
@@ -17,7 +17,7 @@
 #include "method_handle_impl-inl.h"
 
 #include "class-alloc-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 
 namespace art {
 namespace mirror {
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
index e0e7b06..e9c41f9 100644
--- a/runtime/mirror/method_handles_lookup.cc
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -17,7 +17,7 @@
 #include "method_handles_lookup.h"
 
 #include "class-alloc-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/modifiers.h"
 #include "handle_scope.h"
 #include "jni/jni_internal.h"
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 1d88d85..821de7b 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -17,7 +17,7 @@
 #include "method_type-inl.h"
 
 #include "class-alloc-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "method_handles.h"
 #include "obj_ptr-inl.h"
 #include "object_array-alloc-inl.h"
diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc
index 4e9ba51..742960b 100644
--- a/runtime/mirror/method_type_test.cc
+++ b/runtime/mirror/method_type_test.cc
@@ -22,7 +22,7 @@
 #include "class-inl.h"
 #include "class_linker-inl.h"
 #include "class_loader.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_runtime_test.h"
 #include "handle_scope-inl.h"
 #include "object_array-alloc-inl.h"
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index ee137f0..80ed832 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -30,7 +30,7 @@
 #include "class-inl.h"
 #include "class_linker-inl.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_runtime_test.h"
 #include "dex/dex_file.h"
 #include "entrypoints/entrypoint_utils-inl.h"
@@ -253,6 +253,48 @@
   TestPrimitiveArray<ShortArray>(class_linker_);
 }
 
+TEST_F(ObjectTest, PointerArrayWriteRead) {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<2> hs(soa.Self());
+
+  Handle<PointerArray> a32 =
+      hs.NewHandle(ObjPtr<PointerArray>::DownCast<Array>(IntArray::Alloc(soa.Self(), 1)));
+  ASSERT_TRUE(a32 != nullptr);
+  ASSERT_EQ(1, a32->GetLength());
+  EXPECT_EQ(0u, (a32->GetElementPtrSize<uint32_t, PointerSize::k32>(0u)));
+  EXPECT_EQ(0u, (a32->GetElementPtrSizeUnchecked<uint32_t, PointerSize::k32>(0u)));
+  for (uint32_t value : { 0u, 1u, 0x7fffffffu, 0x80000000u, 0xffffffffu }) {
+    a32->SetElementPtrSize(0u, value, PointerSize::k32);
+    EXPECT_EQ(value, (a32->GetElementPtrSize<uint32_t, PointerSize::k32>(0u)));
+    EXPECT_EQ(value, (a32->GetElementPtrSizeUnchecked<uint32_t, PointerSize::k32>(0u)));
+    // Check that the value matches also when retrieved as `uint64_t`.
+    // This is a regression test for unintended sign-extension. b/155780442
+    // (Using `uint64_t` rather than `uintptr_t`, so that the 32-bit test checks this too.)
+    EXPECT_EQ(value, (a32->GetElementPtrSize<uint64_t, PointerSize::k32>(0u)));
+    EXPECT_EQ(value, (a32->GetElementPtrSizeUnchecked<uint64_t, PointerSize::k32>(0u)));
+  }
+
+  Handle<PointerArray> a64 =
+      hs.NewHandle(ObjPtr<PointerArray>::DownCast<Array>(LongArray::Alloc(soa.Self(), 1)));
+  ASSERT_TRUE(a64 != nullptr);
+  ASSERT_EQ(1, a64->GetLength());
+  EXPECT_EQ(0u, (a64->GetElementPtrSize<uint32_t, PointerSize::k64>(0u)));
+  EXPECT_EQ(0u, (a64->GetElementPtrSizeUnchecked<uint32_t, PointerSize::k64>(0u)));
+  for (uint64_t value : { UINT64_C(0),
+                          UINT64_C(1),
+                          UINT64_C(0x7fffffff),
+                          UINT64_C(0x80000000),
+                          UINT64_C(0xffffffff),
+                          UINT64_C(0x100000000),
+                          UINT64_C(0x7fffffffffffffff),
+                          UINT64_C(0x8000000000000000),
+                          UINT64_C(0xffffffffffffffff) }) {
+    a64->SetElementPtrSize(0u, value, PointerSize::k64);
+    EXPECT_EQ(value, (a64->GetElementPtrSize<uint64_t, PointerSize::k64>(0u)));
+    EXPECT_EQ(value, (a64->GetElementPtrSizeUnchecked<uint64_t, PointerSize::k64>(0u)));
+  }
+}
+
 TEST_F(ObjectTest, PrimitiveArray_Double_Alloc) {
   using ArrayT = DoubleArray;
   ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index 2a272d3..7fc6c09 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -18,7 +18,7 @@
 
 #include "class-alloc-inl.h"
 #include "class.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "handle_scope-inl.h"
 #include "object-inl.h"
diff --git a/runtime/mirror/string-alloc-inl.h b/runtime/mirror/string-alloc-inl.h
index e2b0805..5f3abd7 100644
--- a/runtime/mirror/string-alloc-inl.h
+++ b/runtime/mirror/string-alloc-inl.h
@@ -23,7 +23,7 @@
 #include "array.h"
 #include "base/bit_utils.h"
 #include "class.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "gc/allocator_type.h"
 #include "gc/heap-inl.h"
 #include "obj_ptr.h"
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index faa605c..a03277c 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -22,7 +22,7 @@
 #include "base/enums.h"
 #include "base/utils.h"
 #include "class-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "obj_ptr-inl.h"
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index 6d5ff2c..3079c35 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -21,7 +21,7 @@
 #include "base/casts.h"
 #include "class-inl.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "intrinsics_enum.h"
 #include "jni/jni_internal.h"
 #include "jvalue-inl.h"
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index 6c765d6..bb67a4a 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -24,7 +24,7 @@
 #include "class-inl.h"
 #include "class_linker-inl.h"
 #include "class_loader.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_runtime_test.h"
 #include "handle_scope-inl.h"
 #include "jvalue-inl.h"
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 32733a8..9d2dfac 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -84,7 +84,7 @@
   ScopedFastNativeObjectAccess soa(env);
   auto fn = [](Thread* thread, const ScopedFastNativeObjectAccess& soaa)
       REQUIRES_SHARED(Locks::mutator_lock_) -> jobject {
-    return thread->CreateInternalStackTrace<false>(soaa);
+    return thread->CreateInternalStackTrace(soaa);
   };
   jobject trace = GetThreadStack(soa, javaThread, fn);
   if (trace == nullptr) {
@@ -151,7 +151,7 @@
   ScopedFastNativeObjectAccess soa(env);
   auto fn = [](Thread* thread, const ScopedFastNativeObjectAccess& soaa)
      REQUIRES_SHARED(Locks::mutator_lock_) -> jobject {
-    return thread->CreateInternalStackTrace<false>(soaa);
+    return thread->CreateInternalStackTrace(soaa);
   };
   jobject trace = GetThreadStack(soa, javaThread, fn);
   if (trace == nullptr) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 2c537c6..7ea0746 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -22,7 +22,7 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "common_throws.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
@@ -33,7 +33,7 @@
 #include "mirror/class-alloc-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
-#include "mirror/field-inl.h"
+#include "mirror/field.h"
 #include "mirror/method.h"
 #include "mirror/method_handles_lookup.h"
 #include "mirror/object-inl.h"
@@ -312,7 +312,7 @@
   for (ArtField& field : ifields) {
     if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
       ObjPtr<mirror::Field> reflect_field =
-          mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, &field, force_resolve);
+          mirror::Field::CreateFromArtField(self, &field, force_resolve);
       if (reflect_field == nullptr) {
         if (kIsDebugBuild) {
           self->AssertPendingException();
@@ -326,7 +326,7 @@
   for (ArtField& field : sfields) {
     if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
       ObjPtr<mirror::Field> reflect_field =
-          mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, &field, force_resolve);
+          mirror::Field::CreateFromArtField(self, &field, force_resolve);
       if (reflect_field == nullptr) {
         if (kIsDebugBuild) {
           self->AssertPendingException();
@@ -417,11 +417,11 @@
   }
   ArtField* art_field = FindFieldByName(name, c->GetIFieldsPtr());
   if (art_field != nullptr) {
-    return mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, art_field, true);
+    return mirror::Field::CreateFromArtField(self, art_field, true);
   }
   art_field = FindFieldByName(name, c->GetSFieldsPtr());
   if (art_field != nullptr) {
-    return mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, art_field, true);
+    return mirror::Field::CreateFromArtField(self, art_field, true);
   }
   return nullptr;
 }
@@ -540,10 +540,10 @@
     return nullptr;
   }
   Handle<mirror::Constructor> result = hs.NewHandle(
-      mirror::Class::GetDeclaredConstructorInternal<kRuntimePointerSize, false>(
-      soa.Self(),
-      klass,
-      soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
+      mirror::Class::GetDeclaredConstructorInternal<kRuntimePointerSize>(
+          soa.Self(),
+          klass,
+          soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
   if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
     return nullptr;
   }
@@ -588,7 +588,7 @@
       DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
       DCHECK(!Runtime::Current()->IsActiveTransaction());
       ObjPtr<mirror::Constructor> constructor =
-          mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), &m);
+          mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), &m);
       if (UNLIKELY(constructor == nullptr)) {
         soa.Self()->AssertPendingOOMException();
         return nullptr;
@@ -611,7 +611,7 @@
     return nullptr;
   }
   Handle<mirror::Method> result = hs.NewHandle(
-      mirror::Class::GetDeclaredMethodInternal<kRuntimePointerSize, false>(
+      mirror::Class::GetDeclaredMethodInternal<kRuntimePointerSize>(
           soa.Self(),
           klass,
           soa.Decode<mirror::String>(name),
@@ -659,7 +659,7 @@
       DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
       DCHECK(!Runtime::Current()->IsActiveTransaction());
       ObjPtr<mirror::Method> method =
-          mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), &m);
+          mirror::Method::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), &m);
       if (method == nullptr) {
         soa.Self()->AssertPendingException();
         return nullptr;
diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc
index b5ef7d8..b89e287 100644
--- a/runtime/native/java_lang_Throwable.cc
+++ b/runtime/native/java_lang_Throwable.cc
@@ -27,7 +27,7 @@
 
 static jobject Throwable_nativeFillInStackTrace(JNIEnv* env, jclass) {
   ScopedFastNativeObjectAccess soa(env);
-  return soa.Self()->CreateInternalStackTrace<false>(soa);
+  return soa.Self()->CreateInternalStackTrace(soa);
 }
 
 static jobjectArray Throwable_nativeGetStackTrace(JNIEnv* env, jclass, jobject javaStackState) {
diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
index 0b26bd7..45d9a8a 100644
--- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc
+++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
@@ -40,23 +40,18 @@
   // a Method for method invokers and a Constructor for constructors.
   const mirror::MethodHandle::Kind handle_kind = handle->GetHandleKind();
 
-  // We check this here because we pass false to CreateFromArtField and
-  // CreateFromArtMethod.
-  DCHECK(!Runtime::Current()->IsActiveTransaction());
-
   MutableHandle<mirror::Object> h_object(hs.NewHandle<mirror::Object>(nullptr));
   if (handle_kind >= mirror::MethodHandle::kFirstAccessorKind) {
     ArtField* const field = handle->GetTargetField();
-    h_object.Assign(mirror::Field::CreateFromArtField<kRuntimePointerSize, false>(
-        soa.Self(), field, /* force_resolve= */ false));
+    h_object.Assign(
+        mirror::Field::CreateFromArtField(soa.Self(), field, /* force_resolve= */ false));
   } else {
     ArtMethod* const method = handle->GetTargetMethod();
     if (method->IsConstructor()) {
-      h_object.Assign(mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(
-          soa.Self(), method));
+      h_object.Assign(
+          mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), method));
     } else {
-      h_object.Assign(mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(
-          soa.Self(), method));
+      h_object.Assign(mirror::Method::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), method));
     }
   }
 
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index f9cdc36..e11b0e4 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -21,7 +21,7 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/dex_file_annotations.h"
 #include "jni/jni_internal.h"
 #include "mirror/class-alloc-inl.h"
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index 2ce56b5..fef16b9 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -20,7 +20,7 @@
 #include "nativehelper/jni_macros.h"
 
 #include "art_method-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/dex_file_annotations.h"
 #include "handle.h"
 #include "jni/jni_internal.h"
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index 0d9a257..66fef4c 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -22,7 +22,7 @@
 #include "base/enums.h"
 #include "class_linker-inl.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "dex/dex_file_annotations.h"
 #include "jni/jni_internal.h"
 #include "mirror/class-inl.h"
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index d405735..b50825a 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -60,7 +60,7 @@
   if (static_cast<uint32_t>(thin_lock_id) == self->GetThreadId()) {
     // No need to suspend ourself to build stacktrace.
     ScopedObjectAccess soa(env);
-    jobject internal_trace = self->CreateInternalStackTrace<false>(soa);
+    jobject internal_trace = self->CreateInternalStackTrace(soa);
     trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
   } else {
     ThreadList* thread_list = Runtime::Current()->GetThreadList();
@@ -78,7 +78,7 @@
     if (thread != nullptr) {
       {
         ScopedObjectAccess soa(env);
-        jobject internal_trace = thread->CreateInternalStackTrace<false>(soa);
+        jobject internal_trace = thread->CreateInternalStackTrace(soa);
         trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
       }
       // Restart suspended thread.
diff --git a/runtime/nterp_helpers.cc b/runtime/nterp_helpers.cc
index a2ec882..d622241 100644
--- a/runtime/nterp_helpers.cc
+++ b/runtime/nterp_helpers.cc
@@ -59,6 +59,8 @@
  *    ----------------      registers array for easy access from nterp when returning.
  *    |  dex_pc_ptr  |      Pointer to the dex instruction being executed.
  *    ----------------      Stored whenever nterp goes into the runtime.
+ *    |  alignment   |      Pointer aligment for dex_pc_ptr and caller_fp.
+ *    ----------------
  *    |              |      In case nterp calls compiled code, we reserve space
  *    |     out      |      for out registers. This space will be used for
  *    |   registers  |      arguments passed on stack.
@@ -97,6 +99,11 @@
   const uint16_t num_regs = accessor.RegistersSize();
   const uint16_t out_regs = accessor.OutsSize();
 
+  // Note: There may be two pieces of alignment but there is no need to align
+  // out args to `kPointerSize` separately before aligning to kStackAlignment.
+  static_assert(IsAligned<kPointerSize>(kStackAlignment));
+  static_assert(IsAligned<kPointerSize>(NterpGetFrameEntrySize()));
+  static_assert(IsAligned<kPointerSize>(kVRegSize * 2));
   size_t frame_size =
       NterpGetFrameEntrySize() +
       (num_regs * kVRegSize) * 2 +  // dex registers and reference registers
@@ -128,7 +135,7 @@
   // The references array is just above the saved frame pointer.
   return reinterpret_cast<uintptr_t>(frame) +
       kPointerSize +  // method
-      (out_regs * kVRegSize) +  // out arguments
+      RoundUp(out_regs * kVRegSize, kPointerSize) +  // out arguments and pointer alignment
       kPointerSize +  // saved dex pc
       kPointerSize;  // previous frame.
 }
@@ -138,7 +145,7 @@
   const uint16_t out_regs = accessor.OutsSize();
   uintptr_t dex_pc_ptr = reinterpret_cast<uintptr_t>(frame) +
       kPointerSize +  // method
-      (out_regs * kVRegSize);  // out arguments
+      RoundUp(out_regs * kVRegSize, kPointerSize);  // out arguments and pointer alignment
   CodeItemInstructionAccessor instructions((*frame)->DexInstructions());
   return *reinterpret_cast<const uint16_t**>(dex_pc_ptr) - instructions.Insns();
 }
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 723bf49..52e0dd1 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -396,6 +396,10 @@
   return IsKeyEnabled(OatHeader::kNativeDebuggableKey);
 }
 
+bool OatHeader::RequiresImage() const {
+  return IsKeyEnabled(OatHeader::kRequiresImage);
+}
+
 CompilerFilter::Filter OatHeader::GetCompilerFilter() const {
   CompilerFilter::Filter filter;
   const char* key_value = GetStoreValueByKey(kCompilerFilter);
diff --git a/runtime/oat.h b/runtime/oat.h
index c1659e4..0645e4c 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
-  // Last oat version changed reason: Change x86-64 @CriticalNative hidden arg register to RAX.
-  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '1', '\0' } };
+  // Last oat version changed reason: Add requires-image flag.
+  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '2', '\0' } };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
@@ -44,6 +44,7 @@
   static constexpr const char* kBootClassPathChecksumsKey = "bootclasspath-checksums";
   static constexpr const char* kConcurrentCopying = "concurrent-copying";
   static constexpr const char* kCompilationReasonKey = "compilation-reason";
+  static constexpr const char* kRequiresImage = "requires-image";
 
   static constexpr const char kTrueValue[] = "true";
   static constexpr const char kFalseValue[] = "false";
@@ -102,6 +103,7 @@
   bool IsNativeDebuggable() const;
   CompilerFilter::Filter GetCompilerFilter() const;
   bool IsConcurrentCopying() const;
+  bool RequiresImage() const;
 
  private:
   bool KeyHasValue(const char* key, const char* value, size_t value_size) const;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 346c112..a7054d3 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -2124,6 +2124,8 @@
   return oat_dex_file->GetOatClass(class_def_idx);
 }
 
+bool OatFile::RequiresImage() const { return GetOatHeader().RequiresImage(); }
+
 static void DCheckIndexToBssMapping(const OatFile* oat_file,
                                     uint32_t number_of_indexes,
                                     size_t slot_size,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index dce34d9..ee5aede 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -378,6 +378,9 @@
     return external_dex_files_.empty();
   }
 
+  // Returns whether an image (e.g. app image) is required to safely execute this OAT file.
+  bool RequiresImage() const;
+
  protected:
   OatFile(const std::string& filename, bool executable);
 
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index ed47ca3..7b1a5eb 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -1437,6 +1437,7 @@
 TEST_F(OatFileAssistantTest, GetDexLocation) {
   std::string dex_location = GetScratchDir() + "/TestDex.jar";
   std::string oat_location = GetOdexDir() + "/TestDex.odex";
+  std::string art_location = GetOdexDir() + "/TestDex.art";
 
   // Start the runtime to initialize the system's class loader.
   Thread::Current()->TransitionFromSuspendedToRunnable();
@@ -1463,6 +1464,7 @@
     args.push_back("--dex-file=" + dex_location);
     args.push_back("--dex-location=TestDex.jar");
     args.push_back("--oat-file=" + oat_location);
+    args.push_back("--app-image-file=" + art_location);
     std::string error_msg;
     ASSERT_TRUE(DexoptTest::Dex2Oat(args, &error_msg)) << error_msg;
   }
@@ -1490,6 +1492,7 @@
   odex_dir = odex_dir + std::string(GetInstructionSetString(kRuntimeISA));
   mkdir(odex_dir.c_str(), 0700);
   std::string oat_location = odex_dir + "/" + filebase + ".odex";
+  std::string art_location = odex_dir + "/" + filebase + ".art";
   // Clean up in case previous run crashed.
   remove(oat_location.c_str());
 
@@ -1527,6 +1530,7 @@
     args.push_back("--dex-file=" + dex_location);
     args.push_back("--dex-location=" + filebase + ".jar");
     args.push_back("--oat-file=" + oat_location);
+    args.push_back("--app-image-file=" + art_location);
     std::string error_msg;
     ASSERT_TRUE(DexoptTest::Dex2Oat(args, &error_msg)) << error_msg;
   }
@@ -1554,6 +1558,41 @@
   EXPECT_EQ(0, remove(oat_location.c_str()));
 }
 
+// Make sure OAT files that require app images are not loaded as executable.
+TEST_F(OatFileAssistantTest, LoadOatNoArt) {
+  std::string dex_location = GetScratchDir() + "/TestDex.jar";
+  std::string odex_location = GetOdexDir() + "/TestDex.odex";
+  std::string art_location = GetOdexDir() + "/TestDex.art";
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOdexForTest(dex_location,
+                      odex_location,
+                      CompilerFilter::kSpeed,
+                      "install",
+                      {
+                          "--app-image-file=" + art_location,
+                      });
+
+  unlink(art_location.c_str());
+
+  std::vector<std::string> error_msgs;
+  const OatFile* oat_file = nullptr;
+
+  // Start the runtime to initialize the system's class loader.
+  Thread::Current()->TransitionFromSuspendedToRunnable();
+  runtime_->Start();
+
+  const auto dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(
+      dex_location.c_str(),
+      Runtime::Current()->GetSystemClassLoader(),
+      /*dex_elements=*/nullptr,
+      &oat_file,
+      &error_msgs);
+
+  EXPECT_FALSE(dex_files.empty());
+  EXPECT_NE(oat_file, nullptr);
+  EXPECT_FALSE(oat_file->IsExecutable());
+}
+
 // TODO: More Tests:
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index ff743d5..14ec631 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -492,13 +492,14 @@
   const OatFile* source_oat_file = nullptr;
   CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions;
   std::string error_msg;
+  bool accept_oat_file = false;
   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.
     check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg);
-    bool accept_oat_file = AcceptOatFile(check_collision_result);
+    accept_oat_file = AcceptOatFile(check_collision_result);
     if (!accept_oat_file) {
       // Failed the collision check. Print warning.
       if (runtime->IsDexFileFallbackEnabled()) {
@@ -531,30 +532,24 @@
 
       LOG(WARNING) << error_msg;
     }
-
-    if (accept_oat_file) {
-      VLOG(class_linker) << "Registering " << oat_file->GetLocation();
-      source_oat_file = RegisterOatFile(std::move(oat_file));
-      *out_oat_file = source_oat_file;
-    }
   }
 
   std::vector<std::unique_ptr<const DexFile>> dex_files;
 
   // Load the dex files from the oat file.
-  if (source_oat_file != nullptr) {
-    bool added_image_space = false;
-    if (source_oat_file->IsExecutable()) {
+  bool added_image_space = false;
+  if (accept_oat_file) {
+    if (oat_file->IsExecutable()) {
       ScopedTrace app_image_timing("AppImage:Loading");
 
       // 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 (ShouldLoadAppImage(check_collision_result,
-                             source_oat_file,
+                             oat_file.get(),
                              context.get(),
                              &error_msg)) {
-        image_space = oat_file_assistant.OpenImageSpace(source_oat_file);
+        image_space = oat_file_assistant.OpenImageSpace(oat_file.get());
       }
       if (image_space != nullptr) {
         ScopedObjectAccess soa(self);
@@ -608,7 +603,18 @@
     }
     if (!added_image_space) {
       DCHECK(dex_files.empty());
-      dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
+
+      if (oat_file->RequiresImage()) {
+        // If we could not load the image, but the OAT file requires it, we have to reload the OAT
+        // file as non-executable.
+        OatFileAssistant nonexecutable_oat_file_assistant(dex_location,
+                                                          kRuntimeISA,
+                                                          /*load_executable=*/false,
+                                                          only_use_system_oat_files_);
+        oat_file.reset(nonexecutable_oat_file_assistant.GetBestOatFile().release());
+      }
+
+      dex_files = oat_file_assistant.LoadDexFiles(*oat_file.get(), dex_location);
 
       // Register for tracking.
       for (const auto& dex_file : dex_files) {
@@ -616,13 +622,17 @@
       }
     }
     if (dex_files.empty()) {
-      error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation());
+      error_msgs->push_back("Failed to open dex files from " + oat_file->GetLocation());
     } else {
       // Opened dex files from an oat file, madvise them to their loaded state.
        for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
          OatDexFile::MadviseDexFile(*dex_file, MadviseState::kMadviseStateAtLoad);
        }
     }
+
+    VLOG(class_linker) << "Registering " << oat_file->GetLocation();
+    source_oat_file = RegisterOatFile(std::move(oat_file));
+    *out_oat_file = source_oat_file;
   }
 
   // Fall back to running out of the original dex file if we couldn't load any
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index c8ee9b4..9a1133e 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -114,7 +114,8 @@
   }
 
   bool Contains(uintptr_t pc) const {
-    uintptr_t code_start = reinterpret_cast<uintptr_t>(code_);
+    // Remove hwasan tag to make comparison below valid. The PC from the stack does not have it.
+    uintptr_t code_start = reinterpret_cast<uintptr_t>(HWASanUntag(code_));
     static_assert(kRuntimeISA != InstructionSet::kThumb2, "kThumb2 cannot be a runtime ISA");
     if (kRuntimeISA == InstructionSet::kArm) {
       // On Thumb-2, the pc is offset by one.
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 351d5e8..c62caa9 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -389,6 +389,10 @@
           .WithType<bool>()
           .WithValueMap({{"false", false}, {"true", true}})
           .IntoKey(M::VerifierMissingKThrowFatal)
+      .Define("-XX:PerfettoHprof=_")
+          .WithType<bool>()
+          .WithValueMap({{"false", false}, {"true", true}})
+          .IntoKey(M::PerfettoHprof)
       .Ignore({
           "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
           "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index b78855f..d8e170b 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -20,7 +20,7 @@
 #include "art_field-inl.h"
 #include "base/enums.h"
 #include "common_runtime_test.h"
-#include "mirror/field-inl.h"
+#include "mirror/field.h"
 #include "proxy_test.h"
 #include "scoped_thread_state_change-inl.h"
 #include "well_known_classes.h"
@@ -173,17 +173,13 @@
   ASSERT_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
   ASSERT_FALSE(Runtime::Current()->IsActiveTransaction());
   Handle<mirror::Field> field00 =
-      hs.NewHandle(mirror::Field::CreateFromArtField<kRuntimePointerSize, false>(
-          soa.Self(), &static_fields0->At(0), true));
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0->At(0), true));
   Handle<mirror::Field> field01 =
-      hs.NewHandle(mirror::Field::CreateFromArtField<kRuntimePointerSize, false>(
-          soa.Self(), &static_fields0->At(1), true));
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0->At(1), true));
   Handle<mirror::Field> field10 =
-      hs.NewHandle(mirror::Field::CreateFromArtField<kRuntimePointerSize, false>(
-          soa.Self(), &static_fields1->At(0), true));
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1->At(0), true));
   Handle<mirror::Field> field11 =
-      hs.NewHandle(mirror::Field::CreateFromArtField<kRuntimePointerSize, false>(
-          soa.Self(), &static_fields1->At(1), true));
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1->At(1), true));
   EXPECT_EQ(field00->GetArtField(), &static_fields0->At(0));
   EXPECT_EQ(field01->GetArtField(), &static_fields0->At(1));
   EXPECT_EQ(field10->GetArtField(), &static_fields1->At(0));
diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h
index 23e536d..9a01441 100644
--- a/runtime/proxy_test.h
+++ b/runtime/proxy_test.h
@@ -22,7 +22,7 @@
 
 #include "art_method-inl.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/method.h"
 #include "obj_ptr-inl.h"
@@ -33,11 +33,11 @@
 // 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.
-ObjPtr<mirror::Class> GenerateProxyClass(ScopedObjectAccess& soa,
-                                         jobject jclass_loader,
-                                         ClassLinker* class_linker,
-                                         const char* className,
-                                         const std::vector<Handle<mirror::Class>>& interfaces)
+inline ObjPtr<mirror::Class> GenerateProxyClass(ScopedObjectAccess& soa,
+                                                jobject jclass_loader,
+                                                ClassLinker* class_linker,
+                                                const char* className,
+                                                const std::vector<Handle<mirror::Class>>& interfaces)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> javaLangObject = hs.NewHandle(GetClassRoot<mirror::Object>());
@@ -76,14 +76,14 @@
   DCHECK(!Runtime::Current()->IsActiveTransaction());
   soa.Env()->SetObjectArrayElement(
       proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
-          mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
+          mirror::Method::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), method)));
   method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize);
   CHECK(method != nullptr);
   CHECK(!method->IsDirect());
   CHECK(method->GetDeclaringClass() == javaLangObject.Get());
   soa.Env()->SetObjectArrayElement(
       proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
-          mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
+          mirror::Method::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), method)));
   method = javaLangObject->FindClassMethod(
       "toString", "()Ljava/lang/String;", kRuntimePointerSize);
   CHECK(method != nullptr);
@@ -91,13 +91,13 @@
   CHECK(method->GetDeclaringClass() == javaLangObject.Get());
   soa.Env()->SetObjectArrayElement(
       proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
-          mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
+          mirror::Method::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), method)));
   // Now adds all interfaces virtual methods.
   for (Handle<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)));
+              mirror::Method::CreateFromArtMethod<kRuntimePointerSize>(soa.Self(), &m)));
     }
   }
   CHECK_EQ(array_index, methods_count);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8fbc178..ad4d7a7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -68,7 +68,7 @@
 #include "base/unix_file/fd_file.h"
 #include "base/utils.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "compiler_callbacks.h"
 #include "debugger.h"
 #include "dex/art_dex_file_loader.h"
@@ -296,7 +296,8 @@
       process_state_(kProcessStateJankPerceptible),
       zygote_no_threads_(false),
       verifier_logging_threshold_ms_(100),
-      verifier_missing_kthrow_fatal_(false) {
+      verifier_missing_kthrow_fatal_(false),
+      perfetto_hprof_enabled_(false) {
   static_assert(Runtime::kCalleeSaveSize ==
                     static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
   CheckConstants();
@@ -1062,8 +1063,9 @@
   StartSignalCatcher();
 
   ScopedObjectAccess soa(Thread::Current());
-  if (Dbg::IsJdwpAllowed() || IsProfileableFromShell() || IsJavaDebuggable() ||
-      Runtime::Current()->IsSystemServer()) {
+  if (IsPerfettoHprofEnabled() &&
+      (Dbg::IsJdwpAllowed() || IsProfileableFromShell() || IsJavaDebuggable() ||
+       Runtime::Current()->IsSystemServer())) {
     std::string err;
     ScopedTrace tr("perfetto_hprof init.");
     ScopedThreadSuspension sts(Thread::Current(), ThreadState::kNative);
@@ -1199,6 +1201,7 @@
   MemMap::Init();
 
   verifier_missing_kthrow_fatal_ = runtime_options.GetOrDefault(Opt::VerifierMissingKThrowFatal);
+  perfetto_hprof_enabled_ = runtime_options.GetOrDefault(Opt::PerfettoHprof);
 
   // Try to reserve a dedicated fault page. This is allocated for clobbered registers and sentinels.
   // If we cannot reserve it, log a warning.
@@ -1785,6 +1788,14 @@
     callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInitialAgents);
   }
 
+  if (IsZygote() && IsPerfettoHprofEnabled()) {
+    constexpr const char* plugin_name = kIsDebugBuild ?
+        "libperfetto_hprofd.so" : "libperfetto_hprof.so";
+    // Load eagerly in Zygote to improve app startup times. This will make
+    // subsequent dlopens for the library no-ops.
+    dlopen(plugin_name, RTLD_NOW | RTLD_LOCAL);
+  }
+
   VLOG(startup) << "Runtime::Init exiting";
 
   // Set OnlyUseSystemOatFiles only after boot classpath has been set up.
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 655f05f..822c0ac 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -976,6 +976,10 @@
     return verifier_missing_kthrow_fatal_;
   }
 
+  bool IsPerfettoHprofEnabled() const {
+    return perfetto_hprof_enabled_;
+  }
+
   // Return true if we should load oat files as executable or not.
   bool GetOatFilesExecutable() const;
 
@@ -1336,6 +1340,7 @@
       gc::space::ImageSpaceLoadingOrder::kSystemFirst;
 
   bool verifier_missing_kthrow_fatal_;
+  bool perfetto_hprof_enabled_;
 
   // Note: See comments on GetFaultMessage.
   friend std::string GetFaultMessageForAbortLogging();
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 877a5a0..5707a33 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -173,4 +173,17 @@
 RUNTIME_OPTIONS_KEY (bool,                FastClassNotFoundException,     true)
 RUNTIME_OPTIONS_KEY (bool,                VerifierMissingKThrowFatal,     true)
 
+// Whether to allow loading of the perfetto hprof plugin.
+// Even with this option set, we will still only actually load the plugin
+// if we are on a userdebug build or the app is debuggable or profileable.
+//
+// We do not want to enable this by default because PerfettoHprof does not
+// work on host, and we do not want to enable it in tests.
+//
+// Switching this on adds ~500us to the startup on userdebug builds, or for
+// profileable / debuggable apps.
+//
+// This is set to true in frameworks/base/core/jni/AndroidRuntime.cpp.
+RUNTIME_OPTIONS_KEY (bool,                PerfettoHprof,                  false)
+
 #undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 77b9f4f..21b8d05 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -56,7 +56,7 @@
 #include "base/to_str.h"
 #include "base/utils.h"
 #include "class_linker-inl.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "debugger.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
@@ -162,7 +162,6 @@
   CHECK(kUseReadBarrier);
   tls32_.is_gc_marking = is_marking;
   UpdateReadBarrierEntrypoints(&tlsPtr_.quick_entrypoints, /* is_active= */ is_marking);
-  ResetQuickAllocEntryPointsForThread(is_marking);
 }
 
 void Thread::InitTlsEntryPoints() {
@@ -177,12 +176,8 @@
   InitEntryPoints(&tlsPtr_.jni_entrypoints, &tlsPtr_.quick_entrypoints);
 }
 
-void Thread::ResetQuickAllocEntryPointsForThread(bool is_marking) {
-  if (kUseReadBarrier && kRuntimeISA != InstructionSet::kX86_64) {
-    // Allocation entrypoint switching is currently only implemented for X86_64.
-    is_marking = true;
-  }
-  ResetQuickAllocEntryPoints(&tlsPtr_.quick_entrypoints, is_marking);
+void Thread::ResetQuickAllocEntryPointsForThread() {
+  ResetQuickAllocEntryPoints(&tlsPtr_.quick_entrypoints);
 }
 
 class DeoptimizationContextRecord {
@@ -2474,12 +2469,6 @@
   CHECK(tlsPtr_.flip_function == nullptr);
   CHECK_EQ(tls32_.is_transitioning_to_runnable, false);
 
-  if (kUseReadBarrier) {
-    Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()
-        ->AssertNoThreadMarkStackMapping(this);
-    gc::accounting::AtomicStack<mirror::Object>* tl_mark_stack = GetThreadLocalMarkStack();
-    CHECK(tl_mark_stack == nullptr) << "mark-stack: " << tl_mark_stack;
-  }
   // Make sure we processed all deoptimization requests.
   CHECK(tlsPtr_.deoptimization_context_stack == nullptr) << "Missed deoptimization";
   CHECK(tlsPtr_.frame_id_to_shadow_frame == nullptr) <<
@@ -2716,7 +2705,6 @@
   DISALLOW_COPY_AND_ASSIGN(FetchStackTraceVisitor);
 };
 
-template<bool kTransactionActive>
 class BuildInternalStackTraceVisitor : public StackVisitor {
  public:
   BuildInternalStackTraceVisitor(Thread* self, Thread* thread, int skip_depth)
@@ -2752,7 +2740,7 @@
       self_->AssertPendingOOMException();
       return false;
     }
-    trace->Set(0, methods_and_pcs);
+    trace->Set</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(0, methods_and_pcs);
     trace_ = trace.Get();
     // If We are called from native, use non-transactional mode.
     CHECK(last_no_suspend_cause == nullptr) << last_no_suspend_cause;
@@ -2780,15 +2768,15 @@
   }
 
   void AddFrame(ArtMethod* method, uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) {
-    ObjPtr<mirror::PointerArray> trace_methods_and_pcs = GetTraceMethodsAndPCs();
-    trace_methods_and_pcs->SetElementPtrSize<kTransactionActive>(count_, method, pointer_size_);
-    trace_methods_and_pcs->SetElementPtrSize<kTransactionActive>(
-        trace_methods_and_pcs->GetLength() / 2 + count_,
-        dex_pc,
-        pointer_size_);
+    ObjPtr<mirror::PointerArray> methods_and_pcs = GetTraceMethodsAndPCs();
+    methods_and_pcs->SetElementPtrSize</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+        count_, method, pointer_size_);
+    methods_and_pcs->SetElementPtrSize</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+        methods_and_pcs->GetLength() / 2 + count_, dex_pc, pointer_size_);
     // Save the declaring class of the method to ensure that the declaring classes of the methods
     // do not get unloaded while the stack trace is live.
-    trace_->Set(count_ + 1, method->GetDeclaringClass());
+    trace_->Set</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+        count_ + 1, method->GetDeclaringClass());
     ++count_;
   }
 
@@ -2807,9 +2795,10 @@
   // Current position down stack trace.
   uint32_t count_ = 0;
   // An object array where the first element is a pointer array that contains the ArtMethod
-  // pointers on the stack and dex PCs. The rest of the elements are the declaring
-  // class of the ArtMethod pointers. trace_[i+1] contains the declaring class of the ArtMethod of
-  // the i'th frame.
+  // pointers on the stack and dex PCs. The rest of the elements are the declaring class of
+  // the ArtMethod pointers. trace_[i+1] contains the declaring class of the ArtMethod of the
+  // i'th frame. We're initializing a newly allocated trace, so we do not need to record that
+  // under a transaction. If the transaction is aborted, the whole trace shall be unreachable.
   mirror::ObjectArray<mirror::Object>* trace_ = nullptr;
   // For cross compilation.
   const PointerSize pointer_size_;
@@ -2817,7 +2806,6 @@
   DISALLOW_COPY_AND_ASSIGN(BuildInternalStackTraceVisitor);
 };
 
-template<bool kTransactionActive>
 jobject Thread::CreateInternalStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const {
   // Compute depth of stack, save frames if possible to avoid needing to recompute many.
   constexpr size_t kMaxSavedFrames = 256;
@@ -2830,9 +2818,8 @@
   const uint32_t skip_depth = count_visitor.GetSkipDepth();
 
   // Build internal stack trace.
-  BuildInternalStackTraceVisitor<kTransactionActive> build_trace_visitor(soa.Self(),
-                                                                         const_cast<Thread*>(this),
-                                                                         skip_depth);
+  BuildInternalStackTraceVisitor build_trace_visitor(
+      soa.Self(), const_cast<Thread*>(this), skip_depth);
   if (!build_trace_visitor.Init(depth)) {
     return nullptr;  // Allocation failed.
   }
@@ -2858,10 +2845,6 @@
   }
   return soa.AddLocalReference<jobject>(trace);
 }
-template jobject Thread::CreateInternalStackTrace<false>(
-    const ScopedObjectAccessAlreadyRunnable& soa) const;
-template jobject Thread::CreateInternalStackTrace<true>(
-    const ScopedObjectAccessAlreadyRunnable& soa) const;
 
 bool Thread::IsExceptionThrownByCurrentMethod(ObjPtr<mirror::Throwable> exception) const {
   // Only count the depth since we do not pass a stack frame array as an argument.
@@ -3278,10 +3261,7 @@
     if (cause.get() != nullptr) {
       exception->SetCause(DecodeJObject(cause.get())->AsThrowable());
     }
-    ScopedLocalRef<jobject> trace(GetJniEnv(),
-                                  Runtime::Current()->IsActiveTransaction()
-                                      ? CreateInternalStackTrace<true>(soa)
-                                      : CreateInternalStackTrace<false>(soa));
+    ScopedLocalRef<jobject> trace(GetJniEnv(), CreateInternalStackTrace(soa));
     if (trace.get() != nullptr) {
       exception->SetStackState(DecodeJObject(trace.get()).Ptr());
     }
diff --git a/runtime/thread.h b/runtime/thread.h
index 7129526..5742689 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -638,7 +638,6 @@
 
   // Create the internal representation of a stack trace, that is more time
   // and space efficient to compute than the StackTraceElement[].
-  template<bool kTransactionActive>
   jobject CreateInternalStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -1143,7 +1142,7 @@
     return tls32_.use_mterp.load();
   }
 
-  void ResetQuickAllocEntryPointsForThread(bool is_marking);
+  void ResetQuickAllocEntryPointsForThread();
 
   // Returns the remaining space in the TLAB.
   size_t TlabSize() const {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 740fd09..c4593f2 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -34,7 +34,7 @@
 #include "base/time_utils.h"
 #include "base/utils.h"
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "compiler_callbacks.h"
 #include "dex/class_accessor-inl.h"
 #include "dex/descriptors_names.h"
diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h
index f62e8b6..036719d 100644
--- a/runtime/verifier/reg_type_cache-inl.h
+++ b/runtime/verifier/reg_type_cache-inl.h
@@ -18,7 +18,7 @@
 #define ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
 
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/method_handle_impl.h"
 #include "mirror/method_type.h"
diff --git a/sigchainlib/Android.bp b/sigchainlib/Android.bp
index 0e25f62..6225e0c 100644
--- a/sigchainlib/Android.bp
+++ b/sigchainlib/Android.bp
@@ -18,6 +18,10 @@
     name: "libsigchain",
     defaults: ["art_defaults"],
     visibility: [
+        // Visibility for prebuilt binaries from the prebuilt of this module.
+        // TODO(b/155921753): Restrict this when prebuilts are in their proper
+        // locations.
+        "//prebuilts:__subpackages__",
         // TODO(b/142944043, b/133140750): Clean this up.
         "//frameworks/base/cmds/app_process",
     ],
diff --git a/test/044-proxy/src/OOMEOnDispatch.java b/test/044-proxy/src/OOMEOnDispatch.java
index 2ee5792..00110bc 100644
--- a/test/044-proxy/src/OOMEOnDispatch.java
+++ b/test/044-proxy/src/OOMEOnDispatch.java
@@ -26,6 +26,17 @@
 
     static ArrayList<Object> storage = new ArrayList<>(100000);
 
+    private static void exhaustJavaHeap(int size) {
+      Runtime.getRuntime().gc();
+      while (size > 0) {
+        try {
+          storage.add(new byte[size]);
+        } catch (OutOfMemoryError e) {
+          size = size/2;
+        }
+      }
+    }
+
     public static void main(String[] args) {
         InvocationHandler handler = new OOMEOnDispatch();
         OOMEInterface inf = (OOMEInterface)Proxy.newProxyInstance(
@@ -37,14 +48,17 @@
         Main.stopJit();
         Main.waitForCompilation();
 
-        int l = 1024 * 1024;
-        while (l > 8) {
-          try {
-            storage.add(new byte[l]);
-          } catch (OutOfMemoryError e) {
-            l = l/2;
-          }
-        }
+        // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
+        // OOME to prevent GC thrashing, even if later allocations may succeed.
+        Runtime.getRuntime().gc();
+        System.runFinalization();
+        // NOTE: There is a GC invocation in the exhaustJavaHeap(). So we don't need one here.
+
+        int initial_size = 1024 * 1024;
+        // Repeat to ensure there is no space left on the heap.
+        exhaustJavaHeap(initial_size);
+        exhaustJavaHeap(/*size*/ 4);
+        exhaustJavaHeap(/*size*/ 4);
 
         try {
             inf.foo();
diff --git a/test/064-field-access/src/OOMEOnNullAccess.java b/test/064-field-access/src/OOMEOnNullAccess.java
index 9343cd6..fbcb613 100644
--- a/test/064-field-access/src/OOMEOnNullAccess.java
+++ b/test/064-field-access/src/OOMEOnNullAccess.java
@@ -36,20 +36,35 @@
 
     static ArrayList<Object> storage = new ArrayList<>(100000);
 
+    private static void exhaustJavaHeap(int size) {
+      Runtime.getRuntime().gc();
+      while (size > 0) {
+        try {
+          storage.add(new byte[size]);
+        } catch (OutOfMemoryError e) {
+          size = size/2;
+        }
+      }
+    }
+
     public static void main(String[] args) {
         // Stop the JIT to be sure nothing is running that could be resolving classes or causing
         // verification.
         Main.stopJit();
         Main.waitForCompilation();
 
-        int l = 1024 * 1024;
-        while (l > 8) {
-          try {
-            storage.add(new byte[l]);
-          } catch (OutOfMemoryError e) {
-            l = l/2;
-          }
-        }
+        // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
+        // OOME to prevent GC thrashing, even if later allocations may succeed.
+        Runtime.getRuntime().gc();
+        System.runFinalization();
+        // NOTE: There is a GC invocation in the exhaustJavaHeap(). So we don't need one here.
+
+        int initial_size = 1024 * 1024;
+        // Repeat to ensure there is no space left on the heap.
+        exhaustJavaHeap(initial_size);
+        exhaustJavaHeap(/*size*/ 4);
+        exhaustJavaHeap(/*size*/ 4);
+
 
         try {
             nullAccess(null);
diff --git a/test/067-preemptive-unpark/src/Main.java b/test/067-preemptive-unpark/src/Main.java
index fa09462..475f544 100644
--- a/test/067-preemptive-unpark/src/Main.java
+++ b/test/067-preemptive-unpark/src/Main.java
@@ -30,7 +30,6 @@
 
         test.start();
         UNSAFE.unpark(test);
-        clearStack(10);
 
         System.out.println("GC'ing");
         System.gc();
@@ -83,28 +82,6 @@
         UNSAFE = (Unsafe) field.get(null);
     }
 
-    /**
-     * Scribbles on the stack to help ensure we don't have a fake
-     * pointer that would keep would-be garbage alive.
-     * TODO(hboehm): Remove this long useless cruft once we understand current failure.
-     */
-    private static void clearStack(int depth) {
-        int a = 0;
-        int b = 0;
-        int c = 0;
-        int d = 0;
-        int e = 0;
-        int f = 0;
-        int g = 0;
-        int h = 0;
-        int i = 0;
-        int j = 0;
-
-        if (depth > 0) {
-            clearStack(depth - 1);
-        }
-    }
-
     private static class ParkTester extends Thread {
         public volatile boolean parkNow = false;
         public volatile boolean success = false;
@@ -126,11 +103,13 @@
             long elapsed = System.currentTimeMillis() - start;
 
             if (elapsed > 200) {
-                System.out.println("park()ed for " + elapsed + " msec");
                 success = false;
+                System.out.println("park()ed for " + elapsed + " msec");
             } else {
-                System.out.println("park() returned quickly");
                 success = true;
+                // println is occasionally very slow.
+                // But output still appears before main thread output.
+                System.out.println("park() returned quickly");
                 finishTime = System.currentTimeMillis();
                 startTime = start;
                 elapsedTime = elapsed;
diff --git a/test/074-gc-thrash/src/Main.java b/test/074-gc-thrash/src/Main.java
index 5165df7..828184e 100644
--- a/test/074-gc-thrash/src/Main.java
+++ b/test/074-gc-thrash/src/Main.java
@@ -195,7 +195,7 @@
  * Allocates useless objects in recursive calls.
  */
 class Deep extends Thread {
-    private static final int MAX_DEPTH = 61;
+    private static final int MAX_DEPTH = 50;
 
     private static String strong[] = new String[MAX_DEPTH];
     private static WeakReference weak[] = new WeakReference[MAX_DEPTH];
diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java
index 3d5d062..cd36eff 100644
--- a/test/080-oom-throw/src/Main.java
+++ b/test/080-oom-throw/src/Main.java
@@ -53,9 +53,28 @@
         }
     }
 
+    private static int exhaustJavaHeap(Object[] data, int index, int size) {
+        Runtime.getRuntime().gc();
+        while (index != data.length && size != 0) {
+            try {
+                data[index] = new byte[size];
+                ++index;
+            } catch (OutOfMemoryError oome) {
+                size /= 2;
+            }
+        }
+        return index;
+    }
+
     public static Object eatAllMemory() {
         Object[] result = null;
         int size = 1000000;
+        // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
+        // OOME to prevent GC thrashing, even if later allocations may succeed.
+        Runtime.getRuntime().gc();
+        System.runFinalization();
+        // NOTE: There is a GC invocation in exhaustJavaHeap. So we don't need one here.
+
         while (result == null && size != 0) {
             try {
                 result = new Object[size];
@@ -65,14 +84,10 @@
         }
         if (result != null) {
             int index = 0;
-            while (index != result.length && size != 0) {
-                try {
-                    result[index] = new byte[size];
-                    ++index;
-                } catch (OutOfMemoryError oome) {
-                    size /= 2;
-                }
-            }
+            // Repeat to ensure there is no space left on the heap.
+            index = exhaustJavaHeap(result, index, size);
+            index = exhaustJavaHeap(result, index, /*size*/ 4);
+            index = exhaustJavaHeap(result, index, /*size*/ 4);
         }
         return result;
     }
diff --git a/test/174-escaping-instance-of-bad-class/expected.txt b/test/174-escaping-instance-of-bad-class/expected.txt
index 611d698..4da6a09 100644
--- a/test/174-escaping-instance-of-bad-class/expected.txt
+++ b/test/174-escaping-instance-of-bad-class/expected.txt
@@ -3,6 +3,7 @@
 Caught NoClassDefFoundError.
 Bad.bar()
 Caught NoClassDefFoundError.
+Bad.$noinline$testResolutionTrampolineCallee()
 BadSuper.foo()
 BadSuper.superInstanceValue = 1
 Caught NoClassDefFoundError.
diff --git a/test/174-escaping-instance-of-bad-class/src/Main.java b/test/174-escaping-instance-of-bad-class/src/Main.java
index 4f66a31..cee978a 100644
--- a/test/174-escaping-instance-of-bad-class/src/Main.java
+++ b/test/174-escaping-instance-of-bad-class/src/Main.java
@@ -50,6 +50,9 @@
         ncdfe.printStackTrace();
       }
     }
+
+    // Test that we handle bad instance correctly in the resolution trampoline.
+    bad.$noinline$testResolutionTrampoline();
   }
 
   public static void hierarchyTest() {
@@ -141,6 +144,15 @@
       return Bad.staticValue;
     }
   }
+
+  public void $noinline$testResolutionTrampoline() {
+    // The first call to private method uses the resolution trampoline when AOT-compiled.
+    $noinline$testResolutionTrampolineCallee();
+  }
+
+  private void $noinline$testResolutionTrampolineCallee() {
+    System.out.println("Bad.$noinline$testResolutionTrampolineCallee()");
+  }
 }
 
 class BadSuper {
diff --git a/test/179-nonvirtual-jni/expected.txt b/test/179-nonvirtual-jni/expected.txt
new file mode 100644
index 0000000..7b603b1
--- /dev/null
+++ b/test/179-nonvirtual-jni/expected.txt
@@ -0,0 +1,9 @@
+JNI_OnLoad called
+Call lookup: Base, caller: Base, Obj: Base
+Hello from Base
+Call lookup: Base, caller: Base, Obj: Ext
+Hello from Base
+Call lookup: Base, caller: Ext, Obj: Ext
+Hello from Base
+Call lookup: Ext, caller: Ext, Obj: Ext
+Hello from Ext
diff --git a/test/179-nonvirtual-jni/info.txt b/test/179-nonvirtual-jni/info.txt
new file mode 100644
index 0000000..528d3a4
--- /dev/null
+++ b/test/179-nonvirtual-jni/info.txt
@@ -0,0 +1 @@
+Ensure that calls to InvokeNonVirtual resolve correctly
diff --git a/test/179-nonvirtual-jni/nonvirtual-call.cc b/test/179-nonvirtual-jni/nonvirtual-call.cc
new file mode 100644
index 0000000..ef6c6c8a
--- /dev/null
+++ b/test/179-nonvirtual-jni/nonvirtual-call.cc
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <nativehelper/scoped_local_ref.h>
+#include <pthread.h>
+
+#include <android-base/logging.h>
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_callSayHiMethodNonvirtualWith(
+    JNIEnv* env, jclass, jclass lookup, jclass caller, jobject recv) {
+  jmethodID meth = env->GetMethodID(lookup, "sayHi", "()V");
+  env->CallNonvirtualVoidMethod(recv, caller, meth);
+}
+
+}  // namespace art
diff --git a/test/179-nonvirtual-jni/src/Main.java b/test/179-nonvirtual-jni/src/Main.java
new file mode 100644
index 0000000..e809d41
--- /dev/null
+++ b/test/179-nonvirtual-jni/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static class Base {
+    public void sayHi() {
+      System.out.println("Hello from Base");
+    }
+  }
+  public static class Ext extends Base{
+    public void sayHi() {
+      System.out.println("Hello from Ext");
+    }
+  }
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+    try {
+      System.out.println("Call lookup: Base, caller: Base, Obj: Base");
+      callSayHiMethodNonvirtualWith(Base.class, Base.class, new Base());
+    } catch (Exception e) {
+      System.out.println("Caught exception " + e);
+    }
+    try {
+      System.out.println("Call lookup: Base, caller: Base, Obj: Ext");
+      callSayHiMethodNonvirtualWith(Base.class, Base.class, new Ext());
+    } catch (Exception e) {
+      System.out.println("Caught exception " + e);
+    }
+    try {
+      System.out.println("Call lookup: Base, caller: Ext, Obj: Ext");
+      callSayHiMethodNonvirtualWith(Base.class, Ext.class, new Ext());
+    } catch (Exception e) {
+      System.out.println("Caught exception " + e);
+    }
+    try {
+      System.out.println("Call lookup: Ext, caller: Ext, Obj: Ext");
+      callSayHiMethodNonvirtualWith(Ext.class, Ext.class, new Ext());
+    } catch (Exception e) {
+      System.out.println("Caught exception " + e);
+    }
+  }
+
+  private static native void callSayHiMethodNonvirtualWith(Class<?> lookup, Class<?> caller, Object recv);
+}
diff --git a/test/180-native-default-method/build b/test/180-native-default-method/build
new file mode 100644
index 0000000..3963fd3
--- /dev/null
+++ b/test/180-native-default-method/build
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2020 The Android Open 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.
+
+# make us exit on a failure
+set -e
+
+./default-build "$@"
+
+if [[ $@ != *"--jvm"* ]]; then
+  # Change the generated dex file to have a v35 magic number if it is version 39
+  if test -f classes.dex && head -c 7 classes.dex | grep -q 039; then
+    # place ascii value '035' into the classes.dex file starting at byte 4.
+    printf '035' | dd status=none conv=notrunc of=classes.dex bs=1 seek=4 count=3
+    rm -f $TEST_NAME.jar
+    zip $TEST_NAME.jar classes.dex
+  fi
+fi
diff --git a/test/565-checker-rotate/expected.txt b/test/180-native-default-method/expected.txt
similarity index 100%
rename from test/565-checker-rotate/expected.txt
rename to test/180-native-default-method/expected.txt
diff --git a/test/180-native-default-method/info.txt b/test/180-native-default-method/info.txt
new file mode 100644
index 0000000..0cba4eb
--- /dev/null
+++ b/test/180-native-default-method/info.txt
@@ -0,0 +1,3 @@
+Regression test for DCHECK() failure for copying a default native method from
+an interface to a class implementing that interface. The default native method
+should result in ClassFormatError before we reach that DCHECK().
diff --git a/test/180-native-default-method/jasmin/TestClass.j b/test/180-native-default-method/jasmin/TestClass.j
new file mode 100644
index 0000000..fddd99b
--- /dev/null
+++ b/test/180-native-default-method/jasmin/TestClass.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2020 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class                   public TestClass
+.super                   java/lang/Object
+.implements              TestInterface
+
+.method                  public <init>()V
+   .limit stack          1
+   .limit locals         1
+   aload_0
+   invokespecial         java/lang/Object/<init>()V
+   return
+.end method
diff --git a/test/180-native-default-method/jasmin/TestInterface.j b/test/180-native-default-method/jasmin/TestInterface.j
new file mode 100644
index 0000000..080474e
--- /dev/null
+++ b/test/180-native-default-method/jasmin/TestInterface.j
@@ -0,0 +1,19 @@
+; Copyright (C) 2020 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.interface               public TestInterface
+.super                   java/lang/Object
+
+.method                  public native foo()V
+.end method
diff --git a/test/180-native-default-method/src/Main.java b/test/180-native-default-method/src/Main.java
new file mode 100644
index 0000000..4b2704b
--- /dev/null
+++ b/test/180-native-default-method/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String args[]) {
+    try {
+      // Regression test for default native methods that should cause ClassFormatException
+      // if they pass the dex file verification, i.e. for old dex file versions.
+      // We previously did not handle this case properly and failed a DCHECK() for
+      // a non-interface class creating a copied method that was native. b/157170505
+      Class.forName("TestClass");
+      throw new Error("UNREACHABLE");
+    } catch (ClassFormatError expected) {
+      System.out.println("passed");
+    } catch (Throwable unexpected) {
+      unexpected.printStackTrace();
+    }
+  }
+}
diff --git a/test/1953-pop-frame/class-loading-expected.patch b/test/1953-pop-frame/class-loading-expected.patch
index 2edef15..1a5eda7 100644
--- a/test/1953-pop-frame/class-loading-expected.patch
+++ b/test/1953-pop-frame/class-loading-expected.patch
@@ -1,4 +1,19 @@
-74a75,94
+36a37,50
+> Test stopped during notifyFramePop without exception on pop of calledFunction
+> Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+> result is StandardTestObject { cnt: 2 } base-call count: 1
+> Test stopped during notifyFramePop without exception on pop of doNothing
+> Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+> result is StandardTestObject { cnt: 1 } base-call count: 1
+> Test stopped during notifyFramePop with exception on pop of calledFunction
+> Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+> art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+> result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+> Test stopped during notifyFramePop with exception on pop of doThrow
+> Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+> art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+> result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
+60a75,94
 > Test stopped during a ClassLoad event.
 > Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
 > Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
diff --git a/test/1953-pop-frame/expected.txt b/test/1953-pop-frame/expected.txt
index 079768d..dafc6b4 100644
--- a/test/1953-pop-frame/expected.txt
+++ b/test/1953-pop-frame/expected.txt
@@ -34,20 +34,6 @@
 Test stopped during Method Exit due to exception thrown in subroutine
 Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: true } base-call-count: 0
 result is ExceptionOnceObject { cnt: 2, throwInSub: true } base-call count: 1
-Test stopped during notifyFramePop without exception on pop of calledFunction
-Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-result is StandardTestObject { cnt: 2 } base-call count: 1
-Test stopped during notifyFramePop without exception on pop of doNothing
-Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-result is StandardTestObject { cnt: 1 } base-call count: 1
-Test stopped during notifyFramePop with exception on pop of calledFunction
-Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
-art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
-result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
-Test stopped during notifyFramePop with exception on pop of doThrow
-Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
-art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
-result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
 Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
 Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
 art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
diff --git a/test/1953-pop-frame/src/art/Test1953.java b/test/1953-pop-frame/src/art/Test1953.java
index ff41d24..366809e 100644
--- a/test/1953-pop-frame/src/art/Test1953.java
+++ b/test/1953-pop-frame/src/art/Test1953.java
@@ -37,6 +37,7 @@
 import static art.SuspendEvents.setupSuspendClassEvent;
 
 public class Test1953 {
+  private static boolean IS_ART = System.getProperty("java.vm.name").equals("Dalvik");
   public final boolean canRunClassLoadTests;
   public static void doNothing() {}
 
@@ -801,29 +802,34 @@
         (thr) -> setupSuspendMethodEvent(exceptionOnceCalledMethod, /*enter*/ false, thr),
         SuspendEvents::clearSuspendMethodEvent);
 
-    System.out.println("Test stopped during notifyFramePop without exception on pop of calledFunction");
-    runTestOn(new StandardTestObject(false),
-        (thr) -> setupSuspendPopFrameEvent(1, doNothingMethod, thr),
-        SuspendEvents::clearSuspendPopFrameEvent);
-
-    System.out.println("Test stopped during notifyFramePop without exception on pop of doNothing");
-    runTestOn(new StandardTestObject(false),
-        (thr) -> setupSuspendPopFrameEvent(0, doNothingMethod, thr),
-        SuspendEvents::clearSuspendPopFrameEvent);
-
     final Method exceptionThrowCalledMethod =
         ExceptionThrowTestObject.class.getDeclaredMethod("calledFunction");
-    System.out.println("Test stopped during notifyFramePop with exception on pop of calledFunction");
-    runTestOn(new ExceptionThrowTestObject(false),
-        (thr) -> setupSuspendPopFrameEvent(0, exceptionThrowCalledMethod, thr),
-        SuspendEvents::clearSuspendPopFrameEvent);
-
     final Method exceptionCatchThrowMethod =
         ExceptionCatchTestObject.class.getDeclaredMethod("doThrow");
-    System.out.println("Test stopped during notifyFramePop with exception on pop of doThrow");
-    runTestOn(new ExceptionCatchTestObject(),
-        (thr) -> setupSuspendPopFrameEvent(0, exceptionCatchThrowMethod, thr),
-        SuspendEvents::clearSuspendPopFrameEvent);
+    // Disable more often then we technically need to in order to avoid the need
+    // for a huge number of possible results and allow the test to be easily
+    // used in CTS.
+    if (IS_ART && canRunClassLoadTests && CanRunClassLoadingTests()) {
+      System.out.println("Test stopped during notifyFramePop without exception on pop of calledFunction");
+      runTestOn(new StandardTestObject(false),
+          (thr) -> setupSuspendPopFrameEvent(1, doNothingMethod, thr),
+          SuspendEvents::clearSuspendPopFrameEvent);
+
+      System.out.println("Test stopped during notifyFramePop without exception on pop of doNothing");
+      runTestOn(new StandardTestObject(false),
+          (thr) -> setupSuspendPopFrameEvent(0, doNothingMethod, thr),
+          SuspendEvents::clearSuspendPopFrameEvent);
+
+      System.out.println("Test stopped during notifyFramePop with exception on pop of calledFunction");
+      runTestOn(new ExceptionThrowTestObject(false),
+          (thr) -> setupSuspendPopFrameEvent(0, exceptionThrowCalledMethod, thr),
+          SuspendEvents::clearSuspendPopFrameEvent);
+
+      System.out.println("Test stopped during notifyFramePop with exception on pop of doThrow");
+      runTestOn(new ExceptionCatchTestObject(),
+          (thr) -> setupSuspendPopFrameEvent(0, exceptionCatchThrowMethod, thr),
+          SuspendEvents::clearSuspendPopFrameEvent);
+    }
 
     System.out.println("Test stopped during ExceptionCatch event of calledFunction " +
         "(catch in called function, throw in called function)");
diff --git a/test/1954-pop-frame-jit/jvm-expected.patch b/test/1954-pop-frame-jit/jvm-expected.patch
index 718f8ad..6539d56 100644
--- a/test/1954-pop-frame-jit/jvm-expected.patch
+++ b/test/1954-pop-frame-jit/jvm-expected.patch
@@ -1,4 +1,19 @@
-75,94d74
+37,50d36
+< Test stopped during notifyFramePop without exception on pop of calledFunction
+< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+< result is StandardTestObject { cnt: 2 } base-call count: 1
+< Test stopped during notifyFramePop without exception on pop of doNothing
+< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+< result is StandardTestObject { cnt: 1 } base-call count: 1
+< Test stopped during notifyFramePop with exception on pop of calledFunction
+< Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+< art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+< result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+< Test stopped during notifyFramePop with exception on pop of doThrow
+< Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+< art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+< result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
+75,94d60
 < Test stopped during a ClassLoad event.
 < Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
 < Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
diff --git a/test/1955-pop-frame-jit-called/jvm-expected.patch b/test/1955-pop-frame-jit-called/jvm-expected.patch
index 718f8ad..6539d56 100644
--- a/test/1955-pop-frame-jit-called/jvm-expected.patch
+++ b/test/1955-pop-frame-jit-called/jvm-expected.patch
@@ -1,4 +1,19 @@
-75,94d74
+37,50d36
+< Test stopped during notifyFramePop without exception on pop of calledFunction
+< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+< result is StandardTestObject { cnt: 2 } base-call count: 1
+< Test stopped during notifyFramePop without exception on pop of doNothing
+< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+< result is StandardTestObject { cnt: 1 } base-call count: 1
+< Test stopped during notifyFramePop with exception on pop of calledFunction
+< Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+< art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+< result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+< Test stopped during notifyFramePop with exception on pop of doThrow
+< Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+< art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+< result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
+75,94d60
 < Test stopped during a ClassLoad event.
 < Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
 < Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
diff --git a/test/1956-pop-frame-jit-calling/jvm-expected.patch b/test/1956-pop-frame-jit-calling/jvm-expected.patch
index 718f8ad..6539d56 100644
--- a/test/1956-pop-frame-jit-calling/jvm-expected.patch
+++ b/test/1956-pop-frame-jit-calling/jvm-expected.patch
@@ -1,4 +1,19 @@
-75,94d74
+37,50d36
+< Test stopped during notifyFramePop without exception on pop of calledFunction
+< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+< result is StandardTestObject { cnt: 2 } base-call count: 1
+< Test stopped during notifyFramePop without exception on pop of doNothing
+< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+< result is StandardTestObject { cnt: 1 } base-call count: 1
+< Test stopped during notifyFramePop with exception on pop of calledFunction
+< Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+< art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+< result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+< Test stopped during notifyFramePop with exception on pop of doThrow
+< Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+< art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+< result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
+75,94d60
 < Test stopped during a ClassLoad event.
 < Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
 < Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
diff --git a/test/1985-structural-redefine-stack-scope/stack_scope.cc b/test/1985-structural-redefine-stack-scope/stack_scope.cc
index 5c5215b..02d04dd 100644
--- a/test/1985-structural-redefine-stack-scope/stack_scope.cc
+++ b/test/1985-structural-redefine-stack-scope/stack_scope.cc
@@ -21,7 +21,7 @@
 #include <vector>
 
 #include "class_linker.h"
-#include "class_root.h"
+#include "class_root-inl.h"
 #include "jni.h"
 #include "jni/jni_internal.h"
 #include "mirror/class.h"
diff --git a/test/2029-spaces-in-SimpleName/classes.dex b/test/2029-spaces-in-SimpleName/classes.dex
deleted file mode 100644
index 3804ca7..0000000
--- a/test/2029-spaces-in-SimpleName/classes.dex
+++ /dev/null
Binary files differ
diff --git a/test/709-checker-varhandles/build b/test/2033-shutdown-mechanics/check
similarity index 61%
rename from test/709-checker-varhandles/build
rename to test/2033-shutdown-mechanics/check
index 2b0b2c1..c24e09a 100755
--- a/test/709-checker-varhandles/build
+++ b/test/2033-shutdown-mechanics/check
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright 2016 The Android Open Source Project
+# Copyright (C) 2014 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # 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,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
+# The daemon thread seems to occasionally get stopped before finishing.
+# Check that the actual output is a line-by-line prefix of expected.
+head -n $(wc -l < $2) $1 | diff --strip-trailing-cr -q - "$2" >/dev/null
diff --git a/test/2033-shutdown-mechanics/native_shutdown.cc b/test/2033-shutdown-mechanics/native_shutdown.cc
index 784daa7..2b7546a 100644
--- a/test/2033-shutdown-mechanics/native_shutdown.cc
+++ b/test/2033-shutdown-mechanics/native_shutdown.cc
@@ -21,6 +21,8 @@
 
 #include "jni.h"
 
+#include <stdio.h>
+
 namespace art {
 
 static void MaybePrintTime() {
@@ -41,16 +43,19 @@
       found_shutdown = true;
       MaybePrintTime();
       printf("Saw RuntimeShutdownFunctions\n");
+      fflush(stdout);
     }
     if (!found_runtime_deleted && extEnv->IsRuntimeDeleted()) {
       found_runtime_deleted = true;
       MaybePrintTime();
       printf("Saw RuntimeDeleted\n");
+      fflush(stdout);
     }
     if (found_shutdown && found_runtime_deleted) {
       // All JNI calls should now get rerouted to SleepForever();
       (void) env->NewByteArray(17);
       printf("Unexpectedly returned from JNI call\n");
+      fflush(stdout);
       SleepForever();
     }
   }
diff --git a/test/2029-spaces-in-SimpleName/build b/test/2034-spaces-in-SimpleName/build
similarity index 68%
rename from test/2029-spaces-in-SimpleName/build
rename to test/2034-spaces-in-SimpleName/build
index 9c3cc79..940f6da 100755
--- a/test/2029-spaces-in-SimpleName/build
+++ b/test/2034-spaces-in-SimpleName/build
@@ -19,21 +19,10 @@
 
 export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-6.0.jar"
 
-cd src
-
 # generate Java bytecode with ASM
-${JAVAC:-java} -cp "$ASM_JAR:." SpacesInSimpleName.java
-${JAVA:-java} -cp "$ASM_JAR:." SpacesInSimpleName
-
-# compile Java bytecode to DEX bytecode
-# TODO: replace DX with D8 when it adds support for spaces in SimpleName
-# ${D8} --min-api 10000 Main.class
-$ANDROID_HOST_OUT/bin/dx --dex --output=classes.dex Main.class
-
-# move the resulting DEX file and cleanup
-mv classes.dex ../classes.dex
-rm *.class
-
+cd src_gen
+${JAVA:-java} -cp "$ASM_JAR:." SpacesInSimpleName.java
+mkdir ../classes && mv Main.class ../classes/Main.class
 cd ..
 
 # Use API level 10000 for spaces in SimpleName
diff --git a/test/2029-spaces-in-SimpleName/expected.txt b/test/2034-spaces-in-SimpleName/expected.txt
similarity index 100%
rename from test/2029-spaces-in-SimpleName/expected.txt
rename to test/2034-spaces-in-SimpleName/expected.txt
diff --git a/test/2029-spaces-in-SimpleName/info.txt b/test/2034-spaces-in-SimpleName/info.txt
similarity index 100%
rename from test/2029-spaces-in-SimpleName/info.txt
rename to test/2034-spaces-in-SimpleName/info.txt
diff --git a/test/2029-spaces-in-SimpleName/src/SpacesInSimpleName.java b/test/2034-spaces-in-SimpleName/src_gen/SpacesInSimpleName.java
similarity index 100%
rename from test/2029-spaces-in-SimpleName/src/SpacesInSimpleName.java
rename to test/2034-spaces-in-SimpleName/src_gen/SpacesInSimpleName.java
diff --git a/test/411-checker-hdiv-hrem-const/expected.txt b/test/411-checker-hdiv-hrem-const/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/411-checker-hdiv-hrem-const/expected.txt
diff --git a/test/411-checker-hdiv-hrem-const/info.txt b/test/411-checker-hdiv-hrem-const/info.txt
new file mode 100644
index 0000000..ce88f44
--- /dev/null
+++ b/test/411-checker-hdiv-hrem-const/info.txt
@@ -0,0 +1,2 @@
+Checker test for optimizations of integer division and remainder instructions when
+the denominator is a constant, not power of 2.
diff --git a/test/411-checker-hdiv-hrem-const/src/DivTest.java b/test/411-checker-hdiv-hrem-const/src/DivTest.java
new file mode 100644
index 0000000..ed4eb62
--- /dev/null
+++ b/test/411-checker-hdiv-hrem-const/src/DivTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2020 The Android Open 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 DivTest {
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void expectEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void main() {
+    divInt();
+    divLong();
+  }
+
+  private static void divInt() {
+    expectEquals(0, $noinline$IntDivBy18(0));
+    expectEquals(0, $noinline$IntDivBy18(1));
+    expectEquals(0, $noinline$IntDivBy18(-1));
+    expectEquals(1, $noinline$IntDivBy18(18));
+    expectEquals(-1, $noinline$IntDivBy18(-18));
+    expectEquals(3, $noinline$IntDivBy18(65));
+    expectEquals(-3, $noinline$IntDivBy18(-65));
+
+    expectEquals(0, $noinline$IntDivByMinus18(0));
+    expectEquals(0, $noinline$IntDivByMinus18(1));
+    expectEquals(0, $noinline$IntDivByMinus18(-1));
+    expectEquals(-1, $noinline$IntDivByMinus18(18));
+    expectEquals(1, $noinline$IntDivByMinus18(-18));
+    expectEquals(-3, $noinline$IntDivByMinus18(65));
+    expectEquals(3, $noinline$IntDivByMinus18(-65));
+
+    expectEquals(0, $noinline$IntDivBy7(0));
+    expectEquals(0, $noinline$IntDivBy7(1));
+    expectEquals(0, $noinline$IntDivBy7(-1));
+    expectEquals(1, $noinline$IntDivBy7(7));
+    expectEquals(-1, $noinline$IntDivBy7(-7));
+    expectEquals(3, $noinline$IntDivBy7(22));
+    expectEquals(-3, $noinline$IntDivBy7(-22));
+
+    expectEquals(0, $noinline$IntDivByMinus7(0));
+    expectEquals(0, $noinline$IntDivByMinus7(1));
+    expectEquals(0, $noinline$IntDivByMinus7(-1));
+    expectEquals(-1, $noinline$IntDivByMinus7(7));
+    expectEquals(1, $noinline$IntDivByMinus7(-7));
+    expectEquals(-3, $noinline$IntDivByMinus7(22));
+    expectEquals(3, $noinline$IntDivByMinus7(-22));
+
+    expectEquals(0, $noinline$IntDivBy6(0));
+    expectEquals(0, $noinline$IntDivBy6(1));
+    expectEquals(0, $noinline$IntDivBy6(-1));
+    expectEquals(1, $noinline$IntDivBy6(6));
+    expectEquals(-1, $noinline$IntDivBy6(-6));
+    expectEquals(3, $noinline$IntDivBy6(19));
+    expectEquals(-3, $noinline$IntDivBy6(-19));
+
+    expectEquals(0, $noinline$IntDivByMinus6(0));
+    expectEquals(0, $noinline$IntDivByMinus6(1));
+    expectEquals(0, $noinline$IntDivByMinus6(-1));
+    expectEquals(-1, $noinline$IntDivByMinus6(6));
+    expectEquals(1, $noinline$IntDivByMinus6(-6));
+    expectEquals(-3, $noinline$IntDivByMinus6(19));
+    expectEquals(3, $noinline$IntDivByMinus6(-19));
+  }
+
+  // A test case to check that 'lsr' and 'asr' are combined into one 'asr'.
+  // For divisor 18 seen in an MP3 decoding workload there is no need
+  // to correct the result of get_high(dividend * magic). So there are no
+  // instructions between 'lsr' and 'asr'. In such a case they can be combined
+  // into one 'asr'.
+  //
+  /// CHECK-START-ARM64: int DivTest.$noinline$IntDivBy18(int) disassembly (after)
+  /// CHECK:                 asr x{{\d+}}, x{{\d+}}, #34
+  /// CHECK-NEXT:            add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
+  private static int $noinline$IntDivBy18(int v) {
+    int r = v / 18;
+    return r;
+  }
+
+  // A test case to check that 'lsr' and 'asr' are combined into one 'asr'.
+  // Divisor -18 has the same property as divisor 18: no need to correct the
+  // result of get_high(dividend * magic). So there are no
+  // instructions between 'lsr' and 'asr'. In such a case they can be combined
+  // into one 'asr'.
+  //
+  /// CHECK-START-ARM64: int DivTest.$noinline$IntDivByMinus18(int) disassembly (after)
+  /// CHECK:                 asr x{{\d+}}, x{{\d+}}, #34
+  /// CHECK-NEXT:            add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
+  private static int $noinline$IntDivByMinus18(int v) {
+    int r = v / -18;
+    return r;
+  }
+
+  // A test case to check that 'lsr' and 'add' are combined into one 'adds'.
+  // For divisor 7 seen in the core library the result of get_high(dividend * magic)
+  // must be corrected by the 'add' instruction.
+  //
+  // The test case also checks 'add' and 'add_shift' are optimized into 'adds' and 'cinc'.
+  //
+  /// CHECK-START-ARM64: int DivTest.$noinline$IntDivBy7(int) disassembly (after)
+  /// CHECK:                 adds x{{\d+}}, x{{\d+}}, x{{\d+}}, lsl #32
+  /// CHECK-NEXT:            asr  x{{\d+}}, x{{\d+}}, #34
+  /// CHECK-NEXT:            cinc w{{\d+}}, w{{\d+}}, mi
+  private static int $noinline$IntDivBy7(int v) {
+    int r = v / 7;
+    return r;
+  }
+
+  // A test case to check that 'lsr' and 'add' are combined into one 'adds'.
+  // Divisor -7 has the same property as divisor 7: the result of get_high(dividend * magic)
+  // must be corrected. In this case it is a 'sub' instruction.
+  //
+  // The test case also checks 'sub' and 'add_shift' are optimized into 'subs' and 'cinc'.
+  //
+  /// CHECK-START-ARM64: int DivTest.$noinline$IntDivByMinus7(int) disassembly (after)
+  /// CHECK:                 subs x{{\d+}}, x{{\d+}}, x{{\d+}}, lsl #32
+  /// CHECK-NEXT:            asr  x{{\d+}}, x{{\d+}}, #34
+  /// CHECK-NEXT:            cinc w{{\d+}}, w{{\d+}}, mi
+  private static int $noinline$IntDivByMinus7(int v) {
+    int r = v / -7;
+    return r;
+  }
+
+  // A test case to check that 'asr' is used to get the high 32 bits of the result of
+  // 'dividend * magic'.
+  // For divisor 6 seen in the core library there is no need to correct the result of
+  // get_high(dividend * magic). Also there is no 'asr' before the final 'add' instruction
+  // which uses only the high 32 bits of the result. In such a case 'asr' getting the high
+  // 32 bits can be used as well.
+  //
+  /// CHECK-START-ARM64: int DivTest.$noinline$IntDivBy6(int) disassembly (after)
+  /// CHECK:                 asr x{{\d+}}, x{{\d+}}, #32
+  /// CHECK-NEXT:            add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
+  private static int $noinline$IntDivBy6(int v) {
+    int r = v / 6;
+    return r;
+  }
+
+  // A test case to check that 'asr' is used to get the high 32 bits of the result of
+  // 'dividend * magic'.
+  // Divisor -6 has the same property as divisor 6: no need to correct the result of
+  // get_high(dividend * magic) and no 'asr' before the final 'add' instruction
+  // which uses only the high 32 bits of the result. In such a case 'asr' getting the high
+  // 32 bits can be used as well.
+  //
+  /// CHECK-START-ARM64: int DivTest.$noinline$IntDivByMinus6(int) disassembly (after)
+  /// CHECK:                 asr x{{\d+}}, x{{\d+}}, #32
+  /// CHECK-NEXT:            add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
+  private static int $noinline$IntDivByMinus6(int v) {
+    int r = v / -6;
+    return r;
+  }
+
+  private static void divLong() {
+    expectEquals(0L, $noinline$LongDivBy18(0L));
+    expectEquals(0L, $noinline$LongDivBy18(1L));
+    expectEquals(0L, $noinline$LongDivBy18(-1L));
+    expectEquals(1L, $noinline$LongDivBy18(18L));
+    expectEquals(-1L, $noinline$LongDivBy18(-18L));
+    expectEquals(3L, $noinline$LongDivBy18(65L));
+    expectEquals(-3L, $noinline$LongDivBy18(-65L));
+
+    expectEquals(0L, $noinline$LongDivByMinus18(0L));
+    expectEquals(0L, $noinline$LongDivByMinus18(1L));
+    expectEquals(0L, $noinline$LongDivByMinus18(-1L));
+    expectEquals(-1L, $noinline$LongDivByMinus18(18L));
+    expectEquals(1L, $noinline$LongDivByMinus18(-18L));
+    expectEquals(-3L, $noinline$LongDivByMinus18(65L));
+    expectEquals(3L, $noinline$LongDivByMinus18(-65L));
+
+    expectEquals(0L, $noinline$LongDivBy7(0L));
+    expectEquals(0L, $noinline$LongDivBy7(1L));
+    expectEquals(0L, $noinline$LongDivBy7(-1L));
+    expectEquals(1L, $noinline$LongDivBy7(7L));
+    expectEquals(-1L, $noinline$LongDivBy7(-7L));
+    expectEquals(3L, $noinline$LongDivBy7(22L));
+    expectEquals(-3L, $noinline$LongDivBy7(-22L));
+
+    expectEquals(0L, $noinline$LongDivByMinus7(0L));
+    expectEquals(0L, $noinline$LongDivByMinus7(1L));
+    expectEquals(0L, $noinline$LongDivByMinus7(-1L));
+    expectEquals(-1L, $noinline$LongDivByMinus7(7L));
+    expectEquals(1L, $noinline$LongDivByMinus7(-7L));
+    expectEquals(-3L, $noinline$LongDivByMinus7(22L));
+    expectEquals(3L, $noinline$LongDivByMinus7(-22L));
+
+    expectEquals(0L, $noinline$LongDivBy6(0L));
+    expectEquals(0L, $noinline$LongDivBy6(1L));
+    expectEquals(0L, $noinline$LongDivBy6(-1L));
+    expectEquals(1L, $noinline$LongDivBy6(6L));
+    expectEquals(-1L, $noinline$LongDivBy6(-6L));
+    expectEquals(3L, $noinline$LongDivBy6(19L));
+    expectEquals(-3L, $noinline$LongDivBy6(-19L));
+
+    expectEquals(0L, $noinline$LongDivByMinus6(0L));
+    expectEquals(0L, $noinline$LongDivByMinus6(1L));
+    expectEquals(0L, $noinline$LongDivByMinus6(-1L));
+    expectEquals(-1L, $noinline$LongDivByMinus6(6L));
+    expectEquals(1L, $noinline$LongDivByMinus6(-6L));
+    expectEquals(-3L, $noinline$LongDivByMinus6(19L));
+    expectEquals(3L, $noinline$LongDivByMinus6(-19L));
+
+    expectEquals(0L, $noinline$LongDivBy100(0L));
+    expectEquals(0L, $noinline$LongDivBy100(1L));
+    expectEquals(0L, $noinline$LongDivBy100(-1L));
+    expectEquals(1L, $noinline$LongDivBy100(100L));
+    expectEquals(-1L, $noinline$LongDivBy100(-100L));
+    expectEquals(3L, $noinline$LongDivBy100(301L));
+    expectEquals(-3L, $noinline$LongDivBy100(-301L));
+
+    expectEquals(0L, $noinline$LongDivByMinus100(0L));
+    expectEquals(0L, $noinline$LongDivByMinus100(1L));
+    expectEquals(0L, $noinline$LongDivByMinus100(-1L));
+    expectEquals(-1L, $noinline$LongDivByMinus100(100L));
+    expectEquals(1L, $noinline$LongDivByMinus100(-100L));
+    expectEquals(-3L, $noinline$LongDivByMinus100(301L));
+    expectEquals(3L, $noinline$LongDivByMinus100(-301L));
+  }
+
+  // Test cases for Int64 HDiv/HRem to check that optimizations implemented for Int32 are not
+  // used for Int64. The same divisors 18, -18, 7, -7, 6 and -6 are used.
+
+  /// CHECK-START-ARM64: long DivTest.$noinline$LongDivBy18(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  private static long $noinline$LongDivBy18(long v) {
+    long r = v / 18L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long DivTest.$noinline$LongDivByMinus18(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  private static long $noinline$LongDivByMinus18(long v) {
+    long r = v / -18L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long DivTest.$noinline$LongDivBy7(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            asr x{{\d+}}, x{{\d+}}, #1
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  private static long $noinline$LongDivBy7(long v) {
+    long r = v / 7L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long DivTest.$noinline$LongDivByMinus7(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            asr x{{\d+}}, x{{\d+}}, #1
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  private static long $noinline$LongDivByMinus7(long v) {
+    long r = v / -7L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long DivTest.$noinline$LongDivBy6(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  private static long $noinline$LongDivBy6(long v) {
+    long r = v / 6L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long DivTest.$noinline$LongDivByMinus6(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  private static long $noinline$LongDivByMinus6(long v) {
+    long r = v / -6L;
+    return r;
+  }
+
+  // A test to check 'add' and 'add_shift' are optimized into 'adds' and 'cinc'.
+  //
+  /// CHECK-START-ARM64: long DivTest.$noinline$LongDivBy100(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            adds  x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            asr   x{{\d+}}, x{{\d+}}, #6
+  /// CHECK-NEXT:            cinc  x{{\d+}}, x{{\d+}}, mi
+  private static long $noinline$LongDivBy100(long v) {
+    long r = v / 100L;
+    return r;
+  }
+
+  // A test to check 'subs' and 'add_shift' are optimized into 'subs' and 'cinc'.
+  //
+  /// CHECK-START-ARM64: long DivTest.$noinline$LongDivByMinus100(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            subs  x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            asr   x{{\d+}}, x{{\d+}}, #6
+  /// CHECK-NEXT:            cinc  x{{\d+}}, x{{\d+}}, mi
+  private static long $noinline$LongDivByMinus100(long v) {
+    long r = v / -100L;
+    return r;
+  }
+}
diff --git a/test/566-checker-signum/src/Main.java b/test/411-checker-hdiv-hrem-const/src/Main.java
similarity index 80%
rename from test/566-checker-signum/src/Main.java
rename to test/411-checker-hdiv-hrem-const/src/Main.java
index fa8e5cd..4b34bf1 100644
--- a/test/566-checker-signum/src/Main.java
+++ b/test/411-checker-hdiv-hrem-const/src/Main.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-// This file is just for running on the RI as the test is ART specific.
 public class Main {
-  public static void main(String[] args) {
-    System.out.println("passed");
-  }
+    public static void main(String args[]) {
+        DivTest.main();
+        RemTest.main();
+    }
 }
diff --git a/test/411-checker-hdiv-hrem-const/src/RemTest.java b/test/411-checker-hdiv-hrem-const/src/RemTest.java
new file mode 100644
index 0000000..2fae275
--- /dev/null
+++ b/test/411-checker-hdiv-hrem-const/src/RemTest.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2020 The Android Open 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 RemTest {
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void expectEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void main() {
+    remInt();
+    remLong();
+  }
+
+  private static void remInt() {
+    expectEquals(0, $noinline$IntRemBy18(0));
+    expectEquals(1, $noinline$IntRemBy18(1));
+    expectEquals(-1, $noinline$IntRemBy18(-1));
+    expectEquals(0, $noinline$IntRemBy18(18));
+    expectEquals(0, $noinline$IntRemBy18(-18));
+    expectEquals(11, $noinline$IntRemBy18(65));
+    expectEquals(-11, $noinline$IntRemBy18(-65));
+
+    expectEquals(0, $noinline$IntRemByMinus18(0));
+    expectEquals(1, $noinline$IntRemByMinus18(1));
+    expectEquals(-1, $noinline$IntRemByMinus18(-1));
+    expectEquals(0, $noinline$IntRemByMinus18(18));
+    expectEquals(0, $noinline$IntRemByMinus18(-18));
+    expectEquals(11, $noinline$IntRemByMinus18(65));
+    expectEquals(-11, $noinline$IntRemByMinus18(-65));
+
+    expectEquals(0, $noinline$IntRemBy7(0));
+    expectEquals(1, $noinline$IntRemBy7(1));
+    expectEquals(-1, $noinline$IntRemBy7(-1));
+    expectEquals(0, $noinline$IntRemBy7(7));
+    expectEquals(0, $noinline$IntRemBy7(-7));
+    expectEquals(1, $noinline$IntRemBy7(22));
+    expectEquals(-1, $noinline$IntRemBy7(-22));
+
+    expectEquals(0, $noinline$IntRemByMinus7(0));
+    expectEquals(1, $noinline$IntRemByMinus7(1));
+    expectEquals(-1, $noinline$IntRemByMinus7(-1));
+    expectEquals(0, $noinline$IntRemByMinus7(7));
+    expectEquals(0, $noinline$IntRemByMinus7(-7));
+    expectEquals(1, $noinline$IntRemByMinus7(22));
+    expectEquals(-1, $noinline$IntRemByMinus7(-22));
+
+    expectEquals(0, $noinline$IntRemBy6(0));
+    expectEquals(1, $noinline$IntRemBy6(1));
+    expectEquals(-1, $noinline$IntRemBy6(-1));
+    expectEquals(0, $noinline$IntRemBy6(6));
+    expectEquals(0, $noinline$IntRemBy6(-6));
+    expectEquals(1, $noinline$IntRemBy6(19));
+    expectEquals(-1, $noinline$IntRemBy6(-19));
+
+    expectEquals(0, $noinline$IntRemByMinus6(0));
+    expectEquals(1, $noinline$IntRemByMinus6(1));
+    expectEquals(-1, $noinline$IntRemByMinus6(-1));
+    expectEquals(0, $noinline$IntRemByMinus6(6));
+    expectEquals(0, $noinline$IntRemByMinus6(-6));
+    expectEquals(1, $noinline$IntRemByMinus6(19));
+    expectEquals(-1, $noinline$IntRemByMinus6(-19));
+  }
+
+  // A test case to check that 'lsr' and 'asr' are combined into one 'asr'.
+  // For divisor 18 seen in an MP3 decoding workload there is no need
+  // to correct the result of get_high(dividend * magic). So there are no
+  // instructions between 'lsr' and 'asr'. In such a case they can be combined
+  // into one 'asr'.
+  //
+  /// CHECK-START-ARM64: int RemTest.$noinline$IntRemBy18(int) disassembly (after)
+  /// CHECK:                 asr x{{\d+}}, x{{\d+}}, #34
+  /// CHECK-NEXT:            add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
+  /// CHECK-NEXT:            mov w{{\d+}}, #0x12
+  /// CHECK-NEXT:            msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}}
+  private static int $noinline$IntRemBy18(int v) {
+    int r = v % 18;
+    return r;
+  }
+
+  // A test case to check that 'lsr' and 'asr' are combined into one 'asr'.
+  // Divisor -18 has the same property as divisor 18: no need to correct the
+  // result of get_high(dividend * magic). So there are no
+  // instructions between 'lsr' and 'asr'. In such a case they can be combined
+  // into one 'asr'.
+  //
+  /// CHECK-START-ARM64: int RemTest.$noinline$IntRemByMinus18(int) disassembly (after)
+  /// CHECK:                 asr x{{\d+}}, x{{\d+}}, #34
+  /// CHECK-NEXT:            add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
+  /// CHECK-NEXT:            mov w{{\d+}}, #0xffffffee
+  /// CHECK-NEXT:            msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}}
+  private static int $noinline$IntRemByMinus18(int v) {
+    int r = v % -18;
+    return r;
+  }
+
+  // A test case to check that 'lsr' and 'add' are combined into one 'adds'.
+  // For divisor 7 seen in the core library the result of get_high(dividend * magic)
+  // must be corrected by the 'add' instruction.
+  //
+  // The test case also checks 'add' and 'add_shift' are optimized into 'adds' and 'cinc'.
+  //
+  /// CHECK-START-ARM64: int RemTest.$noinline$IntRemBy7(int) disassembly (after)
+  /// CHECK:                 adds x{{\d+}}, x{{\d+}}, x{{\d+}}, lsl #32
+  /// CHECK-NEXT:            asr  x{{\d+}}, x{{\d+}}, #34
+  /// CHECK-NEXT:            cinc w{{\d+}}, w{{\d+}}, mi
+  /// CHECK-NEXT:            mov w{{\d+}}, #0x7
+  /// CHECK-NEXT:            msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}}
+  private static int $noinline$IntRemBy7(int v) {
+    int r = v % 7;
+    return r;
+  }
+
+  // A test case to check that 'lsr' and 'add' are combined into one 'adds'.
+  // Divisor -7 has the same property as divisor 7: the result of get_high(dividend * magic)
+  // must be corrected. In this case it is a 'sub' instruction.
+  //
+  // The test case also checks 'sub' and 'add_shift' are optimized into 'subs' and 'cinc'.
+  //
+  /// CHECK-START-ARM64: int RemTest.$noinline$IntRemByMinus7(int) disassembly (after)
+  /// CHECK:                 subs x{{\d+}}, x{{\d+}}, x{{\d+}}, lsl #32
+  /// CHECK-NEXT:            asr  x{{\d+}}, x{{\d+}}, #34
+  /// CHECK-NEXT:            cinc w{{\d+}}, w{{\d+}}, mi
+  /// CHECK-NEXT:            mov w{{\d+}}, #0xfffffff9
+  /// CHECK-NEXT:            msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}}
+  private static int $noinline$IntRemByMinus7(int v) {
+    int r = v % -7;
+    return r;
+  }
+
+  // A test case to check that 'asr' is used to get the high 32 bits of the result of
+  // 'dividend * magic'.
+  // For divisor 6 seen in the core library there is no need to correct the result of
+  // get_high(dividend * magic). Also there is no 'asr' before the final 'add' instruction
+  // which uses only the high 32 bits of the result. In such a case 'asr' getting the high
+  // 32 bits can be used as well.
+  //
+  /// CHECK-START-ARM64: int RemTest.$noinline$IntRemBy6(int) disassembly (after)
+  /// CHECK:                 asr x{{\d+}}, x{{\d+}}, #32
+  /// CHECK-NEXT:            add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
+  /// CHECK-NEXT:            mov w{{\d+}}, #0x6
+  /// CHECK-NEXT:            msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}}
+  private static int $noinline$IntRemBy6(int v) {
+    int r = v % 6;
+    return r;
+  }
+
+  // A test case to check that 'asr' is used to get the high 32 bits of the result of
+  // 'dividend * magic'.
+  // Divisor -6 has the same property as divisor 6: no need to correct the result of
+  // get_high(dividend * magic) and no 'asr' before the final 'add' instruction
+  // which uses only the high 32 bits of the result. In such a case 'asr' getting the high
+  // 32 bits can be used as well.
+  //
+  /// CHECK-START-ARM64: int RemTest.$noinline$IntRemByMinus6(int) disassembly (after)
+  /// CHECK:                 asr x{{\d+}}, x{{\d+}}, #32
+  /// CHECK-NEXT:            add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
+  /// CHECK-NEXT:            mov w{{\d+}}, #0xfffffffa
+  /// CHECK-NEXT:            msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}}
+  private static int $noinline$IntRemByMinus6(int v) {
+    int r = v % -6;
+    return r;
+  }
+
+  private static void remLong() {
+    expectEquals(0L, $noinline$LongRemBy18(0L));
+    expectEquals(1L, $noinline$LongRemBy18(1L));
+    expectEquals(-1L, $noinline$LongRemBy18(-1L));
+    expectEquals(0L, $noinline$LongRemBy18(18L));
+    expectEquals(0L, $noinline$LongRemBy18(-18L));
+    expectEquals(11L, $noinline$LongRemBy18(65L));
+    expectEquals(-11L, $noinline$LongRemBy18(-65L));
+
+    expectEquals(0L, $noinline$LongRemByMinus18(0L));
+    expectEquals(1L, $noinline$LongRemByMinus18(1L));
+    expectEquals(-1L, $noinline$LongRemByMinus18(-1L));
+    expectEquals(0L, $noinline$LongRemByMinus18(18L));
+    expectEquals(0L, $noinline$LongRemByMinus18(-18L));
+    expectEquals(11L, $noinline$LongRemByMinus18(65L));
+    expectEquals(-11L, $noinline$LongRemByMinus18(-65L));
+
+    expectEquals(0L, $noinline$LongRemBy7(0L));
+    expectEquals(1L, $noinline$LongRemBy7(1L));
+    expectEquals(-1L, $noinline$LongRemBy7(-1L));
+    expectEquals(0L, $noinline$LongRemBy7(7L));
+    expectEquals(0L, $noinline$LongRemBy7(-7L));
+    expectEquals(1L, $noinline$LongRemBy7(22L));
+    expectEquals(-1L, $noinline$LongRemBy7(-22L));
+
+    expectEquals(0L, $noinline$LongRemByMinus7(0L));
+    expectEquals(1L, $noinline$LongRemByMinus7(1L));
+    expectEquals(-1L, $noinline$LongRemByMinus7(-1L));
+    expectEquals(0L, $noinline$LongRemByMinus7(7L));
+    expectEquals(0L, $noinline$LongRemByMinus7(-7L));
+    expectEquals(1L, $noinline$LongRemByMinus7(22L));
+    expectEquals(-1L, $noinline$LongRemByMinus7(-22L));
+
+    expectEquals(0L, $noinline$LongRemBy6(0L));
+    expectEquals(1L, $noinline$LongRemBy6(1L));
+    expectEquals(-1L, $noinline$LongRemBy6(-1L));
+    expectEquals(0L, $noinline$LongRemBy6(6L));
+    expectEquals(0L, $noinline$LongRemBy6(-6L));
+    expectEquals(1L, $noinline$LongRemBy6(19L));
+    expectEquals(-1L, $noinline$LongRemBy6(-19L));
+
+    expectEquals(0L, $noinline$LongRemByMinus6(0L));
+    expectEquals(1L, $noinline$LongRemByMinus6(1L));
+    expectEquals(-1L, $noinline$LongRemByMinus6(-1L));
+    expectEquals(0L, $noinline$LongRemByMinus6(6L));
+    expectEquals(0L, $noinline$LongRemByMinus6(-6L));
+    expectEquals(1L, $noinline$LongRemByMinus6(19L));
+    expectEquals(-1L, $noinline$LongRemByMinus6(-19L));
+
+    expectEquals(0L, $noinline$LongRemBy100(0L));
+    expectEquals(1L, $noinline$LongRemBy100(1L));
+    expectEquals(-1L, $noinline$LongRemBy100(-1L));
+    expectEquals(0L, $noinline$LongRemBy100(100L));
+    expectEquals(0L, $noinline$LongRemBy100(-100L));
+    expectEquals(1L, $noinline$LongRemBy100(101L));
+    expectEquals(-1L, $noinline$LongRemBy100(-101L));
+
+    expectEquals(0L, $noinline$LongRemByMinus100(0L));
+    expectEquals(1L, $noinline$LongRemByMinus100(1L));
+    expectEquals(-1L, $noinline$LongRemByMinus100(-1L));
+    expectEquals(0L, $noinline$LongRemByMinus100(100L));
+    expectEquals(0L, $noinline$LongRemByMinus100(-100L));
+    expectEquals(1L, $noinline$LongRemByMinus100(101L));
+    expectEquals(-1L, $noinline$LongRemByMinus100(-101L));
+  }
+
+  // Test cases for Int64 HDiv/HRem to check that optimizations implemented for Int32 are not
+  // used for Int64. The same divisors 18, -18, 7, -7, 6 and -6 are used.
+
+  /// CHECK-START-ARM64: long RemTest.$noinline$LongRemBy18(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  /// CHECK-NEXT:            mov x{{\d+}}, #0x12
+  /// CHECK-NEXT:            msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+  private static long $noinline$LongRemBy18(long v) {
+    long r = v % 18L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long RemTest.$noinline$LongRemByMinus18(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  /// CHECK-NEXT:            mov x{{\d+}}, #0xffffffffffffffee
+  /// CHECK-NEXT:            msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+  private static long $noinline$LongRemByMinus18(long v) {
+    long r = v % -18L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long RemTest.$noinline$LongRemBy7(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            asr x{{\d+}}, x{{\d+}}, #1
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  /// CHECK-NEXT:            mov x{{\d+}}, #0x7
+  /// CHECK-NEXT:            msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+  private static long $noinline$LongRemBy7(long v) {
+    long r = v % 7L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long RemTest.$noinline$LongRemByMinus7(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            asr x{{\d+}}, x{{\d+}}, #1
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  /// CHECK-NEXT:            mov x{{\d+}}, #0xfffffffffffffff9
+  /// CHECK-NEXT:            msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+  private static long $noinline$LongRemByMinus7(long v) {
+    long r = v % -7L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long RemTest.$noinline$LongRemBy6(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  /// CHECK-NEXT:            mov x{{\d+}}, #0x6
+  /// CHECK-NEXT:            msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+  private static long $noinline$LongRemBy6(long v) {
+    long r = v % 6L;
+    return r;
+  }
+
+  /// CHECK-START-ARM64: long RemTest.$noinline$LongRemByMinus6(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
+  /// CHECK-NEXT:            mov x{{\d+}}, #0xfffffffffffffffa
+  /// CHECK-NEXT:            msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+  private static long $noinline$LongRemByMinus6(long v) {
+    long r = v % -6L;
+    return r;
+  }
+
+  // A test to check 'add' and 'add_shift' are optimized into 'adds' and 'cinc'.
+  //
+  /// CHECK-START-ARM64: long RemTest.$noinline$LongRemBy100(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            adds  x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            asr   x{{\d+}}, x{{\d+}}, #6
+  /// CHECK-NEXT:            cinc  x{{\d+}}, x{{\d+}}, mi
+  /// CHECK-NEXT:            mov x{{\d+}}, #0x64
+  /// CHECK-NEXT:            msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+  private static long $noinline$LongRemBy100(long v) {
+    long r = v % 100L;
+    return r;
+  }
+
+  // A test to check 'sub' and 'add_shift' are optimized into 'subs' and 'cinc'.
+  //
+  /// CHECK-START-ARM64: long RemTest.$noinline$LongRemByMinus100(long) disassembly (after)
+  /// CHECK:                 smulh x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            subs  x{{\d+}}, x{{\d+}}, x{{\d+}}
+  /// CHECK-NEXT:            asr   x{{\d+}}, x{{\d+}}, #6
+  /// CHECK-NEXT:            cinc  x{{\d+}}, x{{\d+}}, mi
+  /// CHECK-NEXT:            mov x{{\d+}}, #0xffffffffffffff9c
+  /// CHECK-NEXT:            msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+  private static long $noinline$LongRemByMinus100(long v) {
+    long r = v % -100L;
+    return r;
+  }
+}
diff --git a/test/557-checker-instruct-simplifier-ror/src/Main.java b/test/557-checker-instruct-simplifier-ror/src/Main.java
index 1c52f07..5d4bb7a 100644
--- a/test/557-checker-instruct-simplifier-ror/src/Main.java
+++ b/test/557-checker-instruct-simplifier-ror/src/Main.java
@@ -28,64 +28,6 @@
     }
   }
 
-  /// CHECK-START: int Main.rotateIntegerRight(int, int) builder (after)
-  /// CHECK:          <<ArgValue:i\d+>>     ParameterValue
-  /// CHECK:          <<ArgDistance:i\d+>>  ParameterValue
-  /// CHECK:          <<Ror:i\d+>>          Ror [<<ArgValue>>,<<ArgDistance>>]
-  /// CHECK:                                Return [<<Ror>>]
-
-  /// CHECK-START: int Main.rotateIntegerRight(int, int) builder (after)
-  /// CHECK-NOT:      LoadClass
-  /// CHECK-NOT:      ClinitCheck
-  /// CHECK-NOT:      InvokeStaticOrDirect
-  public static int rotateIntegerRight(int value, int distance) {
-    return Integer.rotateRight(value, distance);
-  }
-
-  /// CHECK-START: int Main.rotateIntegerLeft(int, int) builder (after)
-  /// CHECK:          <<ArgValue:i\d+>>     ParameterValue
-  /// CHECK:          <<ArgDistance:i\d+>>  ParameterValue
-  /// CHECK:          <<Neg:i\d+>>          Neg [<<ArgDistance>>]
-  /// CHECK:          <<Ror:i\d+>>          Ror [<<ArgValue>>,<<Neg>>]
-  /// CHECK:                                Return [<<Ror>>]
-
-  /// CHECK-START: int Main.rotateIntegerLeft(int, int) builder (after)
-  /// CHECK-NOT:      LoadClass
-  /// CHECK-NOT:      ClinitCheck
-  /// CHECK-NOT:      InvokeStaticOrDirect
-  public static int rotateIntegerLeft(int value, int distance) {
-    return Integer.rotateLeft(value, distance);
-  }
-
-  /// CHECK-START: long Main.rotateLongRight(long, int) builder (after)
-  /// CHECK:          <<ArgValue:j\d+>>     ParameterValue
-  /// CHECK:          <<ArgDistance:i\d+>>  ParameterValue
-  /// CHECK:          <<Ror:j\d+>>          Ror [<<ArgValue>>,<<ArgDistance>>]
-  /// CHECK:                                Return [<<Ror>>]
-
-  /// CHECK-START: long Main.rotateLongRight(long, int) builder (after)
-  /// CHECK-NOT:      LoadClass
-  /// CHECK-NOT:      ClinitCheck
-  /// CHECK-NOT:      InvokeStaticOrDirect
-  public static long rotateLongRight(long value, int distance) {
-    return Long.rotateRight(value, distance);
-  }
-
-  /// CHECK-START: long Main.rotateLongLeft(long, int) builder (after)
-  /// CHECK:          <<ArgValue:j\d+>>     ParameterValue
-  /// CHECK:          <<ArgDistance:i\d+>>  ParameterValue
-  /// CHECK:          <<Neg:i\d+>>          Neg [<<ArgDistance>>]
-  /// CHECK:          <<Ror:j\d+>>          Ror [<<ArgValue>>,<<Neg>>]
-  /// CHECK:                                Return [<<Ror>>]
-
-  /// CHECK-START: long Main.rotateLongLeft(long, int) builder (after)
-  /// CHECK-NOT:      LoadClass
-  /// CHECK-NOT:      ClinitCheck
-  /// CHECK-NOT:      InvokeStaticOrDirect
-  public static long rotateLongLeft(long value, int distance) {
-    return Long.rotateLeft(value, distance);
-  }
-
   //  (i >>> #distance) | (i << #(reg_bits - distance))
 
   /// CHECK-START: int Main.ror_int_constant_c_c(int) instruction_simplifier (before)
diff --git a/test/565-checker-rotate/info.txt b/test/565-checker-rotate/info.txt
deleted file mode 100644
index c6a8091..0000000
--- a/test/565-checker-rotate/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Unit test for 32-bit and 64-bit rotate operations.
diff --git a/test/566-checker-signum/expected.txt b/test/566-checker-signum/expected.txt
deleted file mode 100644
index b0aad4d..0000000
--- a/test/566-checker-signum/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/566-checker-signum/info.txt b/test/566-checker-signum/info.txt
deleted file mode 100644
index 328e494..0000000
--- a/test/566-checker-signum/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Unit test for 32-bit and 64-bit signum operations.
diff --git a/test/567-checker-builder-intrinsics/expected.txt b/test/567-checker-builder-intrinsics/expected.txt
new file mode 100644
index 0000000..9554e0d
--- /dev/null
+++ b/test/567-checker-builder-intrinsics/expected.txt
@@ -0,0 +1,8 @@
+TestRotate passed
+TestCompare passed
+TestSignum passed
+TestIsNan passed
+TestVarHandles passed
+TestMinMax passed
+TestAbs passed
+TestFpAbs passed
diff --git a/test/567-checker-builder-intrinsics/info.txt b/test/567-checker-builder-intrinsics/info.txt
new file mode 100644
index 0000000..7ad7aa8
--- /dev/null
+++ b/test/567-checker-builder-intrinsics/info.txt
@@ -0,0 +1,2 @@
+Tests for creating intermediate representation in InstructionBuilder for simple
+intrinsics. It also tests some simplifications for these instructions.
diff --git a/test/565-checker-rotate/src/Main.java b/test/567-checker-builder-intrinsics/src/Main.java
similarity index 72%
rename from test/565-checker-rotate/src/Main.java
rename to test/567-checker-builder-intrinsics/src/Main.java
index 79b8555..3320fb7 100644
--- a/test/565-checker-rotate/src/Main.java
+++ b/test/567-checker-builder-intrinsics/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,9 +14,15 @@
  * limitations under the License.
  */
 
-// This file is just for running on the RI as the test is ART specific.
 public class Main {
   public static void main(String args[]) {
-    System.out.println("passed");
+    TestRotate.main();
+    TestCompare.main();
+    TestSignum.main();
+    TestIsNan.main();
+    TestVarHandles.main();
+    TestMinMax.main();
+    TestAbs.main();
+    TestFpAbs.main();
   }
-}
\ No newline at end of file
+}
diff --git a/test/681-checker-abs/src/Main.java b/test/567-checker-builder-intrinsics/src/TestAbs.java
similarity index 73%
rename from test/681-checker-abs/src/Main.java
rename to test/567-checker-builder-intrinsics/src/TestAbs.java
index 7c68e17..defc988 100644
--- a/test/681-checker-abs/src/Main.java
+++ b/test/567-checker-builder-intrinsics/src/TestAbs.java
@@ -17,29 +17,29 @@
 /**
  * Functional tests for detecting abs.
  */
-public class Main {
+public class TestAbs {
 
   //
   // Intrinsics.
   //
 
-  /// CHECK-START: int Main.absI(int) builder (after)
+  /// CHECK-START: int TestAbs.absI(int) builder (after)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.absI(int) builder (after)
+  /// CHECK-START: int TestAbs.absI(int) builder (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
   public static int absI(int a) {
     return Math.abs(a);
   }
 
-  /// CHECK-START: long Main.absL(long) builder (after)
+  /// CHECK-START: long TestAbs.absL(long) builder (after)
   /// CHECK-DAG: <<Par:j\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:j\d+>> Abs [<<Par>>]
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: long Main.absL(long) builder (after)
+  /// CHECK-START: long TestAbs.absL(long) builder (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
   public static long absL(long a) {
     return Math.abs(a);
@@ -49,7 +49,7 @@
   // Types.
   //
 
-  /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_gvn (before)
+  /// CHECK-START: int TestAbs.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>>]
@@ -57,18 +57,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Neg>>,<<Par>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (before)
+  /// CHECK-START: int TestAbs.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>>]
@@ -76,18 +76,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Neg>>,<<Par>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (before)
+  /// CHECK-START: int TestAbs.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>>]
@@ -95,18 +95,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (before)
+  /// CHECK-START: int TestAbs.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>>]
@@ -114,18 +114,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (before)
+  /// CHECK-START: int TestAbs.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>>]
@@ -133,18 +133,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (before)
+  /// CHECK-START: int TestAbs.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>>]
@@ -152,18 +152,18 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (before)
+  /// CHECK-START: long TestAbs.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>>]
@@ -171,12 +171,12 @@
   /// CHECK-DAG: <<Sel:j\d+>> Select [<<Par>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: long TestAbs.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_gvn (after)
+  /// CHECK-START: long TestAbs.abs7(long) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static long abs7(long a) {
     return a >= 0 ? a : -a;
@@ -186,7 +186,7 @@
   // Complications.
   //
 
-  /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_gvn (before)
+  /// CHECK-START: int TestAbs.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>>]
@@ -194,12 +194,12 @@
   /// CHECK-DAG: <<Sel:i\d+>> Select [<<Arr>>,<<Neg>>,<<Cnd>>]
   /// CHECK-DAG:              Return [<<Sel>>]
   //
-  /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestAbs.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_gvn (after)
+  /// CHECK-START: int TestAbs.abs0(int[]) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static int abs0(int[] a) {
     return a[0] >= 0 ? a[0] : -a[0];
@@ -209,58 +209,58 @@
   // Nop zero extension.
   //
 
-  /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (before)
+  /// CHECK-START: int TestAbs.zabs1(byte) instruction_simplifier (before)
   /// CHECK-DAG: <<Par:b\d+>> ParameterValue
   /// CHECK-DAG: <<Msk:i\d+>> IntConstant 255
   /// CHECK-DAG: <<And:i\d+>> [<<Par>>,<<Msk>>]
   /// CHECK-DAG: <<Abs:i\d+>> Abs
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after)
+  /// CHECK-START: int TestAbs.zabs1(byte) instruction_simplifier (after)
   /// CHECK-DAG: <<Par:b\d+>> ParameterValue
   /// CHECK-DAG: <<Cnv:a\d+>> TypeConversion [<<Par>>]
   /// CHECK-DAG:              Return [<<Cnv>>]
   //
-  /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after)
+  /// CHECK-START: int TestAbs.zabs1(byte) instruction_simplifier (after)
   /// CHECK-NOT:              Abs
   public static int zabs1(byte a) {
     return Math.abs(a & 0xff);
   }
 
-  /// CHECK-START: int Main.zabs2(short) instruction_simplifier (before)
+  /// CHECK-START: int TestAbs.zabs2(short) instruction_simplifier (before)
   /// CHECK-DAG: <<Par:s\d+>> ParameterValue
   /// CHECK-DAG: <<Msk:i\d+>> IntConstant 65535
   /// CHECK-DAG: <<And:i\d+>> [<<Msk>>,<<Par>>]
   /// CHECK-DAG: <<Abs:i\d+>> Abs
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after)
+  /// CHECK-START: int TestAbs.zabs2(short) instruction_simplifier (after)
   /// CHECK-DAG: <<Par:s\d+>> ParameterValue
   /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Par>>]
   /// CHECK-DAG:              Return [<<Cnv>>]
   //
-  /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after)
+  /// CHECK-START: int TestAbs.zabs2(short) instruction_simplifier (after)
   /// CHECK-NOT:              Abs
   public static int zabs2(short a) {
     return Math.abs(a & 0xffff);
   }
 
-  /// CHECK-START: int Main.zabs3(char) instruction_simplifier (before)
+  /// CHECK-START: int TestAbs.zabs3(char) instruction_simplifier (before)
   /// CHECK-DAG: <<Par:c\d+>> ParameterValue
   /// CHECK-DAG: <<Abs:i\d+>> Abs
   /// CHECK-DAG:              Return [<<Abs>>]
   //
-  /// CHECK-START: int Main.zabs3(char) instruction_simplifier (after)
+  /// CHECK-START: int TestAbs.zabs3(char) instruction_simplifier (after)
   /// CHECK-DAG: <<Par:c\d+>> ParameterValue
   /// CHECK-DAG:              Return [<<Par>>]
   //
-  /// CHECK-START: int Main.zabs3(char) instruction_simplifier (after)
+  /// CHECK-START: int TestAbs.zabs3(char) instruction_simplifier (after)
   /// CHECK-NOT:              Abs
   public static int zabs3(char a) {
     return Math.abs(a);
   }
 
-  public static void main(String[] args) {
+  public static void main() {
     // Intrinsics.
     expectEquals(10, absI(-10));
     expectEquals(20, absI(20));
@@ -293,7 +293,7 @@
     expectEquals(0xffff, zabs2((short) -1));
     expectEquals(1, zabs3((char) 1));
     expectEquals(0xffff, zabs3((char) -1));
-    System.out.println("passed");
+    System.out.println("TestAbs passed");
   }
 
   private static void expectEquals(int expected, int result) {
diff --git a/test/567-checker-compare/src/Main.java b/test/567-checker-builder-intrinsics/src/TestCompare.java
similarity index 87%
rename from test/567-checker-compare/src/Main.java
rename to test/567-checker-builder-intrinsics/src/TestCompare.java
index fe1691d..185616a 100644
--- a/test/567-checker-compare/src/Main.java
+++ b/test/567-checker-builder-intrinsics/src/TestCompare.java
@@ -14,46 +14,46 @@
  * limitations under the License.
  */
 
-public class Main {
+public class TestCompare {
 
-  /// CHECK-START: void Main.$opt$noinline$testReplaceInputWithItself(int) builder (after)
+  /// CHECK-START: void TestCompare.$opt$noinline$testReplaceInputWithItself(int) builder (after)
   /// CHECK-DAG:     <<ArgX:i\d+>>   ParameterValue
   /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
   /// CHECK-DAG:     <<Cmp:i\d+>>    Compare [<<ArgX>>,<<Zero>>]
   /// CHECK-DAG:                     GreaterThanOrEqual [<<Cmp>>,<<Zero>>]
 
-  /// CHECK-START: void Main.$opt$noinline$testReplaceInputWithItself(int) instruction_simplifier (after)
+  /// CHECK-START: void TestCompare.$opt$noinline$testReplaceInputWithItself(int) instruction_simplifier (after)
   /// CHECK-DAG:     <<ArgX:i\d+>>   ParameterValue
   /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
   /// CHECK-DAG:                     GreaterThanOrEqual [<<ArgX>>,<<Zero>>]
 
   public static void $opt$noinline$testReplaceInputWithItself(int x) {
-    // The instruction simplifier first replaces Integer.compare(x, 0) with Compare HIR
-    // and then merges the Compare into the GreaterThanOrEqual. This is a regression
-    // test that to check that it is allowed to replace the second input of the
-    // GreaterThanOrEqual, i.e. <<Zero>>, with the very same instruction.
+    // The instruction builder first replaces Integer.compare(x, 0) with Compare HIR
+    // and then the instruction simplifier merges the Compare into the GreaterThanOrEqual.
+    // This is a regression test to check that it is allowed to replace the second
+    // input of the GreaterThanOrEqual, i.e. <<Zero>>, with the very same instruction.
     if (Integer.compare(x, 0) < 0) {
       System.out.println("OOOPS");
     }
   }
 
-  /// CHECK-START: int Main.compareBooleans(boolean, boolean) select_generator (after)
+  /// CHECK-START: int TestCompare.compareBooleans(boolean, boolean) select_generator (after)
   /// CHECK-NOT:                     Phi
 
-  /// CHECK-START: int Main.compareBooleans(boolean, boolean) instruction_simplifier$after_bce (after)
+  /// CHECK-START: int TestCompare.compareBooleans(boolean, boolean) instruction_simplifier$after_bce (after)
   /// CHECK:         <<ArgX:z\d+>>   ParameterValue
   /// CHECK:         <<ArgY:z\d+>>   ParameterValue
   /// CHECK-DAG:     <<Result:i\d+>> Compare [<<ArgX>>,<<ArgY>>]
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareBooleans(boolean, boolean) instruction_simplifier$after_bce (after)
+  /// CHECK-START: int TestCompare.compareBooleans(boolean, boolean) instruction_simplifier$after_bce (after)
   /// CHECK-NOT:                     Select
 
   private static int compareBooleans(boolean x, boolean y) {
     return Integer.compare((x ? 1 : 0), (y ? 1 : 0));
   }
 
-  ///  CHECK-START: int Main.compareBooleans2(boolean, boolean) builder (after)
+  ///  CHECK-START: int TestCompare.compareBooleans2(boolean, boolean) builder (after)
   ///  CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
   ///  CHECK-DAG:     <<One:i\d+>>    IntConstant 1
   ///  CHECK-DAG:     <<PhiX:i\d+>>   Phi [<<One>>,<<Zero>>]
@@ -61,10 +61,10 @@
   ///  CHECK-DAG:     <<Result:i\d+>> Compare [<<PhiX>>,<<PhiY>>]
   ///  CHECK-DAG:                     Return [<<Result>>]
 
-  ///  CHECK-START: int Main.compareBooleans2(boolean, boolean) builder (after)
+  ///  CHECK-START: int TestCompare.compareBooleans2(boolean, boolean) builder (after)
   ///  CHECK-NOT:                     InvokeStaticOrDirect
 
-  ///  CHECK-START: int Main.compareBooleans2(boolean, boolean) select_generator (after)
+  ///  CHECK-START: int TestCompare.compareBooleans2(boolean, boolean) select_generator (after)
   ///  CHECK:         <<ArgX:z\d+>>   ParameterValue
   ///  CHECK:         <<ArgY:z\d+>>   ParameterValue
   ///  CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
@@ -74,16 +74,16 @@
   ///  CHECK-DAG:     <<Result:i\d+>> Compare [<<SelX>>,<<SelY>>]
   ///  CHECK-DAG:                     Return [<<Result>>]
 
-  ///  CHECK-START: int Main.compareBooleans2(boolean, boolean) select_generator (after)
+  ///  CHECK-START: int TestCompare.compareBooleans2(boolean, boolean) select_generator (after)
   ///  CHECK-NOT:                     Phi
 
-  ///  CHECK-START: int Main.compareBooleans2(boolean, boolean) instruction_simplifier$after_bce (after)
+  ///  CHECK-START: int TestCompare.compareBooleans2(boolean, boolean) instruction_simplifier$after_bce (after)
   ///  CHECK:         <<ArgX:z\d+>>   ParameterValue
   ///  CHECK:         <<ArgY:z\d+>>   ParameterValue
   ///  CHECK-DAG:     <<Result:i\d+>> Compare [<<ArgX>>,<<ArgY>>]
   ///  CHECK-DAG:                     Return [<<Result>>]
 
-  ///  CHECK-START: int Main.compareBooleans2(boolean, boolean) instruction_simplifier$after_bce (after)
+  ///  CHECK-START: int TestCompare.compareBooleans2(boolean, boolean) instruction_simplifier$after_bce (after)
   ///  CHECK-NOT:                     Select
 
   private static int compareBooleans2(boolean x, boolean y) {
@@ -104,55 +104,55 @@
     return Integer.compare(src_x, src_y);
   }
 
-  /// CHECK-START: int Main.compareBytes(byte, byte) builder (after)
+  /// CHECK-START: int TestCompare.compareBytes(byte, byte) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareBytes(byte, byte) builder (after)
+  /// CHECK-START: int TestCompare.compareBytes(byte, byte) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int compareBytes(byte x, byte y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareShorts(short, short) builder (after)
+  /// CHECK-START: int TestCompare.compareShorts(short, short) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareShorts(short, short) builder (after)
+  /// CHECK-START: int TestCompare.compareShorts(short, short) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int compareShorts(short x, short y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareChars(char, char) builder (after)
+  /// CHECK-START: int TestCompare.compareChars(char, char) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareChars(char, char) builder (after)
+  /// CHECK-START: int TestCompare.compareChars(char, char) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int compareChars(char x, char y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareInts(int, int) builder (after)
+  /// CHECK-START: int TestCompare.compareInts(int, int) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareInts(int, int) builder (after)
+  /// CHECK-START: int TestCompare.compareInts(int, int) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int compareInts(int x, int y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareLongs(long, long) builder (after)
+  /// CHECK-START: int TestCompare.compareLongs(long, long) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareLongs(long, long) builder (after)
+  /// CHECK-START: int TestCompare.compareLongs(long, long) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int compareLongs(long x, long y) {
@@ -160,33 +160,33 @@
   }
 
 
-  /// CHECK-START: int Main.compareByteShort(byte, short) builder (after)
+  /// CHECK-START: int TestCompare.compareByteShort(byte, short) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareByteShort(byte, short) builder (after)
+  /// CHECK-START: int TestCompare.compareByteShort(byte, short) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareByteShort(byte x, short y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareByteChar(byte, char) builder (after)
+  /// CHECK-START: int TestCompare.compareByteChar(byte, char) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareByteChar(byte, char) builder (after)
+  /// CHECK-START: int TestCompare.compareByteChar(byte, char) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareByteChar(byte x, char y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareByteInt(byte, int) builder (after)
+  /// CHECK-START: int TestCompare.compareByteInt(byte, int) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareByteInt(byte, int) builder (after)
+  /// CHECK-START: int TestCompare.compareByteInt(byte, int) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareByteInt(byte x, int y) {
@@ -194,33 +194,33 @@
   }
 
 
-  /// CHECK-START: int Main.compareShortByte(short, byte) builder (after)
+  /// CHECK-START: int TestCompare.compareShortByte(short, byte) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareShortByte(short, byte) builder (after)
+  /// CHECK-START: int TestCompare.compareShortByte(short, byte) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareShortByte(short x, byte y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareShortChar(short, char) builder (after)
+  /// CHECK-START: int TestCompare.compareShortChar(short, char) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareShortChar(short, char) builder (after)
+  /// CHECK-START: int TestCompare.compareShortChar(short, char) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareShortChar(short x, char y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareShortInt(short, int) builder (after)
+  /// CHECK-START: int TestCompare.compareShortInt(short, int) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareShortInt(short, int) builder (after)
+  /// CHECK-START: int TestCompare.compareShortInt(short, int) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareShortInt(short x, int y) {
@@ -228,33 +228,33 @@
   }
 
 
-  /// CHECK-START: int Main.compareCharByte(char, byte) builder (after)
+  /// CHECK-START: int TestCompare.compareCharByte(char, byte) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareCharByte(char, byte) builder (after)
+  /// CHECK-START: int TestCompare.compareCharByte(char, byte) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareCharByte(char x, byte y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareCharShort(char, short) builder (after)
+  /// CHECK-START: int TestCompare.compareCharShort(char, short) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareCharShort(char, short) builder (after)
+  /// CHECK-START: int TestCompare.compareCharShort(char, short) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareCharShort(char x, short y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareCharInt(char, int) builder (after)
+  /// CHECK-START: int TestCompare.compareCharInt(char, int) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareCharInt(char, int) builder (after)
+  /// CHECK-START: int TestCompare.compareCharInt(char, int) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareCharInt(char x, int y) {
@@ -262,33 +262,33 @@
   }
 
 
-  /// CHECK-START: int Main.compareIntByte(int, byte) builder (after)
+  /// CHECK-START: int TestCompare.compareIntByte(int, byte) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareIntByte(int, byte) builder (after)
+  /// CHECK-START: int TestCompare.compareIntByte(int, byte) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareIntByte(int x, byte y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareIntShort(int, short) builder (after)
+  /// CHECK-START: int TestCompare.compareIntShort(int, short) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareIntShort(int, short) builder (after)
+  /// CHECK-START: int TestCompare.compareIntShort(int, short) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareIntShort(int x, short y) {
     return Integer.compare(x, y);
   }
 
-  /// CHECK-START: int Main.compareIntChar(int, char) builder (after)
+  /// CHECK-START: int TestCompare.compareIntChar(int, char) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.compareIntChar(int, char) builder (after)
+  /// CHECK-START: int TestCompare.compareIntChar(int, char) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int compareIntChar(int x, char y) {
@@ -296,7 +296,7 @@
   }
 
 
-  public static void testCompareBooleans() throws Exception {
+  public static void testCompareBooleans() {
     expectEquals(-1, compareBooleans(false, true));
     expectEquals(-1, compareBooleans2(false, true));
 
@@ -867,7 +867,7 @@
   }
 
 
-  public static void main(String args[]) throws Exception {
+  public static void main() {
     $opt$noinline$testReplaceInputWithItself(42);
 
     testCompareBooleans();
@@ -893,7 +893,7 @@
     testCompareIntShort();
     testCompareIntChar();
 
-    System.out.println("passed");
+    System.out.println("TestCompare passed");
   }
 
   private static void expectEquals(int expected, int result) {
diff --git a/test/631-checker-fp-abs/src/Main.java b/test/567-checker-builder-intrinsics/src/TestFpAbs.java
similarity index 95%
rename from test/631-checker-fp-abs/src/Main.java
rename to test/567-checker-builder-intrinsics/src/TestFpAbs.java
index 6e0e6db..e6c338d 100644
--- a/test/631-checker-fp-abs/src/Main.java
+++ b/test/567-checker-builder-intrinsics/src/TestFpAbs.java
@@ -21,7 +21,7 @@
  * we require that Math.abs() clears the sign bit (but changes nothing else)
  * for all numbers, including NaN (signaling NaN may become quiet though).
  */
-public class Main {
+public class TestFpAbs {
 
   private final static boolean isDalvik =
       System.getProperty("java.vm.name").equals("Dalvik");
@@ -31,21 +31,21 @@
 
   public static boolean doThrow = false;
 
-  /// CHECK-START: float Main.$opt$noinline$absSP(float) builder (after)
+  /// CHECK-START: float TestFpAbs.$opt$noinline$absSP(float) builder (after)
   /// CHECK-DAG: <<Result:f\d+>> Abs
   /// CHECK-DAG:                 Return [<<Result>>]
   private static float $opt$noinline$absSP(float f) {
     return Math.abs(f);
   }
 
-  /// CHECK-START: double Main.$opt$noinline$absDP(double) builder (after)
+  /// CHECK-START: double TestFpAbs.$opt$noinline$absDP(double) builder (after)
   /// CHECK-DAG: <<Result:d\d+>> Abs
   /// CHECK-DAG:                 Return [<<Result>>]
   private static double $opt$noinline$absDP(double d) {
     return Math.abs(d);
   }
 
-  public static void main(String args[]) {
+  public static void main() {
     // A few obvious numbers.
     for (float f = -100.0f; f < 0.0f; f += 0.5f) {
       expectEqualsSP(-f, $opt$noinline$absSP(f));
@@ -129,7 +129,7 @@
           Double.doubleToRawLongBits($opt$noinline$absDP(d)));
     }
 
-    System.out.println("passed");
+    System.out.println("TestFpAbs passed");
   }
 
   private static void expectEquals32(int expected, int result) {
diff --git a/test/575-checker-isnan/src/Main.java b/test/567-checker-builder-intrinsics/src/TestIsNan.java
similarity index 90%
rename from test/575-checker-isnan/src/Main.java
rename to test/567-checker-builder-intrinsics/src/TestIsNan.java
index 4773f63..03b76d7 100644
--- a/test/575-checker-isnan/src/Main.java
+++ b/test/567-checker-builder-intrinsics/src/TestIsNan.java
@@ -14,29 +14,29 @@
  * limitations under the License.
  */
 
-public class Main {
+public class TestIsNan {
 
-  /// CHECK-START: boolean Main.isNaN32(float) builder (after)
+  /// CHECK-START: boolean TestIsNan.isNaN32(float) builder (after)
   /// CHECK-DAG: <<Result:z\d+>> NotEqual
   /// CHECK-DAG: Return [<<Result>>]
   //
-  /// CHECK-START: boolean Main.isNaN32(float) builder (after)
+  /// CHECK-START: boolean TestIsNan.isNaN32(float) builder (after)
   /// CHECK-NOT: InvokeStaticOrDirect
   private static boolean isNaN32(float x) {
     return Float.isNaN(x);
   }
 
-  /// CHECK-START: boolean Main.isNaN64(double) builder (after)
+  /// CHECK-START: boolean TestIsNan.isNaN64(double) builder (after)
   /// CHECK-DAG: <<Result:z\d+>> NotEqual
   /// CHECK-DAG: Return [<<Result>>]
   //
-  /// CHECK-START: boolean Main.isNaN64(double) builder (after)
+  /// CHECK-START: boolean TestIsNan.isNaN64(double) builder (after)
   /// CHECK-NOT: InvokeStaticOrDirect
   private static boolean isNaN64(double x) {
     return Double.isNaN(x);
   }
 
-  public static void main(String args[]) {
+  public static void main() {
     // A few distinct numbers.
     expectFalse(isNaN32(Float.NEGATIVE_INFINITY));
     expectFalse(isNaN32(-1.0f));
@@ -101,7 +101,7 @@
       expectTrue(isNaN64(dvals[i]));
     }
 
-    System.out.println("passed");
+    System.out.println("TestIsNan passed");
   }
 
   private static void expectTrue(boolean value) {
diff --git a/test/679-checker-minmax/src/Main.java b/test/567-checker-builder-intrinsics/src/TestMinMax.java
similarity index 76%
rename from test/679-checker-minmax/src/Main.java
rename to test/567-checker-builder-intrinsics/src/TestMinMax.java
index 0e13264..0e88517 100644
--- a/test/679-checker-minmax/src/Main.java
+++ b/test/567-checker-builder-intrinsics/src/TestMinMax.java
@@ -17,22 +17,22 @@
 /**
  * Functional tests for detecting min/max.
  */
-public class Main {
+public class TestMinMax {
 
   //
   // Direct intrinsics.
   //
 
-  /// CHECK-START: int Main.minI(int) builder (after)
+  /// CHECK-START: int TestMinMax.minI(int) builder (after)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
   /// CHECK-DAG: <<Min:i\d+>> Min [<<Par>>,<<Con>>]
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: int Main.minI(int) builder (after)
+  /// CHECK-START: int TestMinMax.minI(int) builder (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
   //
-  /// CHECK-START-ARM64: int Main.minI(int) disassembly (after)
+  /// CHECK-START-ARM64: int TestMinMax.minI(int) disassembly (after)
   /// CHECK-NOT:              mov {{w\d+}}, #0x14
   /// CHECK:                  cmp {{w\d+}}, #0x14
   //  Check that the constant generation was handled by VIXL.
@@ -42,16 +42,16 @@
     return Math.min(a, 20);
   }
 
-  /// CHECK-START: long Main.minL(long) builder (after)
+  /// CHECK-START: long TestMinMax.minL(long) builder (after)
   /// CHECK-DAG: <<Par:j\d+>> ParameterValue
   /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
   /// CHECK-DAG: <<Min:j\d+>> Min [<<Par>>,<<Con>>]
   /// CHECK-DAG:              Return [<<Min>>]
   //
-  /// CHECK-START: long Main.minL(long) builder (after)
+  /// CHECK-START: long TestMinMax.minL(long) builder (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
   //
-  /// CHECK-START-ARM64: long Main.minL(long) disassembly (after)
+  /// CHECK-START-ARM64: long TestMinMax.minL(long) disassembly (after)
   /// CHECK-NOT:              mov {{x\d+}}, #0x14
   /// CHECK:                  cmp {{x\d+}}, #0x14
   //  Check that the constant generation was handled by VIXL.
@@ -61,16 +61,16 @@
     return Math.min(a, 20L);
   }
 
-  /// CHECK-START: int Main.maxI(int) builder (after)
+  /// CHECK-START: int TestMinMax.maxI(int) builder (after)
   /// CHECK-DAG: <<Par:i\d+>> ParameterValue
   /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
   /// CHECK-DAG: <<Max:i\d+>> Max [<<Par>>,<<Con>>]
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: int Main.maxI(int) builder (after)
+  /// CHECK-START: int TestMinMax.maxI(int) builder (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
   //
-  /// CHECK-START-ARM64: int Main.maxI(int) disassembly (after)
+  /// CHECK-START-ARM64: int TestMinMax.maxI(int) disassembly (after)
   /// CHECK-NOT:              mov {{w\d+}}, #0x14
   /// CHECK:                  cmp {{w\d+}}, #0x14
   //  Check that the constant generation was handled by VIXL.
@@ -80,16 +80,16 @@
     return Math.max(a, 20);
   }
 
-  /// CHECK-START: long Main.maxL(long) builder (after)
+  /// CHECK-START: long TestMinMax.maxL(long) builder (after)
   /// CHECK-DAG: <<Par:j\d+>> ParameterValue
   /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
   /// CHECK-DAG: <<Max:j\d+>> Max [<<Par>>,<<Con>>]
   /// CHECK-DAG:              Return [<<Max>>]
   //
-  /// CHECK-START: long Main.maxL(long) builder (after)
+  /// CHECK-START: long TestMinMax.maxL(long) builder (after)
   /// CHECK-NOT:              InvokeStaticOrDirect
   //
-  /// CHECK-START-ARM64: long Main.maxL(long) disassembly (after)
+  /// CHECK-START-ARM64: long TestMinMax.maxL(long) disassembly (after)
   /// CHECK-NOT:              mov {{x\d+}}, #0x14
   /// CHECK:                  cmp {{x\d+}}, #0x14
   //  Check that the constant generation was handled by VIXL.
@@ -103,7 +103,7 @@
   // Special Cases
   //
 
-  /// CHECK-START-ARM64: int Main.minIntConstantZero(int) disassembly (after)
+  /// CHECK-START-ARM64: int TestMinMax.minIntConstantZero(int) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{w\d+}}, #0x0
   /// CHECK:            cmp {{w\d+}}, #0x0 (0)
@@ -113,7 +113,7 @@
     return Math.min(a, 0);
   }
 
-  /// CHECK-START-ARM64: int Main.minIntConstantOne(int) disassembly (after)
+  /// CHECK-START-ARM64: int TestMinMax.minIntConstantOne(int) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{w\d+}}, #0x1
   /// CHECK:            cmp {{w\d+}}, #0x1 (1)
@@ -123,7 +123,7 @@
     return Math.min(a, 1);
   }
 
-  /// CHECK-START-ARM64: int Main.minIntConstantMinusOne(int) disassembly (after)
+  /// CHECK-START-ARM64: int TestMinMax.minIntConstantMinusOne(int) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{w\d+}}, #0xffffffff
   /// CHECK:            cmn {{w\d+}}, #0x1 (1)
@@ -133,7 +133,7 @@
     return Math.min(a, -1);
   }
 
-  /// CHECK-START-ARM64: long Main.minLongConstantZero(long) disassembly (after)
+  /// CHECK-START-ARM64: long TestMinMax.minLongConstantZero(long) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{x\d+}}, #0x0
   /// CHECK:            cmp {{x\d+}}, #0x0 (0)
@@ -143,7 +143,7 @@
     return Math.min(a, 0L);
   }
 
-  /// CHECK-START-ARM64: long Main.minLongConstantOne(long) disassembly (after)
+  /// CHECK-START-ARM64: long TestMinMax.minLongConstantOne(long) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{x\d+}}, #0x1
   /// CHECK:            cmp {{x\d+}}, #0x1 (1)
@@ -153,7 +153,7 @@
     return Math.min(a, 1L);
   }
 
-  /// CHECK-START-ARM64: long Main.minLongConstantMinusOne(long) disassembly (after)
+  /// CHECK-START-ARM64: long TestMinMax.minLongConstantMinusOne(long) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{x\d+}}, #0xffffffffffffffff
   /// CHECK:            cmn {{x\d+}}, #0x1 (1)
@@ -163,7 +163,7 @@
     return Math.min(a, -1L);
   }
 
-  /// CHECK-START-ARM64: int Main.maxIntConstantZero(int) disassembly (after)
+  /// CHECK-START-ARM64: int TestMinMax.maxIntConstantZero(int) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{w\d+}}, #0x0
   /// CHECK:            cmp {{w\d+}}, #0x0 (0)
@@ -173,7 +173,7 @@
     return Math.max(a, 0);
   }
 
-  /// CHECK-START-ARM64: int Main.maxIntConstantOne(int) disassembly (after)
+  /// CHECK-START-ARM64: int TestMinMax.maxIntConstantOne(int) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{w\d+}}, #0x1
   /// CHECK:            cmp {{w\d+}}, #0x1 (1)
@@ -183,7 +183,7 @@
     return Math.max(a, 1);
   }
 
-  /// CHECK-START-ARM64: int Main.maxIntConstantMinusOne(int) disassembly (after)
+  /// CHECK-START-ARM64: int TestMinMax.maxIntConstantMinusOne(int) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{w\d+}}, #0xffffffff
   /// CHECK:            cmn {{w\d+}}, #0x1 (1)
@@ -193,7 +193,7 @@
     return Math.max(a, -1);
   }
 
-  /// CHECK-START-ARM64: int Main.maxIntLargeConstant(int) disassembly (after)
+  /// CHECK-START-ARM64: int TestMinMax.maxIntLargeConstant(int) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK:            mov {{w\d+}}, #0x2001
   /// CHECK:            cmp {{w\d+}}, {{w\d+}}
@@ -205,7 +205,7 @@
     return Math.max(a, 8193);
   }
 
-  /// CHECK-START-ARM64: long Main.maxLongConstantZero(long) disassembly (after)
+  /// CHECK-START-ARM64: long TestMinMax.maxLongConstantZero(long) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{x\d+}}, #0x0
   /// CHECK:            cmp {{x\d+}}, #0x0 (0)
@@ -215,7 +215,7 @@
     return Math.max(a, 0L);
   }
 
-  /// CHECK-START-ARM64: long Main.maxLongConstantOne(long) disassembly (after)
+  /// CHECK-START-ARM64: long TestMinMax.maxLongConstantOne(long) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{x\d+}}, #0x1
   /// CHECK:            cmp {{x\d+}}, #0x1 (1)
@@ -225,7 +225,7 @@
     return Math.max(a, 1L);
   }
 
-  /// CHECK-START-ARM64: long Main.maxLongConstantMinusOne(long) disassembly (after)
+  /// CHECK-START-ARM64: long TestMinMax.maxLongConstantMinusOne(long) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK-NOT:        mov {{x\d+}}, #0xffffffffffffffff
   /// CHECK:            cmn {{x\d+}}, #0x1 (1)
@@ -235,7 +235,7 @@
     return Math.max(a, -1L);
   }
 
-  /// CHECK-START-ARM64: long Main.maxLongLargeConstant(long) disassembly (after)
+  /// CHECK-START-ARM64: long TestMinMax.maxLongLargeConstant(long) disassembly (after)
   /// CHECK-NOT:        InvokeStaticOrDirect
   /// CHECK:            mov {{x\d+}}, #0x2001
   /// CHECK:            cmp {{x\d+}}, {{x\d+}}
@@ -251,211 +251,211 @@
   // Different types.
   //
 
-  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: long TestMinMax.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_gvn (after)
+  /// CHECK-START: long TestMinMax.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_gvn (after)
+  /// CHECK-START: long TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (before)
+  /// CHECK-START: long TestMinMax.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_gvn (after)
+  /// CHECK-START: long TestMinMax.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_gvn (after)
+  /// CHECK-START: long TestMinMax.max7(long, long) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:              Select
   public static long max7(long a, long b) {
     return a >= b ? a : b;
@@ -465,18 +465,18 @@
   // Complications.
   //
 
-  /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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
@@ -484,18 +484,18 @@
     return a[0] <= b[0] ? a[0] : b[0];
   }
 
-  /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_gvn (before)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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
@@ -503,7 +503,7 @@
     return a[0] >= b[0] ? a[0] : b[0];
   }
 
-  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_gvn (before)
+  /// CHECK-START: int TestMinMax.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
@@ -513,7 +513,7 @@
   /// CHECK-DAG: <<Sel2:i\d+>> Select [<<M100>>,<<Sel1>>,<<Cnd2>>]
   /// CHECK-DAG:               Return [<<Sel2>>]
   //
-  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.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
@@ -521,7 +521,7 @@
   /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<M100>>]
   /// CHECK-DAG:               Return [<<Max>>]
   //
-  /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.minmax1(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:               Select
   public static int minmax1(int x) {
     // Simple if-if gives clean select sequence.
@@ -534,7 +534,7 @@
     return x;
   }
 
-  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_gvn (before)
+  /// CHECK-START: int TestMinMax.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
@@ -544,7 +544,7 @@
   /// CHECK-DAG: <<Sel2:i\d+>> Select [<<P100>>,<<Sel1>>,<<Cnd1>>]
   /// CHECK-DAG:               Return [<<Sel2>>]
   //
-  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.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
@@ -552,7 +552,7 @@
   /// CHECK-DAG: <<Min:i\d+>>  Min [<<Max>>,<<P100>>]
   /// CHECK-DAG:               Return [<<Min>>]
   //
-  /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.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.
@@ -564,7 +564,7 @@
     return x;
   }
 
-  /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.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
@@ -572,13 +572,13 @@
   /// CHECK-DAG: <<Min:i\d+>>  Min [<<Max>>,<<P100>>]
   /// CHECK-DAG:               Return [<<Min>>]
   //
-  /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.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_gvn (after)
+  /// CHECK-START: int TestMinMax.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
@@ -586,13 +586,13 @@
   /// CHECK-DAG: <<Max:i\d+>>  Max [<<Min>>,<<M100>>]
   /// CHECK-DAG:               Return [<<Max>>]
   //
-  /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.minmax4(int) instruction_simplifier$after_gvn (after)
   /// CHECK-NOT:               Select
   public static int minmax4(int x) {
     return (x < -100) ? -100 : ((x > 100) ? 100 : x);
   }
 
-  /// CHECK-START: int Main.minmaxCSEScalar(int, int) select_generator (after)
+  /// CHECK-START: int TestMinMax.minmaxCSEScalar(int, int) select_generator (after)
   /// CHECK-DAG: <<Par1:i\d+>> ParameterValue
   /// CHECK-DAG: <<Par2:i\d+>> ParameterValue
   /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual    [<<Par1>>,<<Par2>>]
@@ -606,7 +606,7 @@
   /// CHECK-DAG: <<Add5:i\d+>> Add                [<<Sel2>>,<<Add4>>]
   /// CHECK-DAG:               Return             [<<Add5>>]
   //
-  /// CHECK-START: int Main.minmaxCSEScalar(int, int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.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>>]
@@ -628,7 +628,7 @@
     return t1 + t2 + t3 + t4 + t5 + t6;
   }
 
-  /// CHECK-START: int Main.minmaxCSEArray(int[], int[]) select_generator (after)
+  /// CHECK-START: int TestMinMax.minmaxCSEArray(int[], int[]) select_generator (after)
   /// CHECK-DAG: <<Arr1:i\d+>> ArrayGet
   /// CHECK-DAG: <<Arr2:i\d+>> ArrayGet
   /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual    [<<Arr1>>,<<Arr2>>]
@@ -642,7 +642,7 @@
   /// CHECK-DAG: <<Add5:i\d+>> Add                [<<Sel2>>,<<Add4>>]
   /// CHECK-DAG:               Return             [<<Add5>>]
   //
-  /// CHECK-START: int Main.minmaxCSEArray(int[], int[]) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.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>>]
@@ -664,7 +664,7 @@
     return t1 + t2 + t3 + t4 + t5 + t6;
   }
 
-  /// CHECK-START: int Main.minmaxCSEScalarAndCond(int, int) instruction_simplifier$after_gvn (after)
+  /// CHECK-START: int TestMinMax.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>>]
@@ -686,7 +686,7 @@
     return t1 + t2 + t3 + t4;
   }
 
-  public static void main(String[] args) {
+  public static void main() {
     // Intrinsics.
     expectEquals(10, minI(10));
     expectEquals(20, minI(25));
@@ -760,7 +760,7 @@
     expectEquals(90, minmaxCSEArray(a, b));
     expectEquals(20, minmaxCSEScalarAndCond(10, 10));
     expectEquals(60, minmaxCSEScalarAndCond(10, 20));
-    System.out.println("passed");
+    System.out.println("TestMinMax passed");
   }
 
   private static void expectEquals(int expected, int result) {
diff --git a/test/565-checker-rotate/src-art/Main.java b/test/567-checker-builder-intrinsics/src/TestRotate.java
similarity index 87%
rename from test/565-checker-rotate/src-art/Main.java
rename to test/567-checker-builder-intrinsics/src/TestRotate.java
index 6efb210..0593e60 100644
--- a/test/565-checker-rotate/src-art/Main.java
+++ b/test/567-checker-builder-intrinsics/src/TestRotate.java
@@ -14,137 +14,137 @@
  * limitations under the License.
  */
 
-public class Main {
+public class TestRotate {
 
-  /// CHECK-START: int Main.rotateLeftByte(byte, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftByte(byte, int) builder (after)
   /// CHECK:         <<ArgVal:b\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<NegDist:i\d+>> Neg [<<ArgDist>>]
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<NegDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateLeftByte(byte, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftByte(byte, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateLeftByte(byte value, int distance) {
     return Integer.rotateLeft(value, distance);
   }
 
-  /// CHECK-START: int Main.rotateLeftShort(short, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftShort(short, int) builder (after)
   /// CHECK:         <<ArgVal:s\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<NegDist:i\d+>> Neg [<<ArgDist>>]
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<NegDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateLeftShort(short, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftShort(short, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateLeftShort(short value, int distance) {
     return Integer.rotateLeft(value, distance);
   }
 
-  /// CHECK-START: int Main.rotateLeftChar(char, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftChar(char, int) builder (after)
   /// CHECK:         <<ArgVal:c\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<NegDist:i\d+>> Neg [<<ArgDist>>]
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<NegDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateLeftChar(char, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftChar(char, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateLeftChar(char value, int distance) {
     return Integer.rotateLeft(value, distance);
   }
 
-  /// CHECK-START: int Main.rotateLeftInt(int, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftInt(int, int) builder (after)
   /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<NegDist:i\d+>> Neg [<<ArgDist>>]
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<NegDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateLeftInt(int, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftInt(int, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateLeftInt(int value, int distance) {
     return Integer.rotateLeft(value, distance);
   }
 
-  /// CHECK-START: long Main.rotateLeftLong(long, int) builder (after)
+  /// CHECK-START: long TestRotate.rotateLeftLong(long, int) builder (after)
   /// CHECK:         <<ArgVal:j\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<NegDist:i\d+>> Neg [<<ArgDist>>]
   /// CHECK-DAG:     <<Result:j\d+>>  Ror [<<ArgVal>>,<<NegDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: long Main.rotateLeftLong(long, int) builder (after)
+  /// CHECK-START: long TestRotate.rotateLeftLong(long, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static long rotateLeftLong(long value, int distance) {
     return Long.rotateLeft(value, distance);
   }
 
-  /// CHECK-START: int Main.rotateRightByte(byte, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightByte(byte, int) builder (after)
   /// CHECK:         <<ArgVal:b\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<ArgDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateRightByte(byte, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightByte(byte, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateRightByte(byte value, int distance) {
     return Integer.rotateRight(value, distance);
   }
 
-  /// CHECK-START: int Main.rotateRightShort(short, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightShort(short, int) builder (after)
   /// CHECK:         <<ArgVal:s\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<ArgDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateRightShort(short, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightShort(short, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateRightShort(short value, int distance) {
     return Integer.rotateRight(value, distance);
   }
 
-  /// CHECK-START: int Main.rotateRightChar(char, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightChar(char, int) builder (after)
   /// CHECK:         <<ArgVal:c\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<ArgDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateRightChar(char, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightChar(char, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateRightChar(char value, int distance) {
     return Integer.rotateRight(value, distance);
   }
 
-  /// CHECK-START: int Main.rotateRightInt(int, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightInt(int, int) builder (after)
   /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<ArgDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateRightInt(int, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightInt(int, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateRightInt(int value, int distance) {
     return Integer.rotateRight(value, distance);
   }
 
-  /// CHECK-START: long Main.rotateRightLong(long, int) builder (after)
+  /// CHECK-START: long TestRotate.rotateRightLong(long, int) builder (after)
   /// CHECK:         <<ArgVal:j\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Result:j\d+>>  Ror [<<ArgVal>>,<<ArgDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: long Main.rotateRightLong(long, int) builder (after)
+  /// CHECK-START: long TestRotate.rotateRightLong(long, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static long rotateRightLong(long value, int distance) {
@@ -152,34 +152,34 @@
   }
 
 
-  /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftIntWithByteDistance(int, byte) builder (after)
   /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:b\d+>> ParameterValue
   /// CHECK-DAG:     <<NegDist:i\d+>> Neg [<<ArgDist>>]
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<NegDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftIntWithByteDistance(int, byte) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateLeftIntWithByteDistance(int value, byte distance) {
     return Integer.rotateLeft(value, distance);
   }
 
-  /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightIntWithByteDistance(int, byte) builder (after)
   /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:b\d+>> ParameterValue
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<ArgDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightIntWithByteDistance(int, byte) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
   private static int rotateRightIntWithByteDistance(int value, byte distance) {
     return Integer.rotateRight(value, distance);
   }
 
-  /// CHECK-START: int Main.rotateLeftBoolean(boolean, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) builder (after)
   /// CHECK:         <<ArgVal:z\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Zero:i\d+>>    IntConstant 0
@@ -189,10 +189,10 @@
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<Val>>,<<NegDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateLeftBoolean(boolean, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
-  /// CHECK-START: int Main.rotateLeftBoolean(boolean, int) select_generator (after)
+  /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) select_generator (after)
   /// CHECK:         <<ArgVal:z\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Zero:i\d+>>    IntConstant 0
@@ -202,17 +202,17 @@
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<SelVal>>,<<NegDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateLeftBoolean(boolean, int) select_generator (after)
+  /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) select_generator (after)
   /// CHECK-NOT:                      Phi
 
-  /// CHECK-START: int Main.rotateLeftBoolean(boolean, int) instruction_simplifier$after_bce (after)
+  /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) instruction_simplifier$after_bce (after)
   /// CHECK:         <<ArgVal:z\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<NegDist:i\d+>> Neg [<<ArgDist>>]
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<NegDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateLeftBoolean(boolean, int) instruction_simplifier$after_bce (after)
+  /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) instruction_simplifier$after_bce (after)
   /// CHECK-NOT:                      Select
 
   private static int rotateLeftBoolean(boolean value, int distance) {
@@ -227,7 +227,7 @@
     return Integer.rotateLeft(src, distance);
   }
 
-  public static void testRotateLeftBoolean() throws Exception {
+  public static void testRotateLeftBoolean() {
     for (int i = 0; i < 40; i++) {  // overshoot a bit
       int j = i & 31;
       expectEqualsInt(0, rotateLeftBoolean(false, i));
@@ -326,7 +326,7 @@
     }
   }
 
-  /// CHECK-START: int Main.rotateRightBoolean(boolean, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightBoolean(boolean, int) builder (after)
   /// CHECK:         <<ArgVal:z\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Zero:i\d+>>    IntConstant 0
@@ -335,10 +335,10 @@
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<Val>>,<<ArgDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateRightBoolean(boolean, int) builder (after)
+  /// CHECK-START: int TestRotate.rotateRightBoolean(boolean, int) builder (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
 
-  /// CHECK-START: int Main.rotateRightBoolean(boolean, int) select_generator (after)
+  /// CHECK-START: int TestRotate.rotateRightBoolean(boolean, int) select_generator (after)
   /// CHECK:         <<ArgVal:z\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Zero:i\d+>>    IntConstant 0
@@ -347,16 +347,16 @@
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<SelVal>>,<<ArgDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateRightBoolean(boolean, int) select_generator (after)
+  /// CHECK-START: int TestRotate.rotateRightBoolean(boolean, int) select_generator (after)
   /// CHECK-NOT:                     Phi
 
-  /// CHECK-START: int Main.rotateRightBoolean(boolean, int) instruction_simplifier$after_bce (after)
+  /// CHECK-START: int TestRotate.rotateRightBoolean(boolean, int) instruction_simplifier$after_bce (after)
   /// CHECK:         <<ArgVal:z\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
   /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<ArgDist>>]
   /// CHECK-DAG:                      Return [<<Result>>]
 
-  /// CHECK-START: int Main.rotateRightBoolean(boolean, int) instruction_simplifier$after_bce (after)
+  /// CHECK-START: int TestRotate.rotateRightBoolean(boolean, int) instruction_simplifier$after_bce (after)
   /// CHECK-NOT:                     Select
 
   private static int rotateRightBoolean(boolean value, int distance) {
@@ -371,7 +371,7 @@
     return Integer.rotateRight(src, distance);
   }
 
-  public static void testRotateRightBoolean() throws Exception {
+  public static void testRotateRightBoolean() {
     for (int i = 0; i < 40; i++) {  // overshoot a bit
       int j = (-i) & 31;
       expectEqualsInt(0, rotateRightBoolean(false, i));
@@ -509,7 +509,7 @@
   }
 
 
-  public static void main(String args[]) throws Exception {
+  public static void main() {
     testRotateLeftBoolean();
     testRotateLeftByte();
     testRotateLeftShort();
@@ -528,7 +528,7 @@
     testRotateLeftIntWithByteDistance();
     testRotateRightIntWithByteDistance();
 
-    System.out.println("passed");
+    System.out.println("TestRotate passed");
   }
 
 
diff --git a/test/566-checker-signum/src-art/Main.java b/test/567-checker-builder-intrinsics/src/TestSignum.java
similarity index 81%
rename from test/566-checker-signum/src-art/Main.java
rename to test/567-checker-builder-intrinsics/src/TestSignum.java
index 8fec4de..0a68ac2 100644
--- a/test/566-checker-signum/src-art/Main.java
+++ b/test/567-checker-builder-intrinsics/src/TestSignum.java
@@ -14,74 +14,74 @@
  * limitations under the License.
  */
 
-public class Main {
+public class TestSignum {
 
-  /// CHECK-START: int Main.signByte(byte) builder (after)
+  /// CHECK-START: int TestSignum.signByte(byte) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.signByte(byte) builder (after)
+  /// CHECK-START: int TestSignum.signByte(byte) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int signByte(byte x) {
     return Integer.signum(x);
   }
 
-  /// CHECK-START: int Main.signShort(short) builder (after)
+  /// CHECK-START: int TestSignum.signShort(short) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.signShort(short) builder (after)
+  /// CHECK-START: int TestSignum.signShort(short) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int signShort(short x) {
     return Integer.signum(x);
   }
 
-  /// CHECK-START: int Main.signChar(char) builder (after)
+  /// CHECK-START: int TestSignum.signChar(char) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.signChar(char) builder (after)
+  /// CHECK-START: int TestSignum.signChar(char) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int signChar(char x) {
     return Integer.signum(x);
   }
 
-  /// CHECK-START: int Main.signInt(int) builder (after)
+  /// CHECK-START: int TestSignum.signInt(int) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.signInt(int) builder (after)
+  /// CHECK-START: int TestSignum.signInt(int) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int signInt(int x) {
     return Integer.signum(x);
   }
 
-  /// CHECK-START: int Main.signLong(long) builder (after)
+  /// CHECK-START: int TestSignum.signLong(long) builder (after)
   /// CHECK-DAG:     <<Result:i\d+>> Compare
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.signLong(long) builder (after)
+  /// CHECK-START: int TestSignum.signLong(long) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
   private static int signLong(long x) {
     return Long.signum(x);
   }
 
-  /// CHECK-START: int Main.signBoolean(boolean) builder (after)
+  /// CHECK-START: int TestSignum.signBoolean(boolean) builder (after)
   /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
   /// CHECK-DAG:     <<One:i\d+>>    IntConstant 1
   /// CHECK-DAG:     <<Phi:i\d+>>    Phi [<<One>>,<<Zero>>]
   /// CHECK-DAG:     <<Result:i\d+>> Compare [<<Phi>>,<<Zero>>]
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.signBoolean(boolean) builder (after)
+  /// CHECK-START: int TestSignum.signBoolean(boolean) builder (after)
   /// CHECK-NOT:                     InvokeStaticOrDirect
 
-  /// CHECK-START: int Main.signBoolean(boolean) select_generator (after)
+  /// CHECK-START: int TestSignum.signBoolean(boolean) select_generator (after)
   /// CHECK-DAG:     <<Arg:z\d+>>    ParameterValue
   /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
   /// CHECK-DAG:     <<One:i\d+>>    IntConstant 1
@@ -89,16 +89,16 @@
   /// CHECK-DAG:     <<Result:i\d+>> Compare [<<Sel>>,<<Zero>>]
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.signBoolean(boolean) select_generator (after)
+  /// CHECK-START: int TestSignum.signBoolean(boolean) select_generator (after)
   /// CHECK-NOT:                     Phi
 
-  /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier$after_bce (after)
+  /// CHECK-START: int TestSignum.signBoolean(boolean) instruction_simplifier$after_bce (after)
   /// CHECK-DAG:     <<Arg:z\d+>>    ParameterValue
   /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
   /// CHECK-DAG:     <<Result:i\d+>> Compare [<<Arg>>,<<Zero>>]
   /// CHECK-DAG:                     Return [<<Result>>]
 
-  /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier$after_bce (after)
+  /// CHECK-START: int TestSignum.signBoolean(boolean) instruction_simplifier$after_bce (after)
   /// CHECK-NOT:                     Select
 
   private static int signBoolean(boolean x) {
@@ -113,7 +113,7 @@
     return Integer.signum(src_x);
   }
 
-  public static void testSignBoolean() throws Exception {
+  public static void testSignBoolean() {
     expectEquals(0, signBoolean(false));
     expectEquals(1, signBoolean(true));
   }
@@ -195,7 +195,7 @@
   }
 
 
-  public static void main(String args[]) throws Exception {
+  public static void main() {
     testSignBoolean();
     testSignByte();
     testSignShort();
@@ -203,7 +203,7 @@
     testSignInt();
     testSignLong();
 
-    System.out.println("passed");
+    System.out.println("TestSignum passed");
   }
 
   private static void expectEquals(int expected, int result) {
diff --git a/test/709-checker-varhandles/src/Main.java b/test/567-checker-builder-intrinsics/src/TestVarHandles.java
similarity index 69%
rename from test/709-checker-varhandles/src/Main.java
rename to test/567-checker-builder-intrinsics/src/TestVarHandles.java
index 8bb0434..2a82c06 100644
--- a/test/709-checker-varhandles/src/Main.java
+++ b/test/567-checker-builder-intrinsics/src/TestVarHandles.java
@@ -22,52 +22,52 @@
  * Instead, this test ensures the methods are recognized as intrinsic and behave
  * as expected.
  */
-public class Main {
+public class TestVarHandles {
 
   //
   // Fences (native).
   //
 
-  /// CHECK-START: void Main.fullFence() builder (after)
+  /// CHECK-START: void TestVarHandles.fullFence() builder (after)
   /// CHECK-NOT: InvokeStaticOrDirect
   //
-  /// CHECK-START: void Main.fullFence() builder (after)
+  /// CHECK-START: void TestVarHandles.fullFence() builder (after)
   /// CHECK-DAG: MemoryBarrier kind:AnyAny
   private static void fullFence() {
       VarHandle.fullFence();
   }
 
-  /// CHECK-START: void Main.acquireFence() builder (after)
+  /// CHECK-START: void TestVarHandles.acquireFence() builder (after)
   /// CHECK-NOT: InvokeStaticOrDirect
   //
-  /// CHECK-START: void Main.acquireFence() builder (after)
+  /// CHECK-START: void TestVarHandles.acquireFence() builder (after)
   /// CHECK-DAG: MemoryBarrier kind:LoadAny
   private static void acquireFence() {
       VarHandle.acquireFence();
   }
 
-  /// CHECK-START: void Main.releaseFence() builder (after)
+  /// CHECK-START: void TestVarHandles.releaseFence() builder (after)
   /// CHECK-NOT: InvokeStaticOrDirect
   //
-  /// CHECK-START: void Main.releaseFence() builder (after)
+  /// CHECK-START: void TestVarHandles.releaseFence() builder (after)
   /// CHECK-DAG: MemoryBarrier kind:AnyStore
   private static void releaseFence() {
       VarHandle.releaseFence();
   }
 
-  /// CHECK-START: void Main.loadLoadFence() builder (after)
+  /// CHECK-START: void TestVarHandles.loadLoadFence() builder (after)
   /// CHECK-NOT: InvokeStaticOrDirect
   //
-  /// CHECK-START: void Main.loadLoadFence() builder (after)
+  /// CHECK-START: void TestVarHandles.loadLoadFence() builder (after)
   /// CHECK-DAG: MemoryBarrier kind:LoadAny
   private static void loadLoadFence() {
       VarHandle.loadLoadFence();
   }
 
-  /// CHECK-START: void Main.storeStoreFence() builder (after)
+  /// CHECK-START: void TestVarHandles.storeStoreFence() builder (after)
   /// CHECK-NOT: InvokeStaticOrDirect
   //
-  /// CHECK-START: void Main.storeStoreFence() builder (after)
+  /// CHECK-START: void TestVarHandles.storeStoreFence() builder (after)
   /// CHECK-DAG: MemoryBarrier kind:StoreStore
   private static void storeStoreFence() {
       VarHandle.storeStoreFence();
@@ -77,13 +77,12 @@
   // Driver.
   //
 
-  public static void main(String[] args) {
-    System.out.println("starting");
+  public static void main() {
     acquireFence();
     releaseFence();
     loadLoadFence();
     storeStoreFence();
     fullFence();
-    System.out.println("passed");
+    System.out.println("TestVarHandles passed");
   }
 }
diff --git a/test/567-checker-compare/expected.txt b/test/567-checker-compare/expected.txt
deleted file mode 100644
index b0aad4d..0000000
--- a/test/567-checker-compare/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/567-checker-compare/info.txt b/test/567-checker-compare/info.txt
deleted file mode 100644
index 5bac7b1..0000000
--- a/test/567-checker-compare/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Unit test for 32-bit and 64-bit compare operations.
diff --git a/test/575-checker-isnan/expected.txt b/test/575-checker-isnan/expected.txt
deleted file mode 100644
index b0aad4d..0000000
--- a/test/575-checker-isnan/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/575-checker-isnan/info.txt b/test/575-checker-isnan/info.txt
deleted file mode 100644
index 5c48a6a..0000000
--- a/test/575-checker-isnan/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Unit test for float/double isNaN() operation.
diff --git a/test/617-clinit-oome/src/Main.java b/test/617-clinit-oome/src/Main.java
index 94cb7ce..bab344f 100644
--- a/test/617-clinit-oome/src/Main.java
+++ b/test/617-clinit-oome/src/Main.java
@@ -15,24 +15,38 @@
  */
 
 public class Main {
+  private static int exhaustJavaHeap(Object[] data, int index, int size) {
+    Runtime.getRuntime().gc();
+    while (size > 0) {
+        try {
+            data[index] = new byte[size];
+            index++;
+        } catch (OutOfMemoryError e) {
+            size /= 2;
+        }
+    }
+    return index;
+  }
+
   public static void main(String[] args) {
     Class klass = Other.class;
     Object[] data = new Object[100000];
     try {
         System.out.println("Filling heap");
-        int size = 256 * 1024 * 1024;
+
+        // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
+        // OOME to prevent GC thrashing, even if later allocations may succeed.
+        Runtime.getRuntime().gc();
+        System.runFinalization();
+        // NOTE: There is a GC invocation in the exhaustJavaHeap(). So we don't need one here.
+
         int index = 0;
-        while (true) {
-            try {
-                data[index] = new byte[size];
-                index++;
-            } catch (OutOfMemoryError e) {
-                size /= 2;
-                if (size == 0) {
-                    break;
-                }
-            }
-        }
+        int initial_size = 256 * 1024 * 1024;
+        // Repeat to ensure there is no space left on the heap.
+        index = exhaustJavaHeap(data, index, initial_size);
+        index = exhaustJavaHeap(data, index, /*size*/ 4);
+        index = exhaustJavaHeap(data, index, /*size*/ 4);
+
         // Initialize now that the heap is full.
         Other.print();
     } catch (OutOfMemoryError e) {
diff --git a/test/631-checker-fp-abs/expected.txt b/test/631-checker-fp-abs/expected.txt
deleted file mode 100644
index b0aad4d..0000000
--- a/test/631-checker-fp-abs/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/631-checker-fp-abs/info.txt b/test/631-checker-fp-abs/info.txt
deleted file mode 100644
index 0a1499e..0000000
--- a/test/631-checker-fp-abs/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests on floating-point Math.abs.
diff --git a/test/672-checker-throw-method/src/Main.java b/test/672-checker-throw-method/src/Main.java
index 360b52c..c2344b2 100644
--- a/test/672-checker-throw-method/src/Main.java
+++ b/test/672-checker-throw-method/src/Main.java
@@ -70,7 +70,9 @@
   /// CHECK:                Throw
   /// CHECK: end_block
   static public void doit1(int[] a) {
-    String par = "a";
+    // Being in the boot image means we know the load string cannot throw. Create one that is
+    // unlikely to be there to ensure we handle that case.
+    String par = "stringUnlikelyToBeInBootImage";
     if (a == null)
       throw new Error("you are null: " + par);
     for (int i = 0; i < a.length; i++) {
@@ -98,7 +100,9 @@
   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
   /// CHECK: end_block
   static public void doit2(int[] a) {
-    String par = "a";
+    // Being in the boot image means we know the load string cannot throw. Create one that is
+    // unlikely to be there to ensure we handle that case.
+    String par = "stringUnlikelyToBeInBootImage";
     if (a == null)
       doThrow(par);
     for (int i = 0; i < a.length; i++) {
@@ -128,7 +132,9 @@
   /// CHECK:                Throw
   /// CHECK: end_block
   static public void doit3(int[] a) {
-    String par = "a";
+    // Being in the boot image means we know the load string cannot throw. Create one that is
+    // unlikely to be there to ensure we handle that case.
+    String par = "stringUnlikelyToBeInBootImage";
     checkNotNullDirect(a, par);
     for (int i = 0; i < a.length; i++) {
       a[i] = 3;
@@ -155,7 +161,9 @@
   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
   /// CHECK: end_block
   static public void doit4(int[] a) {
-    String par = "a";
+    // Being in the boot image means we know the load string cannot throw. Create one that is
+    // unlikely to be there to ensure we handle that case.
+    String par = "stringUnlikelyToBeInBootImage";
     checkNotNullSplit(a, par);  // resembles Kotlin runtime lib
                                 // (test is lined, doThrow is not)
     for (int i = 0; i < a.length; i++) {
@@ -166,7 +174,9 @@
   // Ensures Phi values are merged properly.
   static public int doit5(int[] a) {
     int t = 100;
-    String par = "a";
+    // Being in the boot image means we know the load string cannot throw. Create one that is
+    // unlikely to be there to ensure we handle that case.
+    String par = "stringUnlikelyToBeInBootImage";
     if (a == null) {
       doThrow(par);
     } else {
@@ -205,7 +215,7 @@
   /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (after)
   /// CHECK-NOT:              NullCheck
   static public int deleteNullCheck(int[] a) {
-    checkNotNullSplit(a, "a");
+    checkNotNullSplit(a, "stringUnlikelyToBeInBootImage");
     return a[0];
   }
 
@@ -215,7 +225,7 @@
   /// CHECK-START: int Main.deleteNullCheckAlt(int[]) dead_code_elimination$after_inlining (after)
   /// CHECK-NOT: NullCheck
   static public int deleteNullCheckAlt(int[] a) {
-    checkNotNullSplitAlt(a, "a");
+    checkNotNullSplitAlt(a, "stringUnlikeltyToBeInBootImage");
     return a[0];
   }
 
@@ -227,9 +237,9 @@
   /// CHECK-START: int Main.deleteNullChecks3(int[], int[], int[]) dead_code_elimination$after_inlining (after)
   /// CHECK-NOT: NullCheck
   static public int deleteNullChecks3(int[] a, int[] b, int[] c) {
-    checkNotNullSplit(a, "a");
-    checkNotNullSplit(b, "b");
-    checkNotNullSplit(c, "c");
+    checkNotNullSplit(a, "stringUnlikelytoBeInBootImage1");
+    checkNotNullSplit(b, "stringUnlikelytoBeInBootImage2");
+    checkNotNullSplit(c, "stringUnlikelytoBeInBootImage3");
     return a[0] + b[0] + c[0];
   }
 
diff --git a/test/673-checker-throw-vmethod/src/Main.java b/test/673-checker-throw-vmethod/src/Main.java
index 206dfaf..f62cfc8 100644
--- a/test/673-checker-throw-vmethod/src/Main.java
+++ b/test/673-checker-throw-vmethod/src/Main.java
@@ -64,7 +64,9 @@
   /// CHECK:                Throw
   /// CHECK: end_block
   public void doit1(int[] a) {
-    String par = "a";
+    // Being in the boot image means we know the load string cannot throw. Create one that is
+    // unlikely to be there to ensure we handle that case.
+    String par = "stringUnlikelyToBeInBootImage";
     if (a == null)
       throw new Error("you are null: " + par);
     for (int i = 0; i < a.length; i++) {
@@ -92,7 +94,9 @@
   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
   /// CHECK: end_block
   public void doit2(int[] a) {
-    String par = "a";
+    // Being in the boot image means we know the load string cannot throw. Create one that is
+    // unlikely to be there to ensure we handle that case.
+    String par = "stringUnlikelyToBeInBootImage";
     if (a == null)
       doThrow(par);
     for (int i = 0; i < a.length; i++) {
@@ -122,7 +126,9 @@
   /// CHECK:                Throw
   /// CHECK: end_block
   public void doit3(int[] a) {
-    String par = "a";
+    // Being in the boot image means we know the load string cannot throw. Create one that is
+    // unlikely to be there to ensure we handle that case.
+    String par = "stringUnlikelyToBeInBootImage";
     checkNotNullDirect(a, par);
     for (int i = 0; i < a.length; i++) {
       a[i] = 3;
@@ -149,7 +155,9 @@
   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
   /// CHECK: end_block
   public void doit4(int[] a) {
-    String par = "a";
+    // Being in the boot image means we know the load string cannot throw. Create one that is
+    // unlikely to be there to ensure we handle that case.
+    String par = "stringUnlikelyToBeInBootImage";
     checkNotNullSplit(a, par);
     for (int i = 0; i < a.length; i++) {
       a[i] = 4;
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index 9295655..855134bc 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -103,7 +103,6 @@
     ChildClass.everythingWhitelisted = everythingWhitelisted;
 
     boolean isSameBoot = (isParentInBoot == isChildInBoot);
-    boolean isDebuggable = VMRuntime.getRuntime().isJavaDebuggable();
 
     // For compat reasons, meta-reflection should still be usable by apps if hidden api check
     // hardening is disabled (i.e. target SDK is Q or earlier). The only configuration where this
diff --git a/test/679-checker-minmax/expected.txt b/test/679-checker-minmax/expected.txt
deleted file mode 100644
index b0aad4d..0000000
--- a/test/679-checker-minmax/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/679-checker-minmax/info.txt b/test/679-checker-minmax/info.txt
deleted file mode 100644
index 4f7b9f5..0000000
--- a/test/679-checker-minmax/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on detecting min/max.
diff --git a/test/681-checker-abs/expected.txt b/test/681-checker-abs/expected.txt
deleted file mode 100644
index b0aad4d..0000000
--- a/test/681-checker-abs/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/681-checker-abs/info.txt b/test/681-checker-abs/info.txt
deleted file mode 100644
index d36e76e..0000000
--- a/test/681-checker-abs/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on detecting abs.
diff --git a/test/709-checker-varhandles/expected.txt b/test/709-checker-varhandles/expected.txt
deleted file mode 100644
index 651da72..0000000
--- a/test/709-checker-varhandles/expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-starting
-passed
diff --git a/test/709-checker-varhandles/info.txt b/test/709-checker-varhandles/info.txt
deleted file mode 100644
index 2221240..0000000
--- a/test/709-checker-varhandles/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test support for intrinsics in Java 9 java.lang.invoke.VarHandle.
diff --git a/test/726-array-store/expected.txt b/test/726-array-store/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/726-array-store/expected.txt
diff --git a/test/726-array-store/info.txt b/test/726-array-store/info.txt
new file mode 100644
index 0000000..623d923
--- /dev/null
+++ b/test/726-array-store/info.txt
@@ -0,0 +1 @@
+Regression test on storing an invalid type into an array.
diff --git a/test/726-array-store/src/Main.java b/test/726-array-store/src/Main.java
new file mode 100644
index 0000000..349e272
--- /dev/null
+++ b/test/726-array-store/src/Main.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  public static void main(String[] args) {
+    try {
+      $noinline$doTest(args);
+      throw new Error("Expected ArrayStoreException");
+    } catch (ArrayStoreException e) {
+      // expected
+      check(e, mainLine, methodLine, "$noinline$doTest");
+    }
+  }
+
+  public static void $noinline$doTest(String[] args) {
+    Object[] o = new String[2];
+    o[0] = args;
+  }
+
+  public static int mainLine = 21;
+  public static int methodLine = 31;
+
+  static void check(ArrayStoreException ase, int mainLine, int methodLine, String methodName) {
+    StackTraceElement[] trace = ase.getStackTrace();
+    checkElement(trace[0], "Main", methodName, "Main.java", methodLine);
+    checkElement(trace[1], "Main", "main", "Main.java", mainLine);
+  }
+
+  static void checkElement(StackTraceElement element,
+                           String declaringClass, String methodName,
+                           String fileName, int lineNumber) {
+    assertEquals(declaringClass, element.getClassName());
+    assertEquals(methodName, element.getMethodName());
+    assertEquals(fileName, element.getFileName());
+    assertEquals(lineNumber, element.getLineNumber());
+  }
+
+  static void assertEquals(Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+      throw new AssertionError(msg);
+    }
+  }
+
+  static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 43f1631..b8cf417 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -46,7 +46,6 @@
 
 art_cc_defaults {
     name: "art_gtest_defaults",
-    test_per_src: true,
     // These really are gtests, but the gtest library comes from libart-gtest.so
     gtest: false,
     defaults: [
@@ -115,9 +114,10 @@
         "libdexfiled",
         "libprofiled",
         "libartbased",
+        "liblog",
     ],
     static_libs: [
-        "libgtest",
+        "libgtest_isolated",
     ],
     target: {
         android32: {
@@ -146,11 +146,18 @@
 art_cc_library {
     name: "libart-gtest",
     host_supported: true,
+    defaults: [
+        "art_debug_defaults",
+        "art_defaults",
+    ],
+    srcs: [
+        "common/gtest_main.cc",
+    ],
     whole_static_libs: [
         "libart-compiler-gtest",
         "libart-runtime-gtest",
         "libartbase-art-gtest",
-        "libgtest",
+        "libgtest_isolated",
     ],
     shared_libs: [
         "libartd",
@@ -160,6 +167,7 @@
         "libartbased",
         "libbase",
         "libbacktrace",
+        "liblog",
     ],
     target: {
         darwin: {
@@ -558,6 +566,7 @@
         "172-app-image-twice/debug_print_class.cc",
         "177-visibly-initialized-deadlock/visibly_initialized.cc",
         "178-app-image-native-method/native_methods.cc",
+        "179-nonvirtual-jni/nonvirtual-call.cc",
         "1945-proxy-method-arguments/get_args.cc",
         "203-multi-checkpoint/multi_checkpoint.cc",
         "305-other-fault-handler/fault_handler.cc",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index fb3ca02..a0dfa1b 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -135,7 +135,9 @@
       $(foreach address_size, $(ALL_ADDRESS_SIZES), $(eval \
         $(call core-image-dependencies,$(target),$(image),$(compiler),$(address_size)))))))
 
-test-art-host-run-test-dependencies : $(host_prereq_rules) $(HOST_CORE_IMG_OUTS)
+test-art-host-run-test-dependencies : \
+	$(ART_TEST_HOST_RUN_TEST_DEPENDENCIES) $(TEST_ART_RUN_TEST_DEPENDENCIES) \
+	$(HOST_BOOT_IMAGE_JARS) $(HOST_BOOT_IMAGE) $(2ND_HOST_BOOT_IMAGE)
 .PHONY: test-art-host-run-test-dependencies
 test-art-target-run-test-dependencies :
 .PHONY: test-art-target-run-test-dependencies
diff --git a/test/common/gtest_main.cc b/test/common/gtest_main.cc
new file mode 100644
index 0000000..9176001
--- /dev/null
+++ b/test/common/gtest_main.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open 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 <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/mem_map.h"
+#include "base/mutex.h"
+#include "gtest_extras/IsolateMain.h"
+#include "runtime.h"
+
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+  static const char* initial_args[] = {"--deadline_threshold_ms=1200000",  // hwasan takes ~10min.
+                                       "--slow_threshold_ms=300000"};
+  *args = initial_args;
+  *num_args = 2;
+  return true;
+}
+
+// Allow other test code to run global initialization/configuration before gtest infra takes over.
+extern "C" __attribute__((visibility("default"))) __attribute__((weak)) void ArtTestGlobalInit();
+
+int main(int argc, char** argv, char** envp) {
+  // Gtests can be very noisy. For example, an executable with multiple tests will trigger native
+  // bridge warnings. The following line reduces the minimum log severity to ERROR and suppresses
+  // everything else. In case you want to see all messages, comment out the line.
+  setenv("ANDROID_LOG_TAGS", "*:e", 1);
+
+  art::Locks::Init();
+  art::InitLogging(argv, art::Runtime::Abort);
+  art::MemMap::Init();
+  LOG(INFO) << "Running main() from common_runtime_test.cc...";
+  if (ArtTestGlobalInit != nullptr) {
+    ArtTestGlobalInit();
+  }
+  return IsolateMain(argc, argv, envp);
+}
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 97bb396..af8fecd 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -711,37 +711,35 @@
 # Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
 # because that's what we use for compiling the core.art image.
 # It may contain additional modules from TEST_CORE_JARS.
-bpath_modules="core-oj core-libart core-icu4j okhttp bouncycastle apache-xml conscrypt"
+bpath_modules="core-oj core-libart okhttp bouncycastle apache-xml core-icu4j conscrypt"
 bpath=""
 bpath_locations=""
 bpath_separator=""
+bpath_prefix=""
+bpath_location_prefix=""
 if [ "${HOST}" = "y" ]; then
-    framework="${ANDROID_HOST_OUT}/framework"
-    if [ "${ANDROID_HOST_OUT:0:${#ANDROID_BUILD_TOP}+1}" = "${ANDROID_BUILD_TOP}/" ]; then
-      framework_location="${ANDROID_HOST_OUT:${#ANDROID_BUILD_TOP}+1}/framework"
-    else
-      echo "error: ANDROID_BUILD_TOP/ is not a prefix of ANDROID_HOST_OUT"
-      echo "ANDROID_BUILD_TOP=${ANDROID_BUILD_TOP}"
-      echo "ANDROID_HOST_OUT=${ANDROID_HOST_OUT}"
-      exit
-    fi
-    bpath_suffix="-hostdex"
-    for bpath_module in ${bpath_modules}; do
-      bpath+="${bpath_separator}${framework}/${bpath_module}${bpath_suffix}.jar"
-      bpath_locations+="${bpath_separator}${framework_location}/${bpath_module}${bpath_suffix}.jar"
-      bpath_separator=":"
-    done
-else
-    for bpath_module in ${bpath_modules}; do
-      apex_module="com.android.art"
-      if [ "${bpath_module}" = "conscrypt" ]; then
-        apex_module="com.android.conscrypt"
-      fi
-      bpath+="${bpath_separator}/apex/${apex_module}/javalib/${bpath_module}.jar"
-      bpath_locations+="${bpath_separator}/apex/${apex_module}/javalib/${bpath_module}.jar"
-      bpath_separator=":"
-    done
+  bpath_prefix="${ANDROID_HOST_OUT}"
+  if [ "${ANDROID_HOST_OUT:0:${#ANDROID_BUILD_TOP}+1}" = "${ANDROID_BUILD_TOP}/" ]; then
+    bpath_location_prefix="${ANDROID_HOST_OUT:${#ANDROID_BUILD_TOP}+1}"
+  else
+    echo "error: ANDROID_BUILD_TOP/ is not a prefix of ANDROID_HOST_OUT"
+    echo "ANDROID_BUILD_TOP=${ANDROID_BUILD_TOP}"
+    echo "ANDROID_HOST_OUT=${ANDROID_HOST_OUT}"
+    exit
+  fi
 fi
+for bpath_module in ${bpath_modules}; do
+  apex_module="com.android.art"
+  case "$bpath_module" in
+    (conscrypt)  apex_module="com.android.conscrypt";;
+    (core-icu4j) apex_module="com.android.i18n";;
+    (*)          apex_module="com.android.art";;
+  esac
+  bpath_jar="/apex/${apex_module}/javalib/${bpath_module}.jar"
+  bpath+="${bpath_separator}${bpath_prefix}${bpath_jar}"
+  bpath_locations+="${bpath_separator}${bpath_location_prefix}${bpath_jar}"
+  bpath_separator=":"
+done
 # Pass down the bootclasspath
 FLAGS="${FLAGS} -Xbootclasspath:${bpath}"
 FLAGS="${FLAGS} -Xbootclasspath-locations:${bpath_locations}"
@@ -1170,7 +1168,7 @@
     # Needed to access libarttest(d).so and JVMTI agent libraries.
     LD_LIBRARY_PATH="/data/$TEST_DIRECTORY/art/$ISA:$LD_LIBRARY_PATH"
     # Needed to access the boot (core) image files.
-    LD_LIBRARY_PATH="/data/art-test/$ISA:$LD_LIBRARY_PATH"
+    LD_LIBRARY_PATH="/apex/com.android.art/javalib/$ISA:$LD_LIBRARY_PATH"
     # Needed to access the test's Odex files.
     LD_LIBRARY_PATH="$DEX_LOCATION/oat/$ISA:$LD_LIBRARY_PATH"
     # Needed to access the test's native libraries (see e.g. 674-hiddenapi,
diff --git a/test/gtests-target.xml b/test/gtests-target.xml
index 2668dd6..62aa938 100644
--- a/test/gtests-target.xml
+++ b/test/gtests-target.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="ART gtests">
+    <option name="test-suite-tag" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.InstallApexModuleTargetPreparer" >
         <option name="test-file-name" value="com.android.art.testing.apex" />
     </target_preparer>
@@ -171,6 +172,12 @@
         <option name="push-file"
             key="art-gtest-jars-VerifySoftFailDuringClinit.jar"
             value="/data/nativetest/art/art-gtest-VerifySoftFailDuringClinit.jar"/>
+        <option name="push-file"
+            key="art-gtest-jars-Extension1.jar"
+            value="/data/nativetest/art/art-gtest-Extension1.jar"/>
+        <option name="push-file"
+            key="art-gtest-jars-Extension2.jar"
+            value="/data/nativetest/art/art-gtest-Extension2.jar"/>
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
@@ -326,6 +333,12 @@
         <option name="push-file"
             key="art-gtest-jars-VerifySoftFailDuringClinit.jar"
             value="/data/nativetest64/art/art-gtest-VerifySoftFailDuringClinit.jar"/>
+        <option name="push-file"
+            key="art-gtest-jars-Extension1.jar"
+            value="/data/nativetest64/art/art-gtest-Extension1.jar"/>
+        <option name="push-file"
+            key="art-gtest-jars-Extension2.jar"
+            value="/data/nativetest64/art/art-gtest-Extension2.jar"/>
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <!-- only for debugging purposes -->
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 722f866..10c05f9 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -652,6 +652,12 @@
         "env_vars": {"SANITIZE_HOST": "address"}
     },
     {
+        "tests": "175-alloc-big-bignums",
+        "description": "ASAN runs out of memory due to huge allocations.",
+        "variant": "target",
+        "env_vars": {"SANITIZE_TARGET": "hwaddress"}
+    },
+    {
         "tests": "202-thread-oome",
         "description": "ASAN aborts when large thread stacks are requested.",
         "variant": "host",
@@ -688,36 +694,6 @@
         "bug": "b/31098551"
     },
     {
-        "tests": [
-            "059-finalizer-throw",
-            "074-gc-thrash",
-            "911-get-stack-trace",
-            "913-heaps",
-            "980-redefine-object"
-        ],
-        "description": [
-            "Interpreter with access checks stack frames are too large and result in",
-            "StackOverFlow errors being thrown."
-        ],
-        "variant": "interp-ac & host",
-        "env_vars": {"SANITIZE_HOST": "address"}
-    },
-    {
-        "tests": [
-            "059-finalizer-throw",
-            "074-gc-thrash",
-            "911-get-stack-trace",
-            "913-heaps",
-            "980-redefine-object"
-        ],
-        "description": [
-            "Interpreter with access checks stack frames are too large and result in",
-            "StackOverFlow errors being thrown."
-        ],
-        "variant": "interp-ac & target",
-        "env_vars": {"SANITIZE_TARGET": "address"}
-    },
-    {
         "tests": "071-dexfile-map-clean",
         "description": [ "We use prebuilt zipalign on master-art-host to avoid pulling in a lot",
                          "of the framework. But a non-sanitized zipalign binary does not work with",
@@ -965,7 +941,6 @@
           "565-checker-doublenegbitwise",
           "565-checker-irreducible-loop",
           "566-polymorphic-inlining",
-          "567-checker-compare",
           "569-checker-pattern-replacement",
           "570-checker-osr",
           "571-irreducible-loop",
@@ -1009,7 +984,6 @@
           "628-vdex",
           "629-vdex-speed",
           "630-safecast-array",
-          "631-checker-fp-abs",
           "633-checker-rtp-getclass",
           "634-vdex-duplicate",
           "636-wrong-static-access",
@@ -1222,6 +1196,9 @@
                   "2005-pause-all-redefine-multithreaded",
                   "2006-virtual-structural-finalizing",
                   "2007-virtual-structural-finalizable",
+                  "2009-structural-local-ref",
+                  "2011-stack-walk-concurrent-instrument",
+                  "2012-structural-redefinition-failures-jni-id",
                   "2033-shutdown-mechanics"],
         "variant": "jvm",
         "description": ["Doesn't run on RI."]
@@ -1386,14 +1363,7 @@
                   "960-default-smali",
                   "966-default-conflict",
                   "990-field-trace",
-                  "1953-pop-frame",
-                  "1954-pop-frame-jit",
-                  "1955-pop-frame-jit-called",
-                  "1956-pop-frame-jit-calling",
-                  "2009-structural-local-ref",
-                  "2011-stack-walk-concurrent-instrument",
-                  "2012-structural-redefinition-failures-jni-id",
-                  "2029-spaces-in-SimpleName"],
+                  "2034-spaces-in-SimpleName"],
         "variant": "jvm",
         "bug": "b/154802847",
         "description": ["Failing on RI. Needs further investigating."]
diff --git a/test/run-test b/test/run-test
index 717938f..86d30d5 100755
--- a/test/run-test
+++ b/test/run-test
@@ -615,7 +615,8 @@
 function guess_target_arch_name() {
     # Check whether this is a device with native bridge. Currently this is hardcoded
     # to x86 + arm.
-    x86_arm=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | sort | grep -E '^(arm|x86)$'`
+    local guess_path=${ANDROID_PRODUCT_OUT}/system/apex/com.android.art.testing/javalib
+    local x86_arm=`ls ${guess_path} | sort | grep -E '^(arm|x86)$'`
     # Collapse line-breaks into spaces
     x86_arm=$(echo $x86_arm)
     if [ "x$x86_arm" = "xarm x86" ] ; then
@@ -627,8 +628,8 @@
             target_arch_name=x86
         fi
     else
-        grep32bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm|x86)$'`
-        grep64bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm64|x86_64)$'`
+        local grep32bit=`ls ${guess_path} | grep -E '^(arm|x86)$'`
+        local grep64bit=`ls ${guess_path} | grep -E '^(arm64|x86_64)$'`
         if [ "x${suffix64}" = "x64" ]; then
             target_arch_name=${grep64bit}
         else
@@ -675,12 +676,12 @@
 elif [ "$runtime" = "art" ]; then
     if [ "$target_mode" = "no" ]; then
         guess_host_arch_name
-        run_args+=(--boot "${ANDROID_HOST_OUT}/framework/core.art:*")
+        run_args+=(--boot "${ANDROID_HOST_OUT}/apex/com.android.art/javalib/boot.art")
         run_args+=(--runtime-option "-Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}")
     else
         guess_target_arch_name
         run_args+=(--runtime-option "-Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}")
-        run_args+=(--boot "/data/art-test/core.art:/data/art-test/*")
+        run_args+=(--boot "/apex/com.android.art/javalib/boot.art")
     fi
     if [ "$relocate" = "yes" ]; then
       run_args+=(--relocate)
@@ -797,7 +798,6 @@
         echo "    --zygote              Spawn the process from the Zygote." \
              "If used, then the"
         echo "                          other runtime options are ignored."
-        echo "    --no-dex2oat          Run as though dex2oat was failing."
         echo "    --prebuild            Run dex2oat on the files before starting test. (default)"
         echo "    --no-prebuild         Do not run dex2oat on the files before starting"
         echo "                          the test."
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 8015dde..8bec206 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -61,6 +61,7 @@
   raise
 
 import contextlib
+import csv
 import datetime
 import fnmatch
 import itertools
@@ -80,7 +81,6 @@
 from target_config import target_config
 from device_config import device_config
 
-# timeout for individual tests.
 # TODO: make it adjustable per tests and for buildbots
 #
 # Note: this needs to be larger than run-test timeouts, as long as this script
@@ -131,6 +131,8 @@
 build = False
 gdb = False
 gdb_arg = ''
+csv_result = None
+csv_writer = None
 runtime_option = ''
 with_agent = []
 zipapex_loc = None
@@ -146,6 +148,31 @@
 # value: set of variants user wants to run of type <key>.
 _user_input_variants = collections.defaultdict(set)
 
+def setup_csv_result():
+  """Set up the CSV output if required."""
+  global csv_writer
+  csv_writer = csv.writer(csv_result)
+  # Write the header.
+  csv_writer.writerow(['target', 'run', 'prebuild', 'compiler', 'relocate', 'trace', 'gc',
+                       'jni', 'image', 'debuggable', 'jvmti', 'cdex_level', 'test', 'address_size', 'result'])
+
+
+def send_csv_result(test, result):
+  """
+  Write a line into the CSV results file if one is available.
+  """
+  if csv_writer is not None:
+    csv_writer.writerow(extract_test_name(test) + [result])
+
+def close_csv_file():
+  global csv_result
+  global csv_writer
+  if csv_result is not None:
+    csv_writer = None
+    csv_result.flush()
+    csv_result.close()
+    csv_result = None
+
 def gather_test_info():
   """The method gathers test information about the test to be run which includes
   generating the list of total tests from the art/test directory and the list
@@ -710,6 +737,7 @@
           progress_info,
           test_name,
           result_text)
+    send_csv_result(test_name, result)
     print_text(info)
   except Exception as e:
     print_text(('%s\n%s\n') % (test_name, str(e)))
@@ -909,6 +937,32 @@
     for failed_test in sorted([test_info[0] for test_info in failed_tests]):
       print_text(('%s\n' % (failed_test)))
 
+test_name_matcher = None
+def extract_test_name(test_name):
+  """Parses the test name and returns all the parts"""
+  global test_name_matcher
+  if test_name_matcher is None:
+    regex = '^test-art-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['target']) + ')-'
+    regex += 'run-test-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['run']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['prebuild']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['compiler']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['relocate']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['trace']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['gc']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['jni']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['image']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['debuggable']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['jvmti']) + ')-'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['cdex_level']) + ')-'
+    regex += '(' + '|'.join(RUN_TEST_SET) + ')'
+    regex += '(' + '|'.join(VARIANT_TYPE_DICT['address_sizes']) + ')$'
+    test_name_matcher = re.compile(regex)
+  match = test_name_matcher.match(test_name)
+  if match:
+    return list(match.group(i) for i in range(1,15))
+  raise ValueError(test_name + " is not a valid test")
 
 def parse_test_name(test_name):
   """Parses the testname provided by the user.
@@ -928,39 +982,21 @@
   if test_set:
     return test_set
 
-  regex = '^test-art-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['target']) + ')-'
-  regex += 'run-test-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['run']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['prebuild']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['compiler']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['relocate']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['trace']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['gc']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['jni']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['image']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['debuggable']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['jvmti']) + ')-'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['cdex_level']) + ')-'
-  regex += '(' + '|'.join(RUN_TEST_SET) + ')'
-  regex += '(' + '|'.join(VARIANT_TYPE_DICT['address_sizes']) + ')$'
-  match = re.match(regex, test_name)
-  if match:
-    _user_input_variants['target'].add(match.group(1))
-    _user_input_variants['run'].add(match.group(2))
-    _user_input_variants['prebuild'].add(match.group(3))
-    _user_input_variants['compiler'].add(match.group(4))
-    _user_input_variants['relocate'].add(match.group(5))
-    _user_input_variants['trace'].add(match.group(6))
-    _user_input_variants['gc'].add(match.group(7))
-    _user_input_variants['jni'].add(match.group(8))
-    _user_input_variants['image'].add(match.group(9))
-    _user_input_variants['debuggable'].add(match.group(10))
-    _user_input_variants['jvmti'].add(match.group(11))
-    _user_input_variants['cdex_level'].add(match.group(12))
-    _user_input_variants['address_sizes'].add(match.group(14))
-    return {match.group(13)}
-  raise ValueError(test_name + " is not a valid test")
+  parsed = extract_test_name(test_name)
+  _user_input_variants['target'].add(parsed[0])
+  _user_input_variants['run'].add(parsed[1])
+  _user_input_variants['prebuild'].add(parsed[2])
+  _user_input_variants['compiler'].add(parsed[3])
+  _user_input_variants['relocate'].add(parsed[4])
+  _user_input_variants['trace'].add(parsed[5])
+  _user_input_variants['gc'].add(parsed[6])
+  _user_input_variants['jni'].add(parsed[7])
+  _user_input_variants['image'].add(parsed[8])
+  _user_input_variants['debuggable'].add(parsed[9])
+  _user_input_variants['jvmti'].add(parsed[10])
+  _user_input_variants['cdex_level'].add(parsed[11])
+  _user_input_variants['address_sizes'].add(parsed[13])
+  return {parsed[12]}
 
 
 def setup_env_for_build_target(build_target, parser, options):
@@ -1013,6 +1049,7 @@
   global run_all_configs
   global with_agent
   global zipapex_loc
+  global csv_result
 
   parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
   parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)')
@@ -1057,6 +1094,8 @@
                             help='Location for runtime zipapex.')
   global_group.add_argument('-a', '--all', action='store_true', dest='run_all',
                             help="Run all the possible configurations for the input test set")
+  global_group.add_argument('--csv-results', action='store', dest='csv_result', default=None,
+                            type=argparse.FileType('w'), help='Store a CSV record of all results.')
   for variant_type, variant_set in VARIANT_TYPE_DICT.items():
     var_group = parser.add_argument_group(
         '{}-type Options'.format(variant_type),
@@ -1070,6 +1109,9 @@
       var_group.add_argument(flag, action='store_true', dest=variant)
 
   options = vars(parser.parse_args())
+  if options['csv_result'] is not None:
+    csv_result = options['csv_result']
+    setup_csv_result()
   # Handle the --all-<type> meta-options
   for variant_type, variant_set in VARIANT_TYPE_DICT.items():
     if options['all_' + variant_type]:
@@ -1147,6 +1189,7 @@
     run_tests(RUN_TEST_SET)
 
   print_analysis()
+  close_csv_file()
 
   exit_code = 0 if len(failed_tests) == 0 else 1
   sys.exit(exit_code)
diff --git a/tools/Android.bp b/tools/Android.bp
index d38e218..e9badc4 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -35,9 +35,36 @@
     host_supported: true,
     src: "art",
     filename_from_src: true,
-    required: [
-        "dalvikvm",
-        "dex2oat",
-        "dex2oatd",
-    ],
+    target: {
+        android: {
+            required: [
+                "com.android.art.release",
+            ],
+        },
+        host: {
+            required: [
+                "dalvikvm",
+                "dex2oat",
+            ],
+        },
+    },
+}
+
+sh_binary {
+    name: "dex2oat-script",
+    host_supported: true,
+    src: "dex2oat_wrapper",
+    filename_from_src: true,
+    target: {
+        android: {
+            required: [
+                "com.android.art.release",
+            ],
+        },
+        host: {
+            required: [
+                "dex2oat",
+            ],
+        },
+    },
 }
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index 160bb28..f827113 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -58,8 +58,9 @@
   $(AHAT_TEST_DALVIKVM_DEP) \
   $(ART_HOST_SHARED_LIBRARY_DEPENDENCIES) \
   $(ART_HOST_SHARED_LIBRARY_DEBUG_DEPENDENCIES) \
+  $(ART_HOST_DEX_DEPENDENCIES) \
   $(HOST_OUT_EXECUTABLES)/art \
-  $(HOST_CORE_IMG_OUT_BASE)$(CORE_IMG_SUFFIX)
+  $(HOST_CORE_IMG_OUTS)
 
 $(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_ART := $(HOST_OUT_EXECUTABLES)/art
 $(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_JAR := $(AHAT_TEST_DUMP_JAR)
diff --git a/tools/art b/tools/art
old mode 100644
new mode 100755
index fe74614..aa5abb7
--- a/tools/art
+++ b/tools/art
@@ -195,6 +195,12 @@
     verbose_run mkdir -p $(dirname "$dex_file")/oat/$ISA
     local oat_file=$(basename "$dex_file")
     local oat_file=$(dirname "$dex_file")/oat/$ISA/${oat_file%.*}.odex
+    if [ "$GENERATE_APP_IMAGE" = "yes" ]; then
+      local art_file=$(basename "$dex_file")
+      local art_file=$(dirname "$dex_file")/oat/$ISA/${art_file%.*}.art
+      DEX2OAT_FLAGS+=("--app-image-file=$art_file")
+    fi
+
     # When running dex2oat use the exact same context as when running dalvikvm.
     # (see run_art function)
     verbose_run ANDROID_DATA=$ANDROID_DATA                    \
@@ -232,6 +238,17 @@
     case $1 in
       -Xcompiler-option)
         DEX2OAT_FLAGS+=("$2")
+
+        # Enable app images for profile filters
+        case $2 in
+          --compiler-filter=speed-profile)
+            GENERATE_APP_IMAGE="yes"
+            ;;
+          --compiler-filter=everything-profile)
+            GENERATE_APP_IMAGE="yes"
+            ;;
+        esac
+
         shift
         ;;
       -Ximage:*)
@@ -314,6 +331,7 @@
 EXTRA_OPTIONS=()
 DEX2OAT_FLAGS=()
 DEX2OAT_CLASSPATH=()
+GENERATE_APP_IMAGE="no"
 
 # Parse arguments
 while [[ "$1" = "-"* ]]; do
@@ -527,42 +545,44 @@
   exit 1
 fi
 
-if [[ "$DEX2OAT_BOOT_IMAGE" = *core*.art && "$DEX2OAT_BCP" = "" ]]; then
+# Create boot class path filename or location list.
+# It takes one optional argument which is the prefix to be inserted before each entry.
+function get_boot_class_path() {
   # Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
-  # because that's what we use for compiling the core.art image.
-  # It may contain additional modules from TEST_CORE_JARS.
-  core_jars_list="core-oj core-libart core-icu4j okhttp bouncycastle apache-xml"
-  core_jars_suffix=
-  if [[ -e $ANDROID_ROOT/framework/core-oj-hostdex.jar ]]; then
-    core_jars_suffix=-hostdex
-    core_filenames_dir=$ANDROID_ROOT/framework
-    core_locations_dir=$ANDROID_ROOT/framework
-    prefix=$PWD/
-    if [[ ${core_locations_dir:0:${#prefix}} = $prefix ]]; then
-      core_locations_dir="${core_locations_dir##$prefix}"
-    fi
-  elif [[ -e $ANDROID_ROOT/apex/com.android.art.debug/javalib/core-oj.jar ]]; then
-    core_jars_suffix=
-    core_filenames_dir=$ANDROID_ROOT/apex/com.android.art.debug/javalib
-    core_locations_dir=/apex/com.android.art/javalib
+  local modules="core-oj core-libart okhttp bouncycastle apache-xml core-icu4j conscrypt"
+  local prefix="$1"
+  local result=""
+  local separator=""
+  for module in ${modules}; do
+    case "$module" in
+      (conscrypt)  local apex="com.android.conscrypt";;
+      (core-icu4j) local apex="com.android.i18n";;
+      (*)          local apex="com.android.art";;
+    esac
+    result+="${separator}${prefix}/apex/${apex}/javalib/${module}.jar"
+    separator=":"
+  done
+  echo "$result"
+}
+
+# Create default boot class path if none was provided.
+if [[ "$DEX2OAT_BCP" = "" ]]; then
+  ANDROID_ROOT_MINUS_PWD="${ANDROID_ROOT#$PWD/}"  # For example: out/host/linux-x86
+  if [[ "$ANDROID_ROOT_MINUS_PWD" == */host/* ]]; then
+    DEX2OAT_BCP="$(get_boot_class_path $ANDROID_ROOT)"
+    DEX2OAT_BCP_LOCS="$(get_boot_class_path $ANDROID_ROOT_MINUS_PWD)"
+  elif [[ "$ANDROID_ROOT_MINUS_PWD" == */target/* ]]; then
+    DEX2OAT_BCP="$(get_boot_class_path $ANDROID_ROOT)"
+    DEX2OAT_BCP_LOCS="$(get_boot_class_path)"
   else
-    echo "Can not find jar files for boot image $DEX2OAT_BOOT_IMAGE"
+    echo "Can not determine whether are running on host or target"
     exit 1
   fi
-  if [[ $core_locations_dir != "" ]]; then
-    boot_separator=""
-    for boot_module in ${core_jars_list}; do
-      DEX_FILENAME="$boot_module$core_jars_suffix.jar"
-      DEX2OAT_BCP+="$boot_separator$core_filenames_dir/${DEX_FILENAME}"
-      DEX2OAT_BCP_LOCS+="$boot_separator$core_locations_dir/${DEX_FILENAME}"
-      boot_separator=":"
-    done
-    if [ "$VERBOSE" = "yes" ]; then
-      echo "Using predefined -Xbootclasspath for image $DEX2OAT_BOOT_IMAGE:"
-      echo DEX2OAT_BOOT_IMAGE=$DEX2OAT_BOOT_IMAGE
-      echo DEX2OAT_BCP=$DEX2OAT_BCP
-      echo DEX2OAT_BCP_LOCS=$DEX2OAT_BCP_LOCS
-    fi
+  if [ "$VERBOSE" = "yes" ]; then
+    echo ANDROID_ROOT=$ANDROID_ROOT
+    echo DEX2OAT_BOOT_IMAGE=$DEX2OAT_BOOT_IMAGE
+    echo DEX2OAT_BCP=$DEX2OAT_BCP
+    echo DEX2OAT_BCP_LOCS=$DEX2OAT_BCP_LOCS
   fi
 fi
 
diff --git a/tools/bootjars.sh b/tools/bootjars.sh
index bb840b0..d6882a5 100755
--- a/tools/bootjars.sh
+++ b/tools/bootjars.sh
@@ -75,15 +75,19 @@
   # Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
   # because that's what we use for compiling the core.art image.
   # It may contain additional modules from TEST_CORE_JARS.
-  core_jars_list="core-oj core-libart core-icu4j okhttp bouncycastle apache-xml"
-  core_jars_suffix=
-  if [[ $mode == host ]]; then
-    core_jars_suffix=-hostdex
-  fi
+  core_jars_list="core-oj core-libart okhttp bouncycastle apache-xml core-icu4j"
   boot_jars_list=""
   boot_separator=""
   for boot_module in ${core_jars_list}; do
-    boot_jars_list+="${boot_separator}${boot_module}${core_jars_suffix}"
+    jar_suffix=
+    if [[ $mode == host ]]; then
+      if [[ $boot_module == core-icu4j ]]; then
+        jar_suffix="-host-hostdex"
+      else
+        jar_suffix="-hostdex"
+      fi
+    fi
+    boot_jars_list+="${boot_separator}${boot_module}${jar_suffix}"
     boot_separator=" "
   done
 else
@@ -108,6 +112,8 @@
   for jar in $boot_jars_list; do
     if [[ $jar == "conscrypt" ]]; then
       echo "$intermediates_dir/JAVA_LIBRARIES/${jar}.com.android.conscrypt_intermediates/classes.jar"
+    elif [[ $jar == "core-icu4j" ]]; then
+      echo "$intermediates_dir/JAVA_LIBRARIES/${jar}.com.android.i18n_intermediates/classes.jar"
     else
       echo "$intermediates_dir/JAVA_LIBRARIES/${jar}.com.android.art.testing_intermediates/classes.jar"
     fi
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 56b4a23..dde156d 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -63,7 +63,15 @@
 done
 
 # Allow to build successfully in master-art.
-extra_args="SOONG_ALLOW_MISSING_DEPENDENCIES=true TEMPORARY_DISABLE_PATH_RESTRICTIONS=true"
+extra_args="SOONG_ALLOW_MISSING_DEPENDENCIES=true"
+
+apexes=(
+  "com.android.art.testing"
+  "com.android.conscrypt"
+  "com.android.i18n"
+  "com.android.runtime"
+  "com.android.tzdata"
+)
 
 if [[ $mode == "host" ]]; then
   make_command="build/soong/soong_ui.bash --make-mode $j_arg $extra_args $showcommands build-art-host-tests $common_targets"
@@ -75,36 +83,29 @@
     exit 1
   fi
   make_command="build/soong/soong_ui.bash --make-mode $j_arg $extra_args $showcommands build-art-target-tests $common_targets"
-  make_command+=" libnetd_client-target toybox toolbox sh"
+  make_command+=" libnetd_client-target toybox sh"
   make_command+=" debuggerd su gdbserver"
   make_command+=" libstdc++ "
   # vogar requires the class files for conscrypt.
   make_command+=" conscrypt "
   make_command+=" ${ANDROID_PRODUCT_OUT#"${ANDROID_BUILD_TOP}/"}/system/etc/public.libraries.txt"
-  if [[ -n "$ART_TEST_CHROOT" ]]; then
-    # Targets required to generate a linker configuration on device within the
-    # chroot environment.
-    make_command+=" linkerconfig"
-    # Additional targets needed for the chroot environment.
-    make_command+=" crash_dump event-log-tags"
-  fi
+  # Targets required to generate a linker configuration for device within the
+  # chroot environment. The *.libraries.txt targets are required by
+  # linkerconfig but not included in host_linkerconfig_all_targets. We cannot
+  # use linkerconfig, because building the device binary statically might not
+  # work in an unbundled tree.
+  make_command+=" host_linkerconfig_all_targets sanitizer.libraries.txt vndkcorevariant.libraries.txt"
+  # Additional targets needed for the chroot environment.
+  make_command+=" crash_dump event-log-tags"
   # Needed to extract prebuilts apexes.
   make_command+=" deapexer "
-  # Build the Testing ART APEX (which is a superset of the Release and Debug ART APEXes).
-  make_command+=" com.android.art.testing"
-  # Build the Runtime (Bionic) APEX.
-  make_command+=" com.android.runtime"
   # Build the bootstrap Bionic artifacts links (linker, libc, libdl, libm).
   # These targets create these symlinks:
   # - from /system/bin/linker(64) to /apex/com.android.runtime/bin/linker(64); and
   # - from /system/lib(64)/$lib to /apex/com.android.runtime/lib(64)/$lib.
   make_command+=" linker libc.bootstrap libdl.bootstrap libdl_android.bootstrap libm.bootstrap"
-  # Build the Conscrypt APEX.
-  make_command+=" com.android.conscrypt"
-  # Build the i18n APEX.
-  make_command+=" com.android.i18n"
-  # Build the Time Zone Data APEX.
-  make_command+=" com.android.tzdata"
+  # Build/install the required APEXes.
+  make_command+=" ${apexes[*]}"
 fi
 
 mode_specific_libraries="libjavacoretests libjdwp libwrapagentproperties libwrapagentpropertiesd"
@@ -118,6 +119,24 @@
 eval "$make_command"
 
 if [[ $mode == "target" ]]; then
+  if [[ -z "${ANDROID_HOST_OUT}" ]]; then
+    echo "ANDROID_HOST_OUT environment variable is empty; using $out_dir/host/linux-x86"
+    ANDROID_HOST_OUT=$out_dir/host/linux-x86
+  fi
+
+  # Extract prebuilt APEXes.
+  debugfs=$ANDROID_HOST_OUT/bin/debugfs_static
+  for apex in ${apexes[@]}; do
+    dir="$ANDROID_PRODUCT_OUT/system/apex/${apex}"
+    file="$ANDROID_PRODUCT_OUT/system/apex/${apex}.apex"
+    if [ -f "${file}" ]; then
+      echo "Extracting APEX file: ${apex}"
+      rm -rf $dir
+      mkdir -p $dir
+      $ANDROID_HOST_OUT/bin/deapexer --debugfs_path $debugfs extract $file $dir
+    fi
+  done
+
   # Create canonical name -> file name symlink in the symbol directory for the
   # Testing ART APEX.
   #
@@ -143,24 +162,8 @@
     eval "$cmd"
   done
 
-
-  conscrypt_dir="$ANDROID_PRODUCT_OUT/system/apex/com.android.conscrypt"
-  conscrypt_apex="$ANDROID_PRODUCT_OUT/system/apex/com.android.conscrypt.apex"
-  if [ -f "${conscrypt_apex}" ]; then
-    # If there is a conscrypt apex prebuilt, extract it.
-    rm -rf $conscrypt_dir
-    mkdir $conscrypt_dir
-    if [[ -z "${ANDROID_HOST_OUT}" ]]; then
-      echo "ANDROID_HOST_OUT environment variable is empty; using $out_dir/host/linux-x86"
-      ANDROID_HOST_OUT=$out_dir/host/linux-x86
-    fi
-    echo -e "Listing contents of the conscrypt apex"
-    ls -l $conscrypt_apex
-    debugfs=$ANDROID_HOST_OUT/bin/debugfs_static
-    $ANDROID_HOST_OUT/bin/deapexer --debugfs_path $debugfs list $conscrypt_apex
-    $ANDROID_HOST_OUT/bin/deapexer --debugfs_path $debugfs extract $conscrypt_apex $conscrypt_dir
-  fi
   # Temporary fix for libjavacrypto.so dependencies in libcore and jvmti tests (b/147124225).
+  conscrypt_dir="$ANDROID_PRODUCT_OUT/system/apex/com.android.conscrypt"
   conscrypt_libs="libjavacrypto.so libcrypto.so libssl.so"
   if [ ! -d "${conscrypt_dir}" ]; then
     echo -e "Missing conscrypt APEX in build output: ${conscrypt_dir}"
@@ -184,4 +187,29 @@
       fi
     done
   done
+
+  # Create linker config files. We run linkerconfig on host to avoid problems
+  # building it statically for device in an unbundled tree.
+
+  # For linkerconfig to pick up the APEXes correctly we need to make them
+  # available in $ANDROID_PRODUCT_OUT/apex.
+  mkdir -p $ANDROID_PRODUCT_OUT/apex
+  for apex in ${apexes[@]}; do
+    src="$ANDROID_PRODUCT_OUT/system/apex/${apex}"
+    if [[ $apex == com.android.art.* ]]; then
+      dst="$ANDROID_PRODUCT_OUT/apex/com.android.art"
+    else
+      dst="$ANDROID_PRODUCT_OUT/apex/${apex}"
+    fi
+    echo "Copying APEX directory from $src to $dst"
+    rm -rf $dst
+    cp -r $src $dst
+  done
+
+  platform_version=$(build/soong/soong_ui.bash --dumpvar-mode PLATFORM_VERSION)
+  linkerconfig_root=$ANDROID_PRODUCT_OUT/linkerconfig
+  echo "Generating linkerconfig in $linkerconfig_root"
+  rm -rf $linkerconfig_root
+  mkdir -p $linkerconfig_root
+  $ANDROID_HOST_OUT/bin/linkerconfig --target $linkerconfig_root --root $ANDROID_PRODUCT_OUT --vndk $platform_version
 fi
diff --git a/tools/buildbot-sync.sh b/tools/buildbot-sync.sh
index 8bf6885..55bd3b7 100755
--- a/tools/buildbot-sync.sh
+++ b/tools/buildbot-sync.sh
@@ -54,18 +54,28 @@
 fi
 
 
-# `/system` "partition" synchronization.
-# --------------------------------------
+# Sync relevant product directories
+# ---------------------------------
 
-# Sync the system directory to the chroot.
-echo -e "${green}Syncing system directory...${nc}"
-adb shell mkdir -p "$ART_TEST_CHROOT/system"
-adb push "$ANDROID_PRODUCT_OUT/system" "$ART_TEST_CHROOT/"
+sync_dir() {
+  local dir=${1}
+  echo -e "${green}Syncing $dir directory...${nc}"
+  adb shell mkdir -p "$ART_TEST_CHROOT/$dir"
+  adb push "$ANDROID_PRODUCT_OUT/$dir" "$ART_TEST_CHROOT/"
+}
+
+sync_dir system
+sync_dir linkerconfig
+sync_dir data
+
 # Overwrite the default public.libraries.txt file with a smaller one that
 # contains only the public libraries pushed to the chroot directory.
 adb push "$ANDROID_BUILD_TOP/art/tools/public.libraries.buildbot.txt" \
   "$ART_TEST_CHROOT/system/etc/public.libraries.txt"
 
+# Create the framework directory if it doesn't exist. Some gtests need it.
+adb shell mkdir -p "$ART_TEST_CHROOT/system/framework"
+
 
 # APEX packages activation.
 # -------------------------
@@ -98,35 +108,3 @@
 activate_apex com.android.runtime
 activate_apex com.android.tzdata
 activate_apex com.android.conscrypt
-
-
-# Linker configuration.
-# ---------------------
-
-# Statically linked `linkerconfig` binary.
-linkerconfig_binary="/system/bin/linkerconfig"
-# Generated linker configuration file path (since Android R).
-ld_generated_config_file_path="/linkerconfig/ld.config.txt"
-# Location of the generated linker configuration file.
-ld_generated_config_file_location=$(dirname "$ld_generated_config_file_path")
-
-# Generate linker configuration files on device.
-echo -e "${green}Generating linker configuration files on device in" \
-  "\`$ld_generated_config_file_path\`${nc}..."
-adb shell chroot "$ART_TEST_CHROOT" \
-  "$linkerconfig_binary" --target "$ld_generated_config_file_location" || exit 1
-ld_generated_config_files=$(adb shell find $ART_TEST_CHROOT/linkerconfig ! -type d | sed 's/^/  /')
-echo -e "${green}Generated linker configuration files on device:${nc}"
-echo -e "${green}$ld_generated_config_files${nc}"
-
-
-# `/data` "partition" synchronization.
-# ------------------------------------
-
-# Sync the data directory to the chroot.
-echo -e "${green}Syncing data directory...${nc}"
-adb shell mkdir -p "$ART_TEST_CHROOT/data"
-adb push "$ANDROID_PRODUCT_OUT/data" "$ART_TEST_CHROOT/"
-
-# Create the framework directory if it doesn't exist. Some gtests need it.
-adb shell mkdir -p "$ART_TEST_CHROOT/system/framework"
diff --git a/tools/checker/README b/tools/checker/README
index 8a6b128..e5b0211 100644
--- a/tools/checker/README
+++ b/tools/checker/README
@@ -1,20 +1,20 @@
 Checker is a testing tool which compiles a given test file and compares the
 state of the control-flow graph before and after each optimization pass
-against a set of assertions specified alongside the tests.
+against a set of statements specified alongside the tests.
 
 Tests are written in Java or Smali, turned into DEX and compiled with the
-Optimizing compiler. "Check lines" are assertions formatted as comments of the
+Optimizing compiler. "Check lines" are statements formatted as comments of the
 source file. They begin with prefix "/// CHECK" or "## CHECK", respectively,
 followed by a pattern that the engine attempts to match in the compiler output.
 
-Assertions are tested in groups which correspond to the individual compiler
+Statements are tested in groups which correspond to the individual compiler
 passes. Each group of check lines therefore must start with a 'CHECK-START'
 header which specifies the output group it should be tested against. The group
 name must exactly match one of the groups recognized in the output (they can
 be listed with the '--list-passes' command-line flag).
 
 Matching of check lines is carried out in the order of appearance in the
-source file. There are three types of check lines:
+source file. There are five types of check lines:
  - CHECK:      Must match an output line which appears in the output group
                later than lines matched against any preceeding checks. Output
                lines must therefore match the check lines in the same order.
@@ -27,10 +27,10 @@
                later than lines matched against any preceeding checks and
                earlier than lines matched against any subsequent checks.
                Surrounding non-negative checks (or boundaries of the group)
-               therefore create a scope within which the assertion is verified.
+               therefore create a scope within which the statement is verified.
  - CHECK-NEXT: Must match the output line which comes right after the line which
-               matched the previous check. Cannot be used after any but the
-               in-order CHECK.
+               matched the previous check. Can only be used after a CHECK or
+               another CHECK-NEXT.
  - CHECK-EVAL: Specifies a Python expression which must evaluate to 'True'.
 
 Check-line patterns are treated as plain text rather than regular expressions
@@ -47,7 +47,7 @@
 be redefined or used undefined.
 
 Example:
-  The following assertions can be placed in a Java source file:
+  The following statements can be placed in a Java source file:
 
   /// CHECK-START: int MyClass.MyMethod() constant_folding (after)
   /// CHECK:         <<ID:i\d+>>  IntConstant {{11|22}}
diff --git a/tools/checker/common/logger.py b/tools/checker/common/logger.py
index f13eaf6..15eb55c 100644
--- a/tools/checker/common/logger.py
+++ b/tools/checker/common/logger.py
@@ -94,6 +94,6 @@
     Logger.log("PASS", color=Logger.Color.Blue)
 
   @staticmethod
-  def testFailed(msg, assertion, variables):
+  def testFailed(msg, statement, variables):
     Logger.log("FAIL", color=Logger.Color.Red)
-    Logger.fail(msg, assertion.fileName, assertion.lineNo, assertion.originalText, variables)
+    Logger.fail(msg, statement.fileName, statement.lineNo, statement.originalText, variables)
diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py
index 7a5a4c8..a1c8523 100644
--- a/tools/checker/file_format/checker/parser.py
+++ b/tools/checker/file_format/checker/parser.py
@@ -15,7 +15,7 @@
 from common.archs               import archs_list
 from common.logger              import Logger
 from file_format.common         import SplitStream
-from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, TestExpression
+from file_format.checker.struct import CheckerFile, TestCase, TestStatement, TestExpression
 
 import re
 
@@ -93,29 +93,29 @@
   # Lines starting only with 'CHECK' are matched in order.
   plainLine = __extractLine(prefix, line)
   if plainLine is not None:
-    return (plainLine, TestAssertion.Variant.InOrder, lineNo), None, None
+    return (plainLine, TestStatement.Variant.InOrder, lineNo), None, None
 
   # 'CHECK-NEXT' lines are in-order but must match the very next line.
   nextLine = __extractLine(prefix + "-NEXT", line)
   if nextLine is not None:
-    return (nextLine, TestAssertion.Variant.NextLine, lineNo), None, None
+    return (nextLine, TestStatement.Variant.NextLine, lineNo), None, None
 
-  # 'CHECK-DAG' lines are no-order assertions.
+  # 'CHECK-DAG' lines are no-order statements.
   dagLine = __extractLine(prefix + "-DAG", line)
   if dagLine is not None:
-    return (dagLine, TestAssertion.Variant.DAG, lineNo), None, None
+    return (dagLine, TestStatement.Variant.DAG, lineNo), None, None
 
-  # 'CHECK-NOT' lines are no-order negative assertions.
+  # 'CHECK-NOT' lines are no-order negative statements.
   notLine = __extractLine(prefix + "-NOT", line)
   if notLine is not None:
-    return (notLine, TestAssertion.Variant.Not, lineNo), None, None
+    return (notLine, TestStatement.Variant.Not, lineNo), None, None
 
   # 'CHECK-EVAL' lines evaluate a Python expression.
   evalLine = __extractLine(prefix + "-EVAL", line)
   if evalLine is not None:
-    return (evalLine, TestAssertion.Variant.Eval, lineNo), None, None
+    return (evalLine, TestStatement.Variant.Eval, lineNo), None, None
 
-  Logger.fail("Checker assertion could not be parsed: '" + line + "'", fileName, lineNo)
+  Logger.fail("Checker statement could not be parsed: '" + line + "'", fileName, lineNo)
 
 def __isMatchAtStart(match):
   """ Tests if the given Match occurred at the beginning of the line. """
@@ -129,12 +129,12 @@
   starts = map(lambda m: len(string) if m is None else m.start(), matches)
   return min(starts)
 
-def ParseCheckerAssertion(parent, line, variant, lineNo):
+def ParseCheckerStatement(parent, line, variant, lineNo):
   """ This method parses the content of a check line stripped of the initial
       comment symbol and the CHECK-* keyword.
   """
-  assertion = TestAssertion(parent, variant, line, lineNo)
-  isEvalLine = (variant == TestAssertion.Variant.Eval)
+  statement = TestStatement(parent, variant, line, lineNo)
+  isEvalLine = (variant == TestStatement.Variant.Eval)
 
   # Loop as long as there is something to parse.
   while line:
@@ -156,24 +156,24 @@
       # A whitespace in the check line creates a new separator of line parts.
       # This allows for ignored output between the previous and next parts.
       line = line[matchWhitespace.end():]
-      assertion.addExpression(TestExpression.createSeparator())
+      statement.addExpression(TestExpression.createSeparator())
     elif __isMatchAtStart(matchPattern):
       pattern = line[0:matchPattern.end()]
       pattern = pattern[2:-2]
       line = line[matchPattern.end():]
-      assertion.addExpression(TestExpression.createPattern(pattern))
+      statement.addExpression(TestExpression.createPattern(pattern))
     elif __isMatchAtStart(matchVariableReference):
       var = line[0:matchVariableReference.end()]
       line = line[matchVariableReference.end():]
       name = var[2:-2]
-      assertion.addExpression(TestExpression.createVariableReference(name))
+      statement.addExpression(TestExpression.createVariableReference(name))
     elif __isMatchAtStart(matchVariableDefinition):
       var = line[0:matchVariableDefinition.end()]
       line = line[matchVariableDefinition.end():]
       colonPos = var.find(":")
       name = var[2:colonPos]
       body = var[colonPos+1:-2]
-      assertion.addExpression(TestExpression.createVariableDefinition(name, body))
+      statement.addExpression(TestExpression.createVariableDefinition(name, body))
     else:
       # If we're not currently looking at a special marker, this is a plain
       # text match all the way until the first special marker (or the end
@@ -186,10 +186,10 @@
       text = line[0:firstMatch]
       line = line[firstMatch:]
       if isEvalLine:
-        assertion.addExpression(TestExpression.createPlainText(text))
+        statement.addExpression(TestExpression.createPlainText(text))
       else:
-        assertion.addExpression(TestExpression.createPatternFromPlainText(text))
-  return assertion
+        statement.addExpression(TestExpression.createPatternFromPlainText(text))
+  return statement
 
 def ParseCheckerStream(fileName, prefix, stream, targetArch = None):
   checkerFile = CheckerFile(fileName)
@@ -202,5 +202,5 @@
     forDebuggable = testData[1]
     testCase = TestCase(checkerFile, caseName, startLineNo, testArch, forDebuggable)
     for caseLine in caseLines:
-      ParseCheckerAssertion(testCase, caseLine[0], caseLine[1], caseLine[2])
+      ParseCheckerStatement(testCase, caseLine[0], caseLine[1], caseLine[2])
   return checkerFile
diff --git a/tools/checker/file_format/checker/struct.py b/tools/checker/file_format/checker/struct.py
index a31aa54..643beca 100644
--- a/tools/checker/file_format/checker/struct.py
+++ b/tools/checker/file_format/checker/struct.py
@@ -41,7 +41,7 @@
 
     self.parent = parent
     self.name = name
-    self.assertions = []
+    self.statements = []
     self.startLineNo = startLineNo
     self.testArch = testArch
     self.forDebuggable = forDebuggable
@@ -55,26 +55,26 @@
   def fileName(self):
     return self.parent.fileName
 
-  def addAssertion(self, new_assertion):
-    if new_assertion.variant == TestAssertion.Variant.NextLine:
-      if not self.assertions or \
-         (self.assertions[-1].variant != TestAssertion.Variant.InOrder and \
-          self.assertions[-1].variant != TestAssertion.Variant.NextLine):
-        Logger.fail("A next-line assertion can only be placed after an "
-                    "in-order assertion or another next-line assertion.",
-                    new_assertion.fileName, new_assertion.lineNo)
-    self.assertions.append(new_assertion)
+  def addStatement(self, new_statement):
+    if new_statement.variant == TestStatement.Variant.NextLine:
+      if not self.statements or \
+         (self.statements[-1].variant != TestStatement.Variant.InOrder and \
+          self.statements[-1].variant != TestStatement.Variant.NextLine):
+        Logger.fail("A next-line statement can only be placed after an "
+                    "in-order statement or another next-line statement.",
+                    new_statement.fileName, new_statement.lineNo)
+    self.statements.append(new_statement)
 
   def __eq__(self, other):
     return isinstance(other, self.__class__) \
        and self.name == other.name \
-       and self.assertions == other.assertions
+       and self.statements == other.statements
 
 
-class TestAssertion(PrintableMixin):
+class TestStatement(PrintableMixin):
 
   class Variant(object):
-    """Supported types of assertions."""
+    """Supported types of statements."""
     InOrder, NextLine, DAG, Not, Eval = range(5)
 
   def __init__(self, parent, variant, originalText, lineNo):
@@ -86,7 +86,7 @@
     self.lineNo = lineNo
     self.originalText = originalText
 
-    self.parent.addAssertion(self)
+    self.parent.addStatement(self)
 
   @property
   def fileName(self):
@@ -94,13 +94,13 @@
 
   def addExpression(self, new_expression):
     assert isinstance(new_expression, TestExpression)
-    if self.variant == TestAssertion.Variant.Not:
+    if self.variant == TestStatement.Variant.Not:
       if new_expression.variant == TestExpression.Variant.VarDef:
         Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo)
     self.expressions.append(new_expression)
 
   def toRegex(self):
-    """ Returns a regex pattern for this entire assertion. Only used in tests. """
+    """ Returns a regex pattern for this entire statement. Only used in tests. """
     regex = ""
     for expression in self.expressions:
       if expression.variant == TestExpression.Variant.Separator:
diff --git a/tools/checker/file_format/checker/test.py b/tools/checker/file_format/checker/test.py
index 579c190..4686f33 100644
--- a/tools/checker/file_format/checker/test.py
+++ b/tools/checker/file_format/checker/test.py
@@ -17,7 +17,7 @@
 from common.archs               import archs_list
 from common.testing             import ToUnicode
 from file_format.checker.parser import ParseCheckerStream
-from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, TestExpression
+from file_format.checker.struct import CheckerFile, TestCase, TestStatement, TestExpression
 
 import io
 import unittest
@@ -33,12 +33,12 @@
   def assertParses(self, string):
     checkFile = self.tryParse(string)
     self.assertEqual(len(checkFile.testCases), 1)
-    self.assertNotEqual(len(checkFile.testCases[0].assertions), 0)
+    self.assertNotEqual(len(checkFile.testCases[0].statements), 0)
 
   def assertIgnored(self, string):
     checkFile = self.tryParse(string)
     self.assertEqual(len(checkFile.testCases), 1)
-    self.assertEqual(len(checkFile.testCases[0].assertions), 0)
+    self.assertEqual(len(checkFile.testCases[0].statements), 0)
 
   def assertInvalid(self, string):
     with self.assertRaises(CheckerException):
@@ -75,22 +75,22 @@
 
 class CheckerParser_TestExpressionTest(unittest.TestCase):
 
-  def parseAssertion(self, string, variant=""):
+  def parseStatement(self, string, variant=""):
     checkerText = (u"/// CHECK-START: pass\n" +
                    u"/// CHECK" + ToUnicode(variant) + u": " + ToUnicode(string))
     checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText))
     self.assertEqual(len(checkerFile.testCases), 1)
     testCase = checkerFile.testCases[0]
-    self.assertEqual(len(testCase.assertions), 1)
-    return testCase.assertions[0]
+    self.assertEqual(len(testCase.statements), 1)
+    return testCase.statements[0]
 
   def parseExpression(self, string):
-    line = self.parseAssertion(string)
+    line = self.parseStatement(string)
     self.assertEqual(1, len(line.expressions))
     return line.expressions[0]
 
   def assertEqualsRegex(self, string, expected):
-    self.assertEqual(expected, self.parseAssertion(string).toRegex())
+    self.assertEqual(expected, self.parseStatement(string).toRegex())
 
   def assertEqualsText(self, string, text):
     self.assertEqual(self.parseExpression(string), TestExpression.createPatternFromPlainText(text))
@@ -185,7 +185,7 @@
 
   def test_NoVarDefsInNotChecks(self):
     with self.assertRaises(CheckerException):
-      self.parseAssertion("<<ABC:abc>>", "-NOT")
+      self.parseStatement("<<ABC:abc>>", "-NOT")
 
 
 class CheckerParser_FileLayoutTest(unittest.TestCase):
@@ -197,12 +197,12 @@
     for caseEntry in caseList:
       caseName = caseEntry[0]
       testCase = TestCase(testFile, caseName, 0)
-      assertionList = caseEntry[1]
-      for assertionEntry in assertionList:
-        content = assertionEntry[0]
-        variant = assertionEntry[1]
-        assertion = TestAssertion(testCase, variant, content, 0)
-        assertion.addExpression(TestExpression.createPatternFromPlainText(content))
+      statementList = caseEntry[1]
+      for statementEntry in statementList:
+        content = statementEntry[0]
+        variant = statementEntry[1]
+        statement = TestStatement(testCase, variant, content, 0)
+        statement.addExpression(TestExpression.createPatternFromPlainText(content))
     return testFile
 
   def assertParsesTo(self, checkerText, expectedData):
@@ -223,8 +223,8 @@
         /// CHECK:  foo
         /// CHECK:    bar
       """,
-      [ ( "Example Group", [ ("foo", TestAssertion.Variant.InOrder),
-                             ("bar", TestAssertion.Variant.InOrder) ] ) ])
+      [ ( "Example Group", [ ("foo", TestStatement.Variant.InOrder),
+                             ("bar", TestStatement.Variant.InOrder) ] ) ])
 
   def test_MultipleGroups(self):
     self.assertParsesTo(
@@ -236,12 +236,12 @@
         /// CHECK: abc
         /// CHECK: def
       """,
-      [ ( "Example Group1", [ ("foo", TestAssertion.Variant.InOrder),
-                              ("bar", TestAssertion.Variant.InOrder) ] ),
-        ( "Example Group2", [ ("abc", TestAssertion.Variant.InOrder),
-                              ("def", TestAssertion.Variant.InOrder) ] ) ])
+      [ ( "Example Group1", [ ("foo", TestStatement.Variant.InOrder),
+                              ("bar", TestStatement.Variant.InOrder) ] ),
+        ( "Example Group2", [ ("abc", TestStatement.Variant.InOrder),
+                              ("def", TestStatement.Variant.InOrder) ] ) ])
 
-  def test_AssertionVariants(self):
+  def test_StatementVariants(self):
     self.assertParsesTo(
       """
         /// CHECK-START: Example Group
@@ -253,13 +253,13 @@
         /// CHECK-DAG:  abc
         /// CHECK-DAG:  def
       """,
-      [ ( "Example Group", [ ("foo1", TestAssertion.Variant.InOrder),
-                             ("foo2", TestAssertion.Variant.InOrder),
-                             ("foo3", TestAssertion.Variant.NextLine),
-                             ("foo4", TestAssertion.Variant.NextLine),
-                             ("bar", TestAssertion.Variant.Not),
-                             ("abc", TestAssertion.Variant.DAG),
-                             ("def", TestAssertion.Variant.DAG) ] ) ])
+      [ ( "Example Group", [ ("foo1", TestStatement.Variant.InOrder),
+                             ("foo2", TestStatement.Variant.InOrder),
+                             ("foo3", TestStatement.Variant.NextLine),
+                             ("foo4", TestStatement.Variant.NextLine),
+                             ("bar", TestStatement.Variant.Not),
+                             ("abc", TestStatement.Variant.DAG),
+                             ("def", TestStatement.Variant.DAG) ] ) ])
 
   def test_MisplacedNext(self):
     with self.assertRaises(CheckerException):
@@ -315,7 +315,7 @@
     for arch in [None] + archs_list:
       checkerFile = self.parse(self.noarch_block)
       self.assertEqual(len(checkerFile.testCases), 1)
-      self.assertEqual(len(checkerFile.testCases[0].assertions), 4)
+      self.assertEqual(len(checkerFile.testCases[0].statements), 4)
 
   def test_IgnoreNonTargetArch(self):
     for targetArch in archs_list:
@@ -332,7 +332,7 @@
       checkerFile = self.parse(checkerText)
       self.assertEqual(len(checkerFile.testCases), 1)
       self.assertEqual(len(checkerFile.testCasesForArch(arch)), 1)
-      self.assertEqual(len(checkerFile.testCases[0].assertions), 4)
+      self.assertEqual(len(checkerFile.testCases[0].statements), 4)
 
   def test_NoDebugAndArch(self):
     testCase = self.parse("""
@@ -375,20 +375,20 @@
 
   def parseExpressions(self, string):
     testCase = self.parseTestCase("/// CHECK-EVAL: " + string)
-    self.assertEqual(len(testCase.assertions), 1)
-    assertion = testCase.assertions[0]
-    self.assertEqual(assertion.variant, TestAssertion.Variant.Eval)
-    self.assertEqual(assertion.originalText, string)
-    return assertion.expressions
+    self.assertEqual(len(testCase.statements), 1)
+    statement = testCase.statements[0]
+    self.assertEqual(statement.variant, TestStatement.Variant.Eval)
+    self.assertEqual(statement.originalText, string)
+    return statement.expressions
 
   def assertParsesToPlainText(self, text):
     testCase = self.parseTestCase("/// CHECK-EVAL: " + text)
-    self.assertEqual(len(testCase.assertions), 1)
-    assertion = testCase.assertions[0]
-    self.assertEqual(assertion.variant, TestAssertion.Variant.Eval)
-    self.assertEqual(assertion.originalText, text)
-    self.assertEqual(len(assertion.expressions), 1)
-    expression = assertion.expressions[0]
+    self.assertEqual(len(testCase.statements), 1)
+    statement = testCase.statements[0]
+    self.assertEqual(statement.variant, TestStatement.Variant.Eval)
+    self.assertEqual(statement.originalText, text)
+    self.assertEqual(len(statement.expressions), 1)
+    expression = statement.expressions[0]
     self.assertEqual(expression.variant, TestExpression.Variant.PlainText)
     self.assertEqual(expression.text, text)
 
diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py
index 3b2e67e..1db4737 100644
--- a/tools/checker/match/file.py
+++ b/tools/checker/match/file.py
@@ -16,149 +16,162 @@
 from common.immutables                import ImmutableDict
 from common.logger                    import Logger
 from file_format.c1visualizer.struct  import C1visualizerFile, C1visualizerPass
-from file_format.checker.struct       import CheckerFile, TestCase, TestAssertion
+from file_format.checker.struct       import CheckerFile, TestCase, TestStatement
 from match.line                       import MatchLines, EvaluateLine
 
 MatchScope = namedtuple("MatchScope", ["start", "end"])
 MatchInfo = namedtuple("MatchInfo", ["scope", "variables"])
 
 class MatchFailedException(Exception):
-  def __init__(self, assertion, lineNo, variables):
-    self.assertion = assertion
+  def __init__(self, statement, lineNo, variables):
+    self.statement = statement
     self.lineNo = lineNo
     self.variables = variables
 
-def splitIntoGroups(assertions):
-  """ Breaks up a list of assertions, grouping instructions which should be
-      tested in the same scope (consecutive DAG and NOT instructions).
-   """
-  splitAssertions = []
-  lastVariant = None
-  for assertion in assertions:
-    if (assertion.variant == lastVariant and
-        assertion.variant in [TestAssertion.Variant.DAG, TestAssertion.Variant.Not]):
-      splitAssertions[-1].append(assertion)
-    else:
-      splitAssertions.append([assertion])
-      lastVariant = assertion.variant
-  return splitAssertions
-
-def findMatchingLine(assertion, c1Pass, scope, variables, excludeLines=[]):
-  """ Finds the first line in `c1Pass` which matches `assertion`.
+def findMatchingLine(statement, c1Pass, scope, variables, excludeLines=[]):
+  """ Finds the first line in `c1Pass` which matches `statement`.
 
   Scan only lines numbered between `scope.start` and `scope.end` and not on the
   `excludeLines` list.
 
-  Returns the index of the `c1Pass` line matching the assertion and variables
+  Returns the index of the `c1Pass` line matching the statement and variables
   values after the match.
 
   Raises MatchFailedException if no such `c1Pass` line can be found.
   """
   for i in range(scope.start, scope.end):
     if i in excludeLines: continue
-    newVariables = MatchLines(assertion, c1Pass.body[i], variables)
+    newVariables = MatchLines(statement, c1Pass.body[i], variables)
     if newVariables is not None:
       return MatchInfo(MatchScope(i, i), newVariables)
-  raise MatchFailedException(assertion, scope.start, variables)
+  raise MatchFailedException(statement, scope.start, variables)
 
-def matchDagGroup(assertions, c1Pass, scope, variables):
-  """ Attempts to find matching `c1Pass` lines for a group of DAG assertions.
+class ExecutionState(object):
+  def __init__(self, c1Pass, variables={}):
+    self.cursor = 0
+    self.c1Pass = c1Pass
+    self.c1Length = len(c1Pass.body)
+    self.variables = ImmutableDict(variables)
+    self.dagQueue = []
+    self.notQueue = []
 
-  Assertions are matched in the list order and variable values propagated. Only
-  lines in `scope` are scanned and each line can only match one assertion.
+  def moveCursor(self, match):
+    assert self.cursor <= match.scope.end
 
-  Returns the range of `c1Pass` lines covered by this group (min/max of matching
-  line numbers) and the variable values after the match of the last assertion.
+    # Handle any pending NOT statements before moving the cursor
+    self.handleNotQueue(MatchScope(self.cursor, match.scope.start))
 
-  Raises MatchFailedException when an assertion cannot be satisfied.
-  """
-  matchedLines = []
-  for assertion in assertions:
-    assert assertion.variant == TestAssertion.Variant.DAG
-    match = findMatchingLine(assertion, c1Pass, scope, variables, matchedLines)
-    variables = match.variables
-    assert match.scope.start == match.scope.end
-    assert match.scope.start not in matchedLines
-    matchedLines.append(match.scope.start)
-  return MatchInfo(MatchScope(min(matchedLines), max(matchedLines)), variables)
+    self.cursor = match.scope.end + 1
+    self.variables = match.variables
 
-def testNotGroup(assertions, c1Pass, scope, variables):
-  """ Verifies that none of the given NOT assertions matches a line inside
-      the given `scope` of `c1Pass` lines.
+  def handleDagQueue(self, scope):
+    """ Attempts to find matching `c1Pass` lines for a group of DAG statements.
 
-  Raises MatchFailedException if an assertion matches a line in the scope.
-  """
-  for i in range(scope.start, scope.end):
-    line = c1Pass.body[i]
-    for assertion in assertions:
-      assert assertion.variant == TestAssertion.Variant.Not
-      if MatchLines(assertion, line, variables) is not None:
-        raise MatchFailedException(assertion, i, variables)
+    Statements are matched in the list order and variable values propagated. Only
+    lines in `scope` are scanned and each line can only match one statement.
 
-def testEvalGroup(assertions, scope, variables):
-  for assertion in assertions:
-    if not EvaluateLine(assertion, variables):
-      raise MatchFailedException(assertion, scope.start, variables)
+    Returns the range of `c1Pass` lines covered by this group (min/max of matching
+    line numbers) and the variable values after the match of the last statement.
+
+    Raises MatchFailedException when a statement cannot be satisfied.
+    """
+    if not self.dagQueue:
+      return
+
+    matchedLines = []
+    variables = self.variables
+
+    for statement in self.dagQueue:
+      assert statement.variant == TestStatement.Variant.DAG
+      match = findMatchingLine(statement, self.c1Pass, scope, variables, matchedLines)
+      variables = match.variables
+      assert match.scope.start == match.scope.end
+      assert match.scope.start not in matchedLines
+      matchedLines.append(match.scope.start)
+
+    match = MatchInfo(MatchScope(min(matchedLines), max(matchedLines)), variables)
+    self.dagQueue = []
+    self.moveCursor(match)
+
+  def handleNotQueue(self, scope):
+    """ Verifies that none of the given NOT statements matches a line inside
+        the given `scope` of `c1Pass` lines.
+
+    Raises MatchFailedException if a statement matches a line in the scope.
+    """
+    for statement in self.notQueue:
+      assert statement.variant == TestStatement.Variant.Not
+      for i in range(scope.start, scope.end):
+        if MatchLines(statement, self.c1Pass.body[i], self.variables) is not None:
+          raise MatchFailedException(statement, i, self.variables)
+    self.notQueue = []
+
+  def handleEOF(self):
+    """ EOF marker always moves the cursor to the end of the file."""
+    match = MatchInfo(MatchScope(self.c1Length, self.c1Length), None)
+    self.moveCursor(match)
+
+  def handleInOrder(self, statement):
+    """ Single in-order statement. Find the first line that matches and move
+        the cursor to the subsequent line.
+
+    Raises MatchFailedException if no such line can be found.
+    """
+    scope = MatchScope(self.cursor, self.c1Length)
+    match = findMatchingLine(statement, self.c1Pass, scope, self.variables)
+    self.moveCursor(match)
+
+  def handleNextLine(self, statement):
+    """ Single next-line statement. Test if the current line matches and move
+        the cursor to the next line if it does.
+
+    Raises MatchFailedException if the current line does not match.
+    """
+    scope = MatchScope(self.cursor, self.cursor + 1)
+    match = findMatchingLine(statement, self.c1Pass, scope, self.variables)
+    self.moveCursor(match)
+
+  def handleEval(self, statement):
+    """ Evaluates the statement in the current context.
+
+    Raises MatchFailedException if the expression evaluates to False.
+    """
+    if not EvaluateLine(statement, self.variables):
+      raise MatchFailedException(statement, self.cursor, self.variables)
+
+  def handle(self, statement):
+    variant = None if statement is None else statement.variant
+
+    # First non-DAG statement always triggers execution of any preceding
+    # DAG statements.
+    if variant is not TestStatement.Variant.DAG:
+      self.handleDagQueue(MatchScope(self.cursor, self.c1Length))
+
+    if variant is None:
+      self.handleEOF()
+    elif variant is TestStatement.Variant.InOrder:
+      self.handleInOrder(statement)
+    elif variant is TestStatement.Variant.NextLine:
+      self.handleNextLine(statement)
+    elif variant is TestStatement.Variant.DAG:
+      self.dagQueue.append(statement)
+    elif variant is TestStatement.Variant.Not:
+      self.notQueue.append(statement)
+    else:
+      assert variant is TestStatement.Variant.Eval
+      self.handleEval(statement)
 
 def MatchTestCase(testCase, c1Pass):
   """ Runs a test case against a C1visualizer graph dump.
 
-  Raises MatchFailedException when an assertion cannot be satisfied.
+  Raises MatchFailedException when a statement cannot be satisfied.
   """
   assert testCase.name == c1Pass.name
 
-  matchFrom = 0
-  variables = ImmutableDict()
-  c1Length = len(c1Pass.body)
-
-  # NOT assertions are verified retrospectively, once the scope is known.
-  pendingNotAssertions = None
-
-  # Prepare assertions by grouping those that are verified in the same scope.
-  # We also add None as an EOF assertion that will set scope for NOTs.
-  assertionGroups = splitIntoGroups(testCase.assertions)
-  assertionGroups.append(None)
-
-  for assertionGroup in assertionGroups:
-    if assertionGroup is None:
-      # EOF marker always matches the last+1 line of c1Pass.
-      match = MatchInfo(MatchScope(c1Length, c1Length), None)
-    elif assertionGroup[0].variant == TestAssertion.Variant.Not:
-      # NOT assertions will be tested together with the next group.
-      assert not pendingNotAssertions
-      pendingNotAssertions = assertionGroup
-      continue
-    elif assertionGroup[0].variant == TestAssertion.Variant.InOrder:
-      # Single in-order assertion. Find the first line that matches.
-      assert len(assertionGroup) == 1
-      scope = MatchScope(matchFrom, c1Length)
-      match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
-    elif assertionGroup[0].variant == TestAssertion.Variant.NextLine:
-      # Single next-line assertion. Test if the current line matches.
-      assert len(assertionGroup) == 1
-      scope = MatchScope(matchFrom, matchFrom + 1)
-      match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
-    elif assertionGroup[0].variant == TestAssertion.Variant.DAG:
-      # A group of DAG assertions. Match them all starting from the same point.
-      scope = MatchScope(matchFrom, c1Length)
-      match = matchDagGroup(assertionGroup, c1Pass, scope, variables)
-    else:
-      assert assertionGroup[0].variant == TestAssertion.Variant.Eval
-      scope = MatchScope(matchFrom, c1Length)
-      testEvalGroup(assertionGroup, scope, variables)
-      continue
-
-    if pendingNotAssertions:
-      # Previous group were NOT assertions. Make sure they don't match any lines
-      # in the [matchFrom, match.start) scope.
-      scope = MatchScope(matchFrom, match.scope.start)
-      testNotGroup(pendingNotAssertions, c1Pass, scope, variables)
-      pendingNotAssertions = None
-
-    # Update state.
-    assert matchFrom <= match.scope.end
-    matchFrom = match.scope.end + 1
-    variables = match.variables
+  state = ExecutionState(c1Pass)
+  testStatements = testCase.statements + [ None ]
+  for statement in testStatements:
+    state.handle(statement)
 
 def MatchFiles(checkerFile, c1File, targetArch, debuggableMode):
   for testCase in checkerFile.testCases:
@@ -183,11 +196,11 @@
       Logger.testPassed()
     except MatchFailedException as e:
       lineNo = c1Pass.startLineNo + e.lineNo
-      if e.assertion.variant == TestAssertion.Variant.Not:
-        msg = "NOT assertion matched line {}"
+      if e.statement.variant == TestStatement.Variant.Not:
+        msg = "NOT statement matched line {}"
       else:
-        msg = "Assertion could not be matched starting from line {}"
+        msg = "Statement could not be matched starting from line {}"
       msg = msg.format(lineNo)
       with file(c1File.fileName) as cfgFile:
         Logger.log(''.join(cfgFile), Logger.Level.Error)
-      Logger.testFailed(msg, e.assertion, e.variables)
+      Logger.testFailed(msg, e.statement, e.variables)
diff --git a/tools/checker/match/line.py b/tools/checker/match/line.py
index ed48a53..352762d 100644
--- a/tools/checker/match/line.py
+++ b/tools/checker/match/line.py
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 from common.logger              import Logger
-from file_format.checker.struct import TestExpression, TestAssertion
+from file_format.checker.struct import TestExpression, TestStatement
 
 import re
 
@@ -77,7 +77,7 @@
   """ Attempts to match a CHECK line against a string. Returns variable state
       after the match if successful and None otherwise.
   """
-  assert checkerLine.variant != TestAssertion.Variant.Eval
+  assert checkerLine.variant != TestStatement.Variant.Eval
 
   checkerWords = splitAtSeparators(checkerLine.expressions)
   stringWords = stringLine.split()
@@ -109,7 +109,7 @@
     return getVariable(expression.name, variables, pos)
 
 def EvaluateLine(checkerLine, variables):
-  assert checkerLine.variant == TestAssertion.Variant.Eval
+  assert checkerLine.variant == TestStatement.Variant.Eval
   eval_string = "".join(map(lambda expr: getEvalText(expr, variables, checkerLine),
                             checkerLine.expressions))
   return eval(eval_string)
diff --git a/tools/checker/match/test.py b/tools/checker/match/test.py
index 5144ca9..e0d25ef 100644
--- a/tools/checker/match/test.py
+++ b/tools/checker/match/test.py
@@ -16,8 +16,8 @@
 from common.testing                  import ToUnicode
 from file_format.c1visualizer.parser import ParseC1visualizerStream
 from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
-from file_format.checker.parser      import ParseCheckerStream, ParseCheckerAssertion
-from file_format.checker.struct      import CheckerFile, TestCase, TestAssertion
+from file_format.checker.parser      import ParseCheckerStream, ParseCheckerStatement
+from file_format.checker.struct      import CheckerFile, TestCase, TestStatement
 from match.file                      import MatchTestCase, MatchFailedException
 from match.line                      import MatchLines
 
@@ -28,13 +28,13 @@
 
 class MatchLines_Test(unittest.TestCase):
 
-  def createTestAssertion(self, checkerString):
+  def createTestStatement(self, checkerString):
     checkerFile = CheckerFile("<checker-file>")
     testCase = TestCase(checkerFile, "TestMethod TestPass", 0)
-    return ParseCheckerAssertion(testCase, checkerString, TestAssertion.Variant.InOrder, 0)
+    return ParseCheckerStatement(testCase, checkerString, TestStatement.Variant.InOrder, 0)
 
   def tryMatch(self, checkerString, c1String, varState={}):
-    return MatchLines(self.createTestAssertion(checkerString),
+    return MatchLines(self.createTestStatement(checkerString),
                       ToUnicode(c1String),
                       ImmutableDict(varState))
 
@@ -175,7 +175,7 @@
     self.assertDoesNotMatch("/// CHECK: b{{.}}r", "abc barX def")
     self.assertDoesNotMatch("/// CHECK: b{{.}}r", "abc b r def")
 
-  def test_InOrderAssertions(self):
+  def test_InOrderStatements(self):
     self.assertMatches(
     """
       /// CHECK: foo
@@ -195,7 +195,7 @@
       foo
     """)
 
-  def test_NextLineAssertions(self):
+  def test_NextLineStatements(self):
     self.assertMatches(
     """
       /// CHECK:      foo
@@ -243,7 +243,7 @@
       abc
     """)
 
-  def test_DagAssertions(self):
+  def test_DagStatements(self):
     self.assertMatches(
     """
       /// CHECK-DAG: foo
@@ -263,7 +263,7 @@
       foo
     """)
 
-  def test_DagAssertionsScope(self):
+  def test_DagStatementsScope(self):
     self.assertMatches(
     """
       /// CHECK:     foo
@@ -304,7 +304,7 @@
       abc
     """)
 
-  def test_NotAssertions(self):
+  def test_NotStatements(self):
     self.assertMatches(
     """
       /// CHECK-NOT: foo
@@ -331,7 +331,7 @@
       def bar
     """)
 
-  def test_NotAssertionsScope(self):
+  def test_NotStatementsScope(self):
     self.assertMatches(
     """
       /// CHECK:     abc
@@ -387,7 +387,7 @@
       bar
     """)
 
-  def test_EvalAssertions(self):
+  def test_EvalStatements(self):
     self.assertMatches("/// CHECK-EVAL: True", "foo")
     self.assertDoesNotMatch("/// CHECK-EVAL: False", "foo")
 
diff --git a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
index afdd692..1e9ddd2 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -52,6 +52,7 @@
     private static final String FLAG_GREYLIST_MAX_O = "greylist-max-o";
     private static final String FLAG_GREYLIST_MAX_P = "greylist-max-p";
     private static final String FLAG_GREYLIST_MAX_Q = "greylist-max-q";
+    private static final String FLAG_GREYLIST_MAX_R = "greylist-max-r";
 
     private static final String FLAG_PUBLIC_API = "public-api";
 
@@ -63,6 +64,7 @@
         map.put(26, FLAG_GREYLIST_MAX_O);
         map.put(28, FLAG_GREYLIST_MAX_P);
         map.put(29, FLAG_GREYLIST_MAX_Q);
+        map.put(30, FLAG_GREYLIST_MAX_R);
         TARGET_SDK_TO_LIST_MAP = Collections.unmodifiableMap(map);
     }
 
diff --git a/tools/compile-jar.sh b/tools/compile-jar.sh
index 5024ccc..aae2415 100755
--- a/tools/compile-jar.sh
+++ b/tools/compile-jar.sh
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 #
-# This script creates a boot image profile based on input profiles.
+# This script runs dex2oat on the host to compile a provided JAR or APK.
 #
 
 if [[ "$#" -lt 1 ]]; then
@@ -31,9 +31,10 @@
 ISA=$1
 shift
 
-dex2oat \
+# Use services.odex as a quick and dirty way to get correct BCP.
+dex2oatd \
     --runtime-arg -Xms64m --runtime-arg -Xmx512m \
-    --boot-image=${OUT}/apex/com.android.art/javalib/boot.art:${OUT}/system/framework/boot-framework.art \
+    --boot-image=${OUT}/apex/com.android.art.debug/javalib/boot.art:${OUT}/system/framework/boot-framework.art \
     $(${ANDROID_BUILD_TOP}/art/tools/host_bcp.sh ${OUT}/system/framework/oat/${ISA}/services.odex --use-first-dir) \
     --dex-file=${FILE} --dex-location=/system/framework/${FILE} \
     --oat-file=${OUTPUT} \
diff --git a/tools/cpp-define-generator/Android.bp b/tools/cpp-define-generator/Android.bp
index c0b236c..d6c7663 100644
--- a/tools/cpp-define-generator/Android.bp
+++ b/tools/cpp-define-generator/Android.bp
@@ -27,6 +27,7 @@
         "art_libartbase_headers", // For base/bit_utils.h
         "jni_headers",
         "libart_runtime_headers_ndk",
+        "libc_headers", // TODO(b/153662223): Clean this up.
         "libdexfile_all_headers", // For dex/modifiers.h
     ],
     // Produce text file rather than binary.
@@ -52,12 +53,22 @@
             enabled: false,
         },
     },
+
+    apex_available: [
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 cc_library_headers {
     name: "cpp-define-generator-definitions",
     host_supported: true,
     export_include_dirs: ["."],
+
+    apex_available: [
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 python_binary_host {
diff --git a/tools/dex2oat_wrapper b/tools/dex2oat_wrapper
new file mode 100644
index 0000000..5ee1aea
--- /dev/null
+++ b/tools/dex2oat_wrapper
@@ -0,0 +1,125 @@
+# Copyright (C) 2020 The Android Open 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.
+
+# This script is used on host and device. It uses a common subset
+# shell dialect that should work on the host (e.g. bash), and
+# Android (e.g. mksh).
+
+# The purpose of this script is to invoke dex2oat with the right
+# boot classpath and bootclasspath locations.
+
+function find_libdir() {
+  # Get the actual file, $1 is the BINARY_PATH and may be a symbolic link.
+  # Use realpath instead of readlink because Android does not have a readlink.
+  if [[ "$(realpath "$1")" == *dalvikvm64 ]]; then
+    echo "lib64"
+  else
+    echo "lib"
+  fi
+}
+
+# Follow all sym links to get the program name.
+if [[ -n "$BASH_SOURCE" ]]; then
+  PROG_NAME="$BASH_SOURCE"
+else
+  PROG_NAME="$0"
+fi
+while [ -h "$PROG_NAME" ]; do
+  # On Mac OS, readlink -f doesn't work.
+  PROG_NAME="$(readlink "$PROG_NAME")"
+done
+
+PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
+ANDROID_ROOT="$(cd $PROG_DIR/..; pwd -P)"
+LIBDIR="$(find_libdir $PROG_DIR)"
+LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
+
+declare -a args=("$@")
+arg_idx=0
+while true; do
+  if [[ $1 == "-Xbootclasspath:*" ]]; then
+    DEX2OAT_BCP=$1
+    # Remove '-Xbootclasspath:' from the arguments.
+    DEX2OAT_BCP=${DEX2OAT_BCP##-Xbootclasspath:}
+    unset args[arg_idx]
+    shift
+  elif [[ $1 == "-Xbootclasspath-locations:*" ]]; then
+    DEX2OAT_BCP_LOCS=$1
+    # Remove '-Xbootclasspath-locations:' from the argument.
+    DEX2OAT_BCP_LOCS=${DEX2OAT_BCP_LOCS##-Xbootclasspath-locations:}
+    unset args[arg_idx]
+    shift
+  elif [[ $1 == "--32" ]]; then
+    DEX2OAT_SUFFIX=32
+    unset args[arg_idx]
+    shift
+  elif [[ $1 == "--64" ]]; then
+    DEX2OAT_SUFFIX=64
+    unset args[arg_idx]
+    shift
+  elif [[ "$1" == "" ]]; then
+    break
+  else
+    shift
+  fi
+  arg_idx=$((arg_idx + 1))
+done
+
+# Create boot class path filename or location list.
+# It takes one optional argument which is the prefix to be inserted before each entry.
+function get_boot_class_path() {
+  # Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
+  local modules="core-oj core-libart okhttp bouncycastle apache-xml core-icu4j conscrypt"
+  local prefix="$1"
+  local result=""
+  local separator=""
+  for module in ${modules}; do
+    case "$module" in
+      (conscrypt)  local apex="com.android.conscrypt";;
+      (core-icu4j) local apex="com.android.i18n";;
+      (*)          local apex="com.android.art";;
+    esac
+    result+="${separator}${prefix}/apex/${apex}/javalib/${module}.jar"
+    separator=":"
+  done
+  echo "$result"
+}
+
+# Create default boot class path if none was provided.
+if [[ "$DEX2OAT_BCP" = "" ]]; then
+  ANDROID_ROOT_MINUS_PWD="${ANDROID_ROOT#$PWD/}"  # For example: out/host/linux-x86
+  if [[ "$ANDROID_ROOT_MINUS_PWD" == */host/* ]]; then
+    DEX2OAT_BCP="$(get_boot_class_path $ANDROID_ROOT)"
+    DEX2OAT_BCP_LOCS="$(get_boot_class_path $ANDROID_ROOT_MINUS_PWD)"
+  elif [[ "$ANDROID_ROOT_MINUS_PWD" == */target/* ]]; then
+    DEX2OAT_BCP="$(get_boot_class_path $ANDROID_ROOT)"
+    DEX2OAT_BCP_LOCS="$(get_boot_class_path)"
+  else
+    echo "Can not determine whether are running on host or target"
+    exit 1
+  fi
+fi
+
+# If the dex2oat binary with the bitness as a suffix doesn't exist,
+# try with a dex2oat without suffix.
+if [[ ! -f $ANDROID_ROOT/bin/dex2oat${DEX2OAT_SUFFIX} ]]; then
+  DEX2OAT_SUFFIX=""
+fi
+
+LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
+  $ANDROID_ROOT/bin/dex2oat${DEX2OAT_SUFFIX} \
+    --android-root=$ANDROID_ROOT \
+    --runtime-arg -Xbootclasspath:$DEX2OAT_BCP \
+    --runtime-arg -Xbootclasspath-locations:$DEX2OAT_BCP_LOCS \
+    ${args[@]}
diff --git a/tools/hiddenapi/Android.bp b/tools/hiddenapi/Android.bp
index 9ddaf7d..4e89896 100644
--- a/tools/hiddenapi/Android.bp
+++ b/tools/hiddenapi/Android.bp
@@ -72,4 +72,5 @@
         "art_gtest_defaults",
     ],
     srcs: ["hiddenapi_test.cc"],
+    required: ["hiddenapid"],
 }
diff --git a/tools/libcore_gcstress_failures.txt b/tools/libcore_gcstress_failures.txt
index f0eb68e..f750eab 100644
--- a/tools/libcore_gcstress_failures.txt
+++ b/tools/libcore_gcstress_failures.txt
@@ -36,5 +36,15 @@
           "org.apache.harmony.tests.java.text.DateFormatTest#test_getAvailableLocales",
           "org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
           "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"]
+},
+{
+  description: "Timeouts.",
+  result: EXEC_FAILED,
+  bug: 157520256,
+  modes: [device],
+  names: ["jsr166.ForkJoinPoolTest#testIsQuiescent",
+          "libcore.java.util.CalendarTest#testAllDefaultCalendar_Gregorian",
+          "org.apache.harmony.tests.java.util.regex.MatcherTest#testAllCodePoints_P",
+          "org.apache.harmony.tests.java.util.regex.MatcherTest#testAllCodePoints_p"]
 }
 ]
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index f67e46d..dd551bc 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -42,6 +42,8 @@
   do
     if [ "$var" = "conscrypt" ] && [ "$mode" = "target" ]; then
       printf -- "${separator}/apex/com.android.conscrypt/javalib/conscrypt.jar";
+    elif [ "$var" = "core-icu4j" ] && [ "$mode" = "target" ]; then
+      printf -- "${separator}/apex/com.android.i18n/javalib/core-icu4j.jar";
     else
       printf -- "${separator}${dir}/${var}${suffix}.jar";
     fi
@@ -52,7 +54,7 @@
 # Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
 # because that's what we use for compiling the core.art image.
 # It may contain additional modules from TEST_CORE_JARS.
-BOOT_CLASSPATH_JARS="core-oj core-libart core-icu4j okhttp bouncycastle apache-xml conscrypt"
+BOOT_CLASSPATH_JARS="core-oj core-libart okhttp bouncycastle apache-xml core-icu4j conscrypt"
 
 vm_args=""
 art="$android_root/bin/art"
@@ -70,7 +72,7 @@
 debug="no"
 explicit_debug="no"
 verbose="no"
-image="-Ximage:/data/art-test/core.art"
+image="-Ximage:/apex/com.android.art/javalib/boot.art"
 boot_classpath="$(boot_classpath_arg /apex/com.android.art/javalib "" $BOOT_CLASSPATH_JARS)"
 boot_classpath_locations=""
 with_jdwp_path=""
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 7140b13..c6731b5 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -52,7 +52,7 @@
   do
     printf -- ":${dir}/${var}.jar";
   done
-  printf -- ":/apex/com.android.conscrypt/javalib/conscrypt.jar";
+  printf -- ":/apex/com.android.i18n/javalib/core-icu4j.jar:/apex/com.android.conscrypt/javalib/conscrypt.jar";
 }
 
 function usage {
@@ -147,7 +147,7 @@
 # Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
 # because that's what we use for compiling the core.art image.
 # It may contain additional modules from TEST_CORE_JARS.
-BOOT_CLASSPATH_JARS="core-oj core-libart core-icu4j okhttp bouncycastle apache-xml"
+BOOT_CLASSPATH_JARS="core-oj core-libart okhttp bouncycastle apache-xml"
 
 DEPS="core-tests jsr166-tests mockito-target"
 
@@ -184,7 +184,7 @@
   case "$1" in
     --mode=device)
       vogar_args="$vogar_args --mode=device"
-      vogar_args="$vogar_args --vm-arg -Ximage:/data/art-test/core.art"
+      vogar_args="$vogar_args --vm-arg -Ximage:/apex/com.android.art/javalib/boot.art"
       vogar_args="$vogar_args $(boot_classpath_arg /apex/com.android.art/javalib $BOOT_CLASSPATH_JARS)"
       execution_mode="device"
       ;;