Merge "Revert "ART: Add StackVisitor accepting a lambda""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index e2a0a39..e610bb1 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -163,6 +163,16 @@
 $(ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMulti_SRC) $(HOST_OUT_EXECUTABLES)/smali
 	 $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
 
+ART_TEST_GTEST_VerifySoftFailDuringClinit_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifySoftFailDuringClinit/*.smali))
+ART_TEST_HOST_GTEST_VerifySoftFailDuringClinit_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifySoftFailDuringClinit,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
+ART_TEST_TARGET_GTEST_VerifySoftFailDuringClinit_DEX := $(dir $(ART_TEST_TARGET_GTEST_Main_DEX))$(subst Main,VerifySoftFailDuringClinit,$(basename $(notdir $(ART_TEST_TARGET_GTEST_Main_DEX))))$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+
+$(ART_TEST_HOST_GTEST_VerifySoftFailDuringClinit_DEX): $(ART_TEST_GTEST_VerifySoftFailDuringClinit_SRC) $(HOST_OUT_EXECUTABLES)/smali
+	 $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
+
+$(ART_TEST_TARGET_GTEST_VerifySoftFailDuringClinit_DEX): $(ART_TEST_GTEST_VerifySoftFailDuringClinit_SRC) $(HOST_OUT_EXECUTABLES)/smali
+	 $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
+
 # Dex file dependencies for each gtest.
 ART_GTEST_art_dex_file_loader_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
 ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary MyClassNatives Nested VerifierDeps VerifierDepsMulti
@@ -180,7 +190,7 @@
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi
 ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures
-ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods
+ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods VerifySoftFailDuringClinit
 ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
 ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
@@ -752,5 +762,8 @@
 ART_TEST_GTEST_VerifierDeps_SRC :=
 ART_TEST_HOST_GTEST_VerifierDeps_DEX :=
 ART_TEST_TARGET_GTEST_VerifierDeps_DEX :=
+ART_TEST_GTEST_VerifySoftFailDuringClinit_SRC :=
+ART_TEST_HOST_GTEST_VerifySoftFailDuringClinit_DEX :=
+ART_TEST_TARGET_GTEST_VerifySoftFailDuringClinit_DEX :=
 GTEST_DEX_DIRECTORIES :=
 LOCAL_PATH :=
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
new file mode 100644
index 0000000..8bddb5d
--- /dev/null
+++ b/build/apex/Android.bp
@@ -0,0 +1,99 @@
+// Android Runtime APEX module.
+
+// Modules listed in LOCAL_REQUIRED_MODULES for module art-runtime in art/Android.mk.
+// - Base requirements (binaries for which both 32- and 64-bit versions are built, if relevant).
+art_runtime_base_binaries_both = [
+    "dalvikvm",
+]
+// - Base requirements (binaries for which a 32-bit version is preferred).
+art_runtime_base_binaries_prefer32 = [
+    "dex2oat",
+    "dexoptanalyzer",
+    "profman",
+]
+// - Base requirements (libraries).
+art_runtime_base_native_shared_libs = [
+    "libart",
+    "libart-compiler",
+    "libopenjdkjvm",
+    "libopenjdkjvmti",
+    "libadbconnection",
+]
+// - Fake library that avoids namespace issues and gives some warnings for nosy apps.
+art_runtime_fake_native_shared_libs = [
+     // FIXME: Does not work as-is, because `libart_fake` is defined in libart_fake/Android.mk,
+     // and because a module defined in a Blueprint file cannot depend on a module defined in a
+     // Makefile. To support `libart_fake` as a dependency of this APEX module, we can either
+     // (probably in that order of preference):
+     // a. translate that logic into Blueprint; or
+     // b. write the whole Android Runtime APEX generation logic in Android.mk; or
+     // c. introduce an `art_apex` module type extending the `apex` module type and write the
+     //    corresponding Go logic to handle this extra dependency.
+     //"libart_fake",
+]
+// - Debug variants (binaries for which a 32-bit version is preferred).
+//   FIXME: These modules are optional (the built product can decide to include them or not).
+//   Should they be moved to another APEX file?
+art_runtime_debug_binaries_prefer32 = [
+    "dex2oatd",
+    "dexoptanalyzerd",
+    "profmand",
+]
+art_runtime_debug_native_shared_libs = [
+    "libartd",
+    "libartd-compiler",
+    "libopenjdkd",
+    "libopenjdkjvmd",
+    "libopenjdkjvmtid",
+    "libadbconnectiond",
+]
+
+// Modules listed in LOCAL_REQUIRED_MODULES for module art-tools in art/Android.mk.
+art_tools_binaries = [
+    "dexdiag",
+    "dexdump",
+    "dexlist",
+    "oatdump",
+]
+
+// Host-only modules listed in LOCAL_REQUIRED_MODULES for module art-tools in art/Android.mk.
+// TODO: Include these modules in the future "host APEX".
+art_tools_host_binaries = [
+    // FIXME: Does not work as-is, because `ahat` is defined in tools/ahat/Android.mk
+    // (same issue as for `libart_fake` above).
+    //"ahat",
+    "hprof-conv",
+    // ...
+]
+
+apex_key {
+    name: "com.android.runtime.key",
+    public_key: "runtime.avbpubkey",
+    private_key: "runtime.pem",
+}
+
+apex {
+    name: "com.android.runtime",
+    compile_multilib: "both",
+    manifest: "manifest.json",
+    native_shared_libs: art_runtime_base_native_shared_libs
+        + art_runtime_fake_native_shared_libs
+        + art_runtime_debug_native_shared_libs,
+    multilib: {
+        both: {
+            // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
+            // (see `symlink_preferred_arch` in art/dalvikvm/Android.bp).
+            binaries: art_runtime_base_binaries_both,
+        },
+        prefer32: {
+            binaries: art_runtime_base_binaries_prefer32
+                + art_runtime_debug_binaries_prefer32
+        },
+        first: {
+            binaries: art_tools_binaries,
+        }
+    },
+    key: "com.android.runtime.key",
+    // TODO: Also package a `ld.config.txt` config file (to be placed in `etc/`).
+    // ...
+}
diff --git a/build/apex/manifest.json b/build/apex/manifest.json
new file mode 100644
index 0000000..20a8314
--- /dev/null
+++ b/build/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.runtime",
+  "version": 1
+}
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
new file mode 100755
index 0000000..6af2a8b
--- /dev/null
+++ b/build/apex/runtests.sh
@@ -0,0 +1,201 @@
+#!/bin/bash
+
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Run Android Runtime APEX tests.
+
+function say {
+  echo "$0: $*"
+}
+
+function die {
+  echo "$0: $*"
+  exit 1
+}
+
+which guestmount >/dev/null && which guestunmount >/dev/null && which virt-filesystems >/dev/null \
+  || die "This script requires 'guestmount', 'guestunmount',
+and 'virt-filesystems' from libguestfs. On Debian-based systems, these tools
+can be installed with:
+
+   sudo apt-get install libguestfs-tools
+"
+[[ -n "$ANDROID_PRODUCT_OUT" ]] \
+  || die "You need to source and lunch before you can use this script."
+
+# Fail early.
+set -e
+
+build_apex_p=true
+list_image_files_p=false
+
+function usage {
+  cat <<EOF
+Usage: $0 [OPTION]
+Build (optional) and run tests on Android Runtime APEX package (on host).
+
+  -s, --skip-build    skip the build step
+  -l, --list-files    list the contents of the ext4 image
+  -h, --help          display this help and exit
+
+EOF
+  exit
+}
+
+while [[ $# -gt 0 ]]; do
+  case "$1" in
+    (-s|--skip-build) build_apex_p=false;;
+    (-l|--list-files) list_image_files_p=true;;
+    (-h|--help) usage;;
+    (*) die "Unknown option: '$1'
+Try '$0 --help' for more information.";;
+  esac
+  shift
+done
+
+work_dir=$(mktemp -d)
+mount_point="$work_dir/image"
+
+# Garbage collection.
+function finish {
+  # Don't fail early during cleanup.
+  set +e
+  guestunmount "$mount_point"
+  rm -rf "$work_dir"
+}
+
+trap finish EXIT
+
+apex_module="com.android.runtime"
+
+# Build the Android Runtime APEX package (optional).
+$build_apex_p && say "Building package" && make "$apex_module"
+
+system_apexdir="$ANDROID_PRODUCT_OUT/system/apex"
+apex_package="$system_apexdir/$apex_module.apex"
+
+say "Extracting and mounting image"
+
+# Extract the image from the Android Runtime APEX.
+image_filename="image.img"
+unzip -q "$apex_package" "$image_filename" -d "$work_dir"
+mkdir "$mount_point"
+image_file="$work_dir/$image_filename"
+
+# Check filesystems in the image.
+image_filesystems="$work_dir/image_filesystems"
+virt-filesystems -a "$image_file" >"$image_filesystems"
+# We expect a single partition (/dev/sda) in the image.
+partition="/dev/sda"
+echo "$partition" | cmp "$image_filesystems" -
+
+# Mount the image from the Android Runtime APEX.
+guestmount -a "$image_file" -m "$partition" "$mount_point"
+
+# List the contents of the mounted image (optional).
+$list_image_files_p && say "Listing image files" && ls -ld "$mount_point" && tree -ap "$mount_point"
+
+say "Running tests"
+
+# Check that the mounted image contains a manifest.
+[[ -f "$mount_point/manifest.json" ]]
+
+function check_binary {
+  [[ -x "$mount_point/bin/$1" ]] || die "Cannot find binary '$1' in mounted image"
+}
+
+function check_multilib_binary {
+  # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+  # the precision of this test?
+  [[ -x "$mount_point/bin/${1}32" ]] || [[ -x "$mount_point/bin/${1}64" ]] \
+    || die "Cannot find binary '$1' in mounted image"
+}
+
+function check_binary_symlink {
+  [[ -h "$mount_point/bin/$1" ]] || die "Cannot find symbolic link '$1' in mounted image"
+}
+
+function check_library {
+  # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+  # the precision of this test?
+  [[ -f "$mount_point/lib/$1" ]] || [[ -f "$mount_point/lib64/$1" ]] \
+    || die "Cannot find library '$1' in mounted image"
+}
+
+# Check that the mounted image contains ART base binaries.
+check_multilib_binary dalvikvm
+# TODO: Does not work yet.
+: check_binary_symlink dalvikvm
+check_binary dex2oat
+check_binary dexoptanalyzer
+check_binary profman
+
+# Check that the mounted image contains ART tools binaries.
+check_binary dexdiag
+check_binary dexdump
+check_binary dexlist
+check_binary oatdump
+
+# Check that the mounted image contains ART debug binaries.
+check_binary dex2oatd
+check_binary dexoptanalyzerd
+check_binary profmand
+
+# Check that the mounted image contains ART libraries.
+check_library libart-compiler.so
+check_library libart.so
+check_library libopenjdkjvm.so
+check_library libopenjdkjvmti.so
+check_library libadbconnection.so
+# TODO: Should we check for these libraries too, even if they are not explicitly
+# listed as dependencies in the Android Runtime APEX module rule?
+check_library libartbase.so
+check_library libart-dexlayout.so
+check_library libart-disassembler.so
+check_library libdexfile.so
+check_library libprofile.so
+
+# Check that the mounted image contains ART debug libraries.
+check_library libartd-compiler.so
+check_library libartd.so
+check_library libdexfiled.so
+check_library libopenjdkd.so
+check_library libopenjdkjvmd.so
+check_library libopenjdkjvmtid.so
+check_library libadbconnectiond.so
+# TODO: Should we check for these libraries too, even if they are not explicitly
+# listed as dependencies in the Android Runtime APEX module rule?
+check_library libartbased.so
+check_library libartd-dexlayout.so
+check_library libprofiled.so
+
+# TODO: Should we check for other libraries, such as:
+#
+#   libbacktrace.so
+#   libbase.so
+#   liblog.so
+#   libsigchain.so
+#   libtombstoned_client.so
+#   libunwindstack.so
+#   libvixl-arm64.so
+#   libvixl-arm.so
+#   libvixld-arm64.so
+#   libvixld-arm.so
+#   ...
+#
+# ?
+
+say "Tests passed"
diff --git a/build/apex/runtime.avbpubkey b/build/apex/runtime.avbpubkey
new file mode 100644
index 0000000..b0ffc9b
--- /dev/null
+++ b/build/apex/runtime.avbpubkey
Binary files differ
diff --git a/build/apex/runtime.pem b/build/apex/runtime.pem
new file mode 100644
index 0000000..4c7ce4b
--- /dev/null
+++ b/build/apex/runtime.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAx/VRn+TOZ4Hah9tHkb2Jvw7aQcqurnNamGa1Ta3x09HEV45s
+KTAqeTIPpbagx7aj6LNimiqoJaClV8pFhYfC6y7GLnXBk6PRGb2kPmrWy2aQFRkh
+Z2LBQwu15Rqr3SVbzMPbC5qoXOpUopzZnjRwniR32fnpJedUwpSMxaJwXDxfgBku
+Wm3EHBuTQ33L/z3VGwbVp1Rh/QhI/RfcwT1u6o9XUl0LqiQu/8DLTLNmsjAsQkbA
+8O1ToIBu2l71HaMqXOKRKtVuAYPyAMml5hXSH0dva2ebKkUM8E7FhcsK71QJ5iCs
+L2uC+OmG9f4aSqiIJld7/nDU7nrkiUxjs5bGp2PIxHmuv725XdBMJ+IuqnZGGkA8
+4tF14bY1YX4Tq6ojzATBnbFcZEAU4epJvX13Wu11ktYndMbppUtnVCdhO2vnA/tP
+MpBSOCHMk2Y2Q96LcIN9ANJrcrkrSIGBTQdvCRJ9LtofXlk/ytGIUceCzRtFhmwL
+zWFwJVT7cQX04Pw/EX/zrZyOq7SUYCGDsBwZsUtlZ30Cx92dergtKlZyJFqKnwMv
+hajr55mqRCv4M1dumCgiQaml29ftXWE6wQxqI0jQN8seSVz/HUazjSb3QFXgX16z
+w4VkxqSKu4subqesMcxiyev5McGXUUthkRGDSSFbJwX0L5jNEPyYPUu2nJ0CAwEA
+AQKCAgEAxGKuDin8hjBE3tWAjyTmWp1Nwvw7X96vhaqqOmayceU9vviERlel/24p
+bAnYEw3QIcW8+8kVaA9FFNn2OdVCnRVNU2gX/NcRkQRugVcRKqfKrs4FvrKBOUYR
+Gbh+Py5n4M4jHlyBKvCCu0rteLHsQYVzqMQINk/jMVAQijKlxBEPgpI4slvIFgsH
+MWwlpMOnv2mRAUyhCJDQjrKW/7tEal7p1lzIDgyHlGxXvzcbj7o8XcN7z6RnU+WP
++iz09GzCOIPVK4p/BkH+tsNVioq32jygs44IGRXERWg4GtV2IeQZ7Mj+E3y2H53M
+DWHJlLW9MlsNzrImjypntmkuKr3Uz+ipg/oXD1tv/XJkBkJUsWSQHzGw4DfxRfq7
+eJ9LlIMzrQn8ZJAJTSsckmGuakSyD9amSbtn1kl+fEZge9SvAoZVZelwB1qfGgyS
+qQVAN9x1SP0DCeX33syxT2rxZVOUZgRT8yt01jVcIU3dD66McYRjiUY6uG1aZ3Xb
+p8TD3xKMqPPc7dIN3xcN58S+sIejydmm636LE1ykA0dYPczqxDfIfhbqE/42B5LZ
+grjZdXN1pd97IeEFQLd+DfP8iq80D6k6ojmXxANXCz1ilJXyr2defWUwSSiwsD5v
+HacFeOQ6+KQyYrkdhbpa5XlO6luDIZmxN3B6rx7kqg6UZW9EzYkCggEBAPDNOZ6X
+TIKBIdV5zkr2rvjor/WvPnoWUOBCmxh8zaAZhReE3RitNjtEVz/ns/x8vyyMRdPA
+JDszBrawYlMjoEW9NQe6BYKfwKRl+QzsWEIsdBfYB70vmguwS/VdMbVaU/jWFbS+
+IFB9F88iEJiI8IeH+IomGXinCDxAkXqJztFZRzonaX5+QHC4s8geRyYn9gs6SxHy
+MqOOzifnebZg4dXLCL7jMDGsEa/Fu188FFae407BsOEt4bday37n91xysdilkPg3
+b3mIB3EFrsbnqXypayM/QUfQ/d48Xfa/l+74i1Qpd1MIeHYNndLDxtRes9Oc7Rnv
+oCdI9Lkc+KuR8AcCggEBANSUKb2jz0VfSZSZsgL5gj34Kcfoe5peQvP+pUuJmZhy
+8QkGUUNtq2l86PMJSfJknbUhVLPe0wzT8NG08HTMkVHlw7lve//avugfpnrR7hsZ
+BTWDjW44x+Y8Q8dwTUl3nYtEYn81ycUzmFBmYDEVXjlvyMlXe0HLEz90v2wwtZlp
+IxEXgEgMnLj36JH5iKh7YuLf9c8laok7Jed6u+h5nlXUcbfaSVN6U3K+6UdQKUrr
+TaSQLw2pEsZ6CEt0yGJDkoID7mfTfc1/olNWWGUz0RE9G5eqQYjgEoAiTBZZeSlm
+3Kaun8gydN7wwJ6AjPCPFOwtgV7dUoN4YbWgfsAgnTsCggEBALHOWCWKgqw6vcjr
+0C/6Ruj0qDk51WBA6icuB2flf9AgB+595OQ7wjexFtvRM03UrzUtvsHEtvwfiW2M
+gI3zWH0mYOn7qeXyIEVEJsosGl+Cg5a3pb9ETvMiknPzBKlssWSkcBKt8R59v/7q
+oGaBd1ocRKF90IEOlT4oT0O0Tkq3Kaj/QR5uCxcwy0+RS+gYyc0wlg4CUPIEmKVO
+fsj0cM10xlhtWUDUVZr83oZLzpjHagDVdM5RGsJRAMIMdtKEvl3Co3ElPeL3VsdV
+8uBcXwH1925nXsSwxUQ8PwXcI0wJqpfSppFhR9Gj7E2c0kwuQYqX7VuhXRik/k9R
+3SyS7jECggEBAL7q7m4GL8IjKSdPvgNT6TgUqBmFX3UtkT4nhnbH9u1m1bmANf20
+Ak20RFb6EbKj0Mv7SmJdDfkoY9FDiu2rSBxgmZ7yVFBeOjSpMFCAODOYDgiYxK2o
+S0go+cqlvpPr3M9WNIwBV9xHUVVsDJookb5N+etyKR3W78t+4+ib+oz0Uu0nySts
+QFkTNYncrXJ7lj0iXVaUSRFE0O8LWLYafCyjpxoy7sYNR+L3OPW2Nc+2cr4ITGod
+XeJpeQejs9Ak1fD07OnMlOC576SfGLaTigHMevqEi2UNsS/pHaK46stXOXZtwM0B
+G9uaJ7RyyaHHL0hKOjVj2pZ+yGph4VRWNj8CggEAQlp/QytXhKZtM9OqRy/th+XO
+ctoVEl8codUydwwxMCsKqGYiCXazeyDZQimOjaxSNFXo8hWuf694WGsQJ6TyXCEs
+0JAJbCooI+DI9Z4LbqHtLDg1/S6a1558Nyrc6j6amevvbB5xKS2mKhGl5JgzBsJO
+H3yE0DD1DHaSM3V1rTfdyGoaxNESw45bnpxkAooMrw62OIO/9f502FLUx+sq+koT
+aajw4qQ6rBll3/+PKCORKzncHDMkIbeD6c6sX+ONUz7vxg3pV4eZG7NClWvA24Td
+1sANz3m6EmqG41lBzeUGConWxWRwkEXJgbxmPwMariRKR8aNVOlDVVbDp9Hhxg==
+-----END RSA PRIVATE KEY-----
diff --git a/build/art.go b/build/art.go
index 0b5ee44..1c8be0f 100644
--- a/build/art.go
+++ b/build/art.go
@@ -53,6 +53,9 @@
 		cflags = append(cflags, "-DART_HEAP_POISONING=1")
 		asflags = append(asflags, "-DART_HEAP_POISONING=1")
 	}
+	if envTrue(ctx, "ART_USE_CXX_INTERPRETER") {
+		cflags = append(cflags, "-DART_USE_CXX_INTERPRETER=1")
+	}
 
 	if !envFalse(ctx, "ART_USE_READ_BARRIER") && ctx.AConfig().ArtUseReadBarrier() {
 		// Used to change the read barrier type. Valid values are BAKER, BROOKS,
diff --git a/build/codegen.go b/build/codegen.go
index 8526bf1..d0db78e 100644
--- a/build/codegen.go
+++ b/build/codegen.go
@@ -107,8 +107,9 @@
 }
 
 type CodegenCommonArchProperties struct {
-	Srcs   []string
-	Cflags []string
+	Srcs     []string
+	Cflags   []string
+	Cppflags []string
 }
 
 type CodegenLibraryArchProperties struct {
diff --git a/compiler/Android.bp b/compiler/Android.bp
index c2f8e3c..0d92b05 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -157,6 +157,8 @@
                 "optimizing/code_generator_x86.cc",
                 "optimizing/code_generator_vector_x86.cc",
                 "optimizing/intrinsics_x86.cc",
+                "optimizing/instruction_simplifier_x86_shared.cc",
+                "optimizing/instruction_simplifier_x86.cc",
                 "optimizing/pc_relative_fixups_x86.cc",
                 "optimizing/x86_memory_gen.cc",
                 "utils/x86/assembler_x86.cc",
@@ -168,6 +170,7 @@
             srcs: [
                 "jni/quick/x86_64/calling_convention_x86_64.cc",
                 "optimizing/intrinsics_x86_64.cc",
+                "optimizing/instruction_simplifier_x86_64.cc",
                 "optimizing/code_generator_x86_64.cc",
                 "optimizing/code_generator_vector_x86_64.cc",
                 "utils/x86_64/assembler_x86_64.cc",
@@ -180,7 +183,6 @@
     shared_libs: [
         "libbase",
         "libcutils",  // for atrace.
-        "liblzma",
     ],
     include_dirs: ["art/disassembler"],
     header_libs: [
@@ -196,7 +198,6 @@
     static_libs: [
         "libbase",
         "libcutils",
-        "liblzma",
     ],
 }
 
@@ -230,12 +231,12 @@
             // VIXL assembly support for ARM targets.
             static: {
                 whole_static_libs: [
-                    "libvixl-arm",
+                    "libvixl",
                 ],
             },
             shared: {
                 shared_libs: [
-                    "libvixl-arm",
+                    "libvixl",
                 ],
             },
         },
@@ -243,12 +244,12 @@
             // VIXL assembly support for ARM64 targets.
             static: {
                 whole_static_libs: [
-                    "libvixl-arm64",
+                    "libvixl",
                 ],
             },
             shared: {
                 shared_libs: [
-                    "libvixl-arm64",
+                    "libvixl",
                 ],
             },
         },
@@ -292,12 +293,12 @@
             // VIXL assembly support for ARM targets.
             static: {
                 whole_static_libs: [
-                    "libvixld-arm",
+                    "libvixld",
                 ],
             },
             shared: {
                 shared_libs: [
-                    "libvixld-arm",
+                    "libvixld",
                 ],
             },
         },
@@ -305,12 +306,12 @@
             // VIXL assembly support for ARM64 targets.
             static: {
                 whole_static_libs: [
-                    "libvixld-arm64",
+                    "libvixld",
                 ],
             },
             shared: {
                 shared_libs: [
-                    "libvixld-arm64",
+                    "libvixld",
                 ],
             },
         },
@@ -451,8 +452,7 @@
         "libprofiled",
         "libartd-compiler",
         "libartd-simulator-container",
-        "libvixld-arm",
-        "libvixld-arm64",
+        "libvixld",
 
         "libbacktrace",
         "libnativeloader",
@@ -509,7 +509,6 @@
     },
     shared_libs: [
         "libartd-compiler",
-        "libvixld-arm",
-        "libvixld-arm64",
+        "libvixld",
     ],
 }
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index fc8cd52..599f4aa 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -188,13 +188,13 @@
   compiler_options_->compile_pic_ = false;  // Non-PIC boot image is a test configuration.
   compiler_options_->SetCompilerFilter(GetCompilerFilter());
   compiler_options_->image_classes_.swap(*GetImageClasses());
+  compiler_options_->profile_compilation_info_ = GetProfileCompilationInfo();
   compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
                                             verification_results_.get(),
                                             compiler_kind_,
                                             &compiler_options_->image_classes_,
                                             number_of_threads_,
-                                            /* swap_fd */ -1,
-                                            GetProfileCompilationInfo()));
+                                            /* swap_fd */ -1));
 }
 
 void CommonCompilerTest::SetUpRuntimeOptions(RuntimeOptions* options) {
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index 5e2f444..8b35bd3 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -33,8 +33,7 @@
                         Compiler::kOptimizing,
                         /* image_classes */ nullptr,
                         /* thread_count */ 1u,
-                        /* swap_fd */ -1,
-                        /* profile_compilation_info */ nullptr);
+                        /* swap_fd */ -1);
   CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
 
   ASSERT_TRUE(storage->DedupeEnabled());  // The default.
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 294072d..792f508 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -99,25 +99,6 @@
   return std::make_pair(fast_get, fast_put);
 }
 
-inline ArtMethod* CompilerDriver::ResolveMethod(
-    ScopedObjectAccess& soa,
-    Handle<mirror::DexCache> dex_cache,
-    Handle<mirror::ClassLoader> class_loader,
-    const DexCompilationUnit* mUnit,
-    uint32_t method_idx,
-    InvokeType invoke_type) {
-  DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
-  ArtMethod* resolved_method =
-      mUnit->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
-          method_idx, dex_cache, class_loader, /* referrer */ nullptr, invoke_type);
-  if (UNLIKELY(resolved_method == nullptr)) {
-    DCHECK(soa.Self()->IsExceptionPending());
-    // Clean up any exception left by type resolution.
-    soa.Self()->ClearException();
-  }
-  return resolved_method;
-}
-
 inline VerificationResults* CompilerDriver::GetVerificationResults() const {
   DCHECK(Runtime::Current()->IsAotCompiler());
   return verification_results_;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 8c276bb..0d0a7f2 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -248,8 +248,7 @@
     Compiler::Kind compiler_kind,
     HashSet<std::string>* image_classes,
     size_t thread_count,
-    int swap_fd,
-    const ProfileCompilationInfo* profile_compilation_info)
+    int swap_fd)
     : compiler_options_(compiler_options),
       verification_results_(verification_results),
       compiler_(Compiler::Create(this, compiler_kind)),
@@ -259,9 +258,7 @@
       had_hard_verifier_failure_(false),
       parallel_thread_count_(thread_count),
       stats_(new AOTCompilationStats),
-      compiler_context_(nullptr),
       compiled_method_storage_(swap_fd),
-      profile_compilation_info_(profile_compilation_info),
       max_arena_alloc_(0),
       dex_to_dex_compiler_(this) {
   DCHECK(compiler_options_ != nullptr);
@@ -431,7 +428,6 @@
     Handle<mirror::ClassLoader> class_loader,
     const DexFile& dex_file,
     optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
-    bool compilation_enabled,
     Handle<mirror::DexCache> dex_cache,
     CompileFn compile_fn) {
   DCHECK(driver != nullptr);
@@ -449,7 +445,6 @@
                                class_loader,
                                dex_file,
                                dex_to_dex_compilation_level,
-                               compilation_enabled,
                                dex_cache);
 
   if (kTimeCompileMethod) {
@@ -482,7 +477,6 @@
     Handle<mirror::ClassLoader> class_loader,
     const DexFile& dex_file,
     optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
-    bool compilation_enabled,
     Handle<mirror::DexCache> dex_cache) {
   auto dex_2_dex_fn = [](Thread* self ATTRIBUTE_UNUSED,
       CompilerDriver* driver,
@@ -494,7 +488,6 @@
       Handle<mirror::ClassLoader> class_loader,
       const DexFile& dex_file,
       optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
-      bool compilation_enabled ATTRIBUTE_UNUSED,
       Handle<mirror::DexCache> dex_cache ATTRIBUTE_UNUSED) -> CompiledMethod* {
     DCHECK(driver != nullptr);
     MethodReference method_ref(&dex_file, method_idx);
@@ -531,7 +524,6 @@
                        class_loader,
                        dex_file,
                        dex_to_dex_compilation_level,
-                       compilation_enabled,
                        dex_cache,
                        dex_2_dex_fn);
 }
@@ -547,7 +539,6 @@
     Handle<mirror::ClassLoader> class_loader,
     const DexFile& dex_file,
     optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
-    bool compilation_enabled,
     Handle<mirror::DexCache> dex_cache) {
   auto quick_fn = [](
       Thread* self,
@@ -560,7 +551,6 @@
       Handle<mirror::ClassLoader> class_loader,
       const DexFile& dex_file,
       optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
-      bool compilation_enabled,
       Handle<mirror::DexCache> dex_cache) {
     DCHECK(driver != nullptr);
     CompiledMethod* compiled_method = nullptr;
@@ -586,7 +576,7 @@
       VerificationResults* results = driver->GetVerificationResults();
       DCHECK(results != nullptr);
       const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
-      bool compile = compilation_enabled &&
+      bool compile =
           // Basic checks, e.g., not <clinit>.
           results->IsCandidateForCompilation(method_ref, access_flags) &&
           // Did not fail to create VerifiedMethod metadata.
@@ -629,7 +619,6 @@
                        class_loader,
                        dex_file,
                        dex_to_dex_compilation_level,
-                       compilation_enabled,
                        dex_cache,
                        quick_fn);
 }
@@ -663,7 +652,6 @@
                      h_class_loader,
                      dex_file,
                      dex_to_dex_compilation_level,
-                     true,
                      dex_cache);
 
   const size_t num_methods = dex_to_dex_compiler_.NumCodeItemsToQuicken(self);
@@ -679,7 +667,6 @@
                          h_class_loader,
                          dex_file,
                          dex_to_dex_compilation_level,
-                         true,
                          dex_cache);
     dex_to_dex_compiler_.ClearState();
   }
@@ -724,16 +711,14 @@
     }
     TimingLogger::ScopedTiming t("Resolve const-string Strings", timings);
 
+    // TODO: Implement a profile-based filter for the boot image. See b/76145463.
     for (ClassAccessor accessor : dex_file->GetClasses()) {
-      if (!IsClassToCompile(accessor.GetDescriptor())) {
-        // Compilation is skipped, do not resolve const-string in code of this class.
-        // FIXME: Make sure that inlining honors this. b/26687569
-        continue;
-      }
+      const ProfileCompilationInfo* profile_compilation_info =
+          GetCompilerOptions().GetProfileCompilationInfo();
 
       const bool is_startup_class =
-          profile_compilation_info_ != nullptr &&
-          profile_compilation_info_->ContainsClass(*dex_file, accessor.GetClassIdx());
+          profile_compilation_info != nullptr &&
+          profile_compilation_info->ContainsClass(*dex_file, accessor.GetClassIdx());
 
       for (const ClassAccessor::Method& method : accessor.GetMethods()) {
         const bool is_clinit = (method.GetAccessFlags() & kAccConstructor) != 0 &&
@@ -741,8 +726,8 @@
         const bool is_startup_clinit = is_startup_class && is_clinit;
 
         if (only_startup_strings &&
-            profile_compilation_info_ != nullptr &&
-            (!profile_compilation_info_->GetMethodHotness(method.GetReference()).IsStartup() &&
+            profile_compilation_info != nullptr &&
+            (!profile_compilation_info->GetMethodHotness(method.GetReference()).IsStartup() &&
              !is_startup_clinit)) {
           continue;
         }
@@ -834,12 +819,6 @@
     TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings);
 
     for (ClassAccessor accessor : dex_file->GetClasses()) {
-      if (!driver->IsClassToCompile(accessor.GetDescriptor())) {
-        // Compilation is skipped, do not look for type checks in code of this class.
-        // FIXME: Make sure that inlining honors this. b/26687569
-        continue;
-      }
-
       // Direct and virtual methods.
       for (const ClassAccessor::Method& method : accessor.GetMethods()) {
         InitializeTypeCheckBitstrings(driver, class_linker, dex_cache, *dex_file, method);
@@ -966,13 +945,6 @@
   }
 }
 
-bool CompilerDriver::IsClassToCompile(const char* descriptor) const {
-  if (classes_to_compile_ == nullptr) {
-    return true;
-  }
-  return classes_to_compile_->find(StringPiece(descriptor)) != classes_to_compile_->end();
-}
-
 bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_ref) const {
   // Profile compilation info may be null if no profile is passed.
   if (!CompilerFilter::DependsOnProfile(compiler_options_->GetCompilerFilter())) {
@@ -981,12 +953,14 @@
     return true;
   }
   // If we are using a profile filter but do not have a profile compilation info, compile nothing.
-  if (profile_compilation_info_ == nullptr) {
+  const ProfileCompilationInfo* profile_compilation_info =
+      GetCompilerOptions().GetProfileCompilationInfo();
+  if (profile_compilation_info == nullptr) {
     return false;
   }
   // Compile only hot methods, it is the profile saver's job to decide what startup methods to mark
   // as hot.
-  bool result = profile_compilation_info_->GetMethodHotness(method_ref).IsHot();
+  bool result = profile_compilation_info->GetMethodHotness(method_ref).IsHot();
 
   if (kDebugProfileGuidedCompilation) {
     LOG(INFO) << "[ProfileGuidedCompilation] "
@@ -2610,9 +2584,6 @@
     optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level =
         GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def);
 
-
-    const bool compilation_enabled = driver->IsClassToCompile(accessor.GetDescriptor());
-
     // Compile direct and virtual methods.
     int64_t previous_method_idx = -1;
     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
@@ -2633,7 +2604,6 @@
                  class_loader,
                  dex_file,
                  dex_to_dex_compilation_level,
-                 compilation_enabled,
                  dex_cache);
     }
   };
@@ -2644,10 +2614,12 @@
                              const std::vector<const DexFile*>& dex_files,
                              TimingLogger* timings) {
   if (kDebugProfileGuidedCompilation) {
+    const ProfileCompilationInfo* profile_compilation_info =
+        GetCompilerOptions().GetProfileCompilationInfo();
     LOG(INFO) << "[ProfileGuidedCompilation] " <<
-        ((profile_compilation_info_ == nullptr)
+        ((profile_compilation_info == nullptr)
             ? "null"
-            : profile_compilation_info_->DumpInfo(dex_files));
+            : profile_compilation_info->DumpInfo(dex_files));
   }
 
   dex_to_dex_compiler_.ClearState();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index f42e555..714b2d1 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -99,8 +99,7 @@
                  Compiler::Kind compiler_kind,
                  HashSet<std::string>* image_classes,
                  size_t thread_count,
-                 int swap_fd,
-                 const ProfileCompilationInfo* profile_compilation_info);
+                 int swap_fd);
 
   ~CompilerDriver();
 
@@ -146,7 +145,6 @@
   bool GetCompiledClass(const ClassReference& ref, ClassStatus* status) const;
 
   CompiledMethod* GetCompiledMethod(MethodReference ref) const;
-  size_t GetNonRelativeLinkerPatchCount() const;
   // Add a compiled method.
   void AddCompiledMethod(const MethodReference& method_ref, CompiledMethod* const compiled_method);
   CompiledMethod* RemoveCompiledMethod(const MethodReference& method_ref);
@@ -181,16 +179,6 @@
                                             uint16_t field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Resolve a method. Returns null on failure, including incompatible class change.
-  ArtMethod* ResolveMethod(
-      ScopedObjectAccess& soa,
-      Handle<mirror::DexCache> dex_cache,
-      Handle<mirror::ClassLoader> class_loader,
-      const DexCompilationUnit* mUnit,
-      uint32_t method_idx,
-      InvokeType invoke_type)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   void ProcessedInstanceField(bool resolved);
   void ProcessedStaticField(bool resolved, bool local);
 
@@ -209,14 +197,6 @@
   const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
   bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc);
 
-  void SetCompilerContext(void* compiler_context) {
-    compiler_context_ = compiler_context;
-  }
-
-  void* GetCompilerContext() const {
-    return compiler_context_;
-  }
-
   size_t GetThreadCount() const {
     return parallel_thread_count_;
   }
@@ -229,9 +209,6 @@
     return compiled_method_storage_.DedupeEnabled();
   }
 
-  // Checks whether the provided class should be compiled, i.e., is in classes_to_compile_.
-  bool IsClassToCompile(const char* descriptor) const;
-
   // Checks whether profile guided compilation is enabled and if the method should be compiled
   // according to the profile file.
   bool ShouldCompileBasedOnProfile(const MethodReference& method_ref) const;
@@ -266,10 +243,6 @@
     return &compiled_method_storage_;
   }
 
-  const ProfileCompilationInfo* GetProfileCompilationInfo() const {
-    return profile_compilation_info_;
-  }
-
   // Is `boot_image_filename` the name of a core image (small boot
   // image used for ART testing only)?
   static bool IsCoreImageFilename(const std::string& boot_image_filename) {
@@ -363,6 +336,12 @@
   void FreeThreadPools();
   void CheckThreadPools();
 
+  // Resolve const string literals that are loaded from dex code. If only_startup_strings is
+  // specified, only methods that are marked startup in the profile are resolved.
+  void ResolveConstStrings(const std::vector<const DexFile*>& dex_files,
+                           bool only_startup_strings,
+                           /*inout*/ TimingLogger* timings);
+
   const CompilerOptions* const compiler_options_;
   VerificationResults* const verification_results_;
 
@@ -377,13 +356,6 @@
 
   typedef AtomicDexRefMap<MethodReference, CompiledMethod*> MethodTable;
 
- private:
-  // Resolve const string literals that are loaded from dex code. If only_startup_strings is
-  // specified, only methods that are marked startup in the profile are resolved.
-  void ResolveConstStrings(const std::vector<const DexFile*>& dex_files,
-                           bool only_startup_strings,
-                           /*inout*/ TimingLogger* timings);
-
   // All method references that this compiler has compiled.
   MethodTable compiled_methods_;
 
@@ -393,11 +365,6 @@
   //       Dex2Oat rather than implicitly by CompileAll().
   HashSet<std::string>* image_classes_;
 
-  // Specifies the classes that will be compiled. Note that if classes_to_compile_ is null,
-  // all classes are eligible for compilation (duplication filters etc. will still apply).
-  // This option may be restricted to the boot image, depending on a flag in the implementation.
-  std::unique_ptr<HashSet<std::string>> classes_to_compile_;
-
   std::atomic<uint32_t> number_of_soft_verifier_failures_;
 
   bool had_hard_verifier_failure_;
@@ -412,16 +379,8 @@
   class AOTCompilationStats;
   std::unique_ptr<AOTCompilationStats> stats_;
 
-  typedef void (*CompilerCallbackFn)(CompilerDriver& driver);
-  typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver);
-
-  void* compiler_context_;
-
   CompiledMethodStorage compiled_method_storage_;
 
-  // Info for profile guided compilation.
-  const ProfileCompilationInfo* const profile_compilation_info_;
-
   size_t max_arena_alloc_;
 
   // Compiler for dex to dex (quickening).
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 6b0e456..685cde3 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -45,8 +45,8 @@
       dex_files_for_oat_file_(),
       image_classes_(),
       boot_image_(false),
-      core_image_(false),
       app_image_(false),
+      compiling_with_core_image_(false),
       baseline_(false),
       debuggable_(false),
       generate_debug_info_(kDefaultGenerateDebugInfo),
@@ -60,6 +60,7 @@
       dump_pass_timings_(false),
       dump_stats_(false),
       top_k_profile_threshold_(kDefaultTopKProfileThreshold),
+      profile_compilation_info_(nullptr),
       verbose_methods_(),
       abort_on_hard_verifier_failure_(false),
       abort_on_soft_verifier_failure_(false),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 4a6bbfa..2f4e542 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -42,6 +42,7 @@
 class DexFile;
 enum class InstructionSet;
 class InstructionSetFeatures;
+class ProfileCompilationInfo;
 
 class CompilerOptions final {
  public:
@@ -197,13 +198,6 @@
     return baseline_;
   }
 
-  // Are we compiling a core image (small boot image only used for ART testing)?
-  bool IsCoreImage() const {
-    // Ensure that `core_image_` => `boot_image_`.
-    DCHECK(!core_image_ || boot_image_);
-    return core_image_;
-  }
-
   // Are we compiling an app image?
   bool IsAppImage() const {
     return app_image_;
@@ -213,11 +207,22 @@
     app_image_ = false;
   }
 
+  // Returns whether we are compiling against a "core" image, which
+  // is an indicative we are running tests. The compiler will use that
+  // information for checking invariants.
+  bool CompilingWithCoreImage() const {
+    return compiling_with_core_image_;
+  }
+
   // Should the code be compiled as position independent?
   bool GetCompilePic() const {
     return compile_pic_;
   }
 
+  const ProfileCompilationInfo* GetProfileCompilationInfo() const {
+    return profile_compilation_info_;
+  }
+
   bool HasVerboseMethods() const {
     return !verbose_methods_.empty();
   }
@@ -352,8 +357,8 @@
   HashSet<std::string> image_classes_;
 
   bool boot_image_;
-  bool core_image_;
   bool app_image_;
+  bool compiling_with_core_image_;
   bool baseline_;
   bool debuggable_;
   bool generate_debug_info_;
@@ -370,6 +375,9 @@
   // When using a profile file only the top K% of the profiled samples will be compiled.
   double top_k_profile_threshold_;
 
+  // Info for profile guided compilation.
+  const ProfileCompilationInfo* profile_compilation_info_;
+
   // Vector of methods to have verbose output enabled for.
   std::vector<std::string> verbose_methods_;
 
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index 1ec34ec..a593240 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -52,7 +52,7 @@
 COMPILER_OPTIONS_KEY (double,                      TopKProfileThreshold)
 COMPILER_OPTIONS_KEY (bool,                        AbortOnHardVerifierFailure)
 COMPILER_OPTIONS_KEY (bool,                        AbortOnSoftVerifierFailure)
-COMPILER_OPTIONS_KEY (bool,                        ResolveStartupConstStrings, kIsDebugBuild)
+COMPILER_OPTIONS_KEY (bool,                        ResolveStartupConstStrings, false)
 COMPILER_OPTIONS_KEY (std::string,                 DumpInitFailures)
 COMPILER_OPTIONS_KEY (std::string,                 DumpCFG)
 COMPILER_OPTIONS_KEY (Unit,                        DumpCFGAppend)
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index bc8641a..bb35065 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -90,10 +90,11 @@
   // Special case max code units for inlining, whose default is "unset" (implictly
   // meaning no limit). Do this before parsing the actual passed options.
   compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+  Runtime* runtime = Runtime::Current();
   {
     std::string error_msg;
-    if (!compiler_options_->ParseCompilerOptions(Runtime::Current()->GetCompilerOptions(),
-                                                 true /* ignore_unrecognized */,
+    if (!compiler_options_->ParseCompilerOptions(runtime->GetCompilerOptions(),
+                                                 /*ignore_unrecognized=*/ true,
                                                  &error_msg)) {
       LOG(FATAL) << error_msg;
       UNREACHABLE();
@@ -103,7 +104,7 @@
   compiler_options_->SetNonPic();
 
   // Set debuggability based on the runtime value.
-  compiler_options_->SetDebuggable(Runtime::Current()->IsJavaDebuggable());
+  compiler_options_->SetDebuggable(runtime->IsJavaDebuggable());
 
   const InstructionSet instruction_set = compiler_options_->GetInstructionSet();
   if (kRuntimeISA == InstructionSet::kArm) {
@@ -112,7 +113,7 @@
     DCHECK_EQ(instruction_set, kRuntimeISA);
   }
   std::unique_ptr<const InstructionSetFeatures> instruction_set_features;
-  for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) {
+  for (const StringPiece option : runtime->GetCompilerOptions()) {
     VLOG(compiler) << "JIT compiler option " << option;
     std::string error_msg;
     if (option.starts_with("--instruction-set-variant=")) {
@@ -144,6 +145,8 @@
     instruction_set_features = InstructionSetFeatures::FromCppDefines();
   }
   compiler_options_->instruction_set_features_ = std::move(instruction_set_features);
+  compiler_options_->compiling_with_core_image_ =
+      CompilerDriver::IsCoreImageFilename(runtime->GetImageLocation());
 
   compiler_driver_.reset(new CompilerDriver(
       compiler_options_.get(),
@@ -151,8 +154,7 @@
       Compiler::kOptimizing,
       /* image_classes */ nullptr,
       /* thread_count */ 1,
-      /* swap_fd */ -1,
-      /* profile_compilation_info */ nullptr));
+      /* swap_fd */ -1));
   // Disable dedupe so we can remove compiled methods.
   compiler_driver_->SetDedupeEnabled(false);
 
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index a9acf90..48b50ea 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2319,9 +2319,10 @@
       if (offset >= kReferenceLoadMinFarOffset) {
         locations->AddTemp(FixedTempLocation());
       }
-    } else {
+    } else if (!instruction->GetArray()->IsIntermediateAddress()) {
       // We need a non-scratch temporary for the array data pointer in
-      // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier().
+      // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier() for the case with no
+      // intermediate address.
       locations->AddTemp(Location::RequiresRegister());
     }
   }
@@ -2351,11 +2352,12 @@
   MacroAssembler* masm = GetVIXLAssembler();
   UseScratchRegisterScope temps(masm);
 
-  // The read barrier instrumentation of object ArrayGet instructions
+  // The non-Baker read barrier instrumentation of object ArrayGet instructions
   // does not support the HIntermediateAddress instruction.
   DCHECK(!((type == DataType::Type::kReference) &&
            instruction->GetArray()->IsIntermediateAddress() &&
-           kEmitCompilerReadBarrier));
+           kEmitCompilerReadBarrier &&
+           !kUseBakerReadBarrier));
 
   if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
     // Object ArrayGet with Baker's read barrier case.
@@ -2363,6 +2365,7 @@
     // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call.
     DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
     if (index.IsConstant()) {
+      DCHECK(!instruction->GetArray()->IsIntermediateAddress());
       // Array load with a constant index can be treated as a field load.
       offset += Int64FromLocation(index) << DataType::SizeShift(type);
       Location maybe_temp =
@@ -2375,9 +2378,8 @@
                                                       /* needs_null_check */ false,
                                                       /* use_load_acquire */ false);
     } else {
-      Register temp = WRegisterFrom(locations->GetTemp(0));
       codegen_->GenerateArrayLoadWithBakerReadBarrier(
-          out, obj.W(), offset, index, temp, /* needs_null_check */ false);
+          instruction, out, obj.W(), offset, index, /* needs_null_check */ false);
     }
   } else {
     // General case.
@@ -2426,8 +2428,8 @@
         // input instruction has done it already. See the comment in
         // `TryExtractArrayAccessAddress()`.
         if (kIsDebugBuild) {
-          HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress();
-          DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), offset);
+          HIntermediateAddress* interm_addr = instruction->GetArray()->AsIntermediateAddress();
+          DCHECK_EQ(interm_addr->GetOffset()->AsIntConstant()->GetValueAsUint64(), offset);
         }
         temp = obj;
       } else {
@@ -2539,8 +2541,8 @@
         // input instruction has done it already. See the comment in
         // `TryExtractArrayAccessAddress()`.
         if (kIsDebugBuild) {
-          HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress();
-          DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset);
+          HIntermediateAddress* interm_addr = instruction->GetArray()->AsIntermediateAddress();
+          DCHECK(interm_addr->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset);
         }
         temp = array;
       } else {
@@ -5957,11 +5959,11 @@
       instruction, ref, obj, src, needs_null_check, use_load_acquire);
 }
 
-void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(Location ref,
+void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HArrayGet* instruction,
+                                                               Location ref,
                                                                Register obj,
                                                                uint32_t data_offset,
                                                                Location index,
-                                                               Register temp,
                                                                bool needs_null_check) {
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
@@ -6000,9 +6002,24 @@
   DCHECK(temps.IsAvailable(ip0));
   DCHECK(temps.IsAvailable(ip1));
   temps.Exclude(ip0, ip1);
+
+  Register temp;
+  if (instruction->GetArray()->IsIntermediateAddress()) {
+    // We do not need to compute the intermediate address from the array: the
+    // input instruction has done it already. See the comment in
+    // `TryExtractArrayAccessAddress()`.
+    if (kIsDebugBuild) {
+      HIntermediateAddress* interm_addr = instruction->GetArray()->AsIntermediateAddress();
+      DCHECK_EQ(interm_addr->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+    }
+    temp = obj;
+  } else {
+    temp = WRegisterFrom(instruction->GetLocations()->GetTemp(0));
+    __ Add(temp.X(), obj.X(), Operand(data_offset));
+  }
+
   uint32_t custom_data = EncodeBakerReadBarrierArrayData(temp.GetCode());
 
-  __ Add(temp.X(), obj.X(), Operand(data_offset));
   {
     ExactAssemblyScope guard(GetVIXLAssembler(),
                              (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 1ba58b1..ada5742 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -694,11 +694,11 @@
                                              bool use_load_acquire);
   // Fast path implementation of ReadBarrier::Barrier for a heap
   // reference array load when Baker's read barriers are used.
-  void GenerateArrayLoadWithBakerReadBarrier(Location ref,
+  void GenerateArrayLoadWithBakerReadBarrier(HArrayGet* instruction,
+                                             Location ref,
                                              vixl::aarch64::Register obj,
                                              uint32_t data_offset,
                                              Location index,
-                                             vixl::aarch64::Register temp,
                                              bool needs_null_check);
 
   // Emit code checking the status of the Marking Register, and
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index e79a96b..5a18c1f 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -216,7 +216,7 @@
   switch (instruction->GetPackedType()) {
     case DataType::Type::kInt32:
       DCHECK_EQ(4u, instruction->GetVectorLength());
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ Addv(dst.S(), src.V4S());
           break;
@@ -230,7 +230,7 @@
       break;
     case DataType::Type::kInt64:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ Addp(dst.D(), src.V2D());
           break;
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index 62b6c4e..b092961 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -138,7 +138,7 @@
   switch (instruction->GetPackedType()) {
     case DataType::Type::kInt32:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ Vpadd(DataTypeValue::I32, dst, src, src);
           break;
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index 24f4fb2..09e96cc 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -187,7 +187,7 @@
   switch (instruction->GetPackedType()) {
     case DataType::Type::kInt32:
       DCHECK_EQ(4u, instruction->GetVectorLength());
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ Hadd_sD(tmp, src, src);
           __ IlvlD(dst, tmp, tmp);
@@ -209,7 +209,7 @@
       break;
     case DataType::Type::kInt64:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ IlvlD(dst, src, src);
           __ AddvD(dst, dst, src);
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
index 972c49e..b6873b1 100644
--- a/compiler/optimizing/code_generator_vector_mips64.cc
+++ b/compiler/optimizing/code_generator_vector_mips64.cc
@@ -185,7 +185,7 @@
   switch (instruction->GetPackedType()) {
     case DataType::Type::kInt32:
       DCHECK_EQ(4u, instruction->GetVectorLength());
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ Hadd_sD(tmp, src, src);
           __ IlvlD(dst, tmp, tmp);
@@ -207,7 +207,7 @@
       break;
     case DataType::Type::kInt64:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ IlvlD(dst, src, src);
           __ AddvD(dst, dst, src);
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index c52ecc7..0ee0035 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -205,8 +205,8 @@
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
   // Long reduction or min/max require a temporary.
   if (instruction->GetPackedType() == DataType::Type::kInt64 ||
-      instruction->GetKind() == HVecReduce::kMin ||
-      instruction->GetKind() == HVecReduce::kMax) {
+      instruction->GetReductionKind() == HVecReduce::kMin ||
+      instruction->GetReductionKind() == HVecReduce::kMax) {
     instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
   }
 }
@@ -218,38 +218,23 @@
   switch (instruction->GetPackedType()) {
     case DataType::Type::kInt32:
       DCHECK_EQ(4u, instruction->GetVectorLength());
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ movaps(dst, src);
           __ phaddd(dst, dst);
           __ phaddd(dst, dst);
           break;
-        case HVecReduce::kMin: {
-          XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
-          __ movaps(tmp, src);
-          __ movaps(dst, src);
-          __ psrldq(tmp, Immediate(8));
-          __ pminsd(dst, tmp);
-          __ psrldq(tmp, Immediate(4));
-          __ pminsd(dst, tmp);
-          break;
-        }
-        case HVecReduce::kMax: {
-          XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
-          __ movaps(tmp, src);
-          __ movaps(dst, src);
-          __ psrldq(tmp, Immediate(8));
-          __ pmaxsd(dst, tmp);
-          __ psrldq(tmp, Immediate(4));
-          __ pmaxsd(dst, tmp);
-          break;
-        }
+        case HVecReduce::kMin:
+        case HVecReduce::kMax:
+          // Historical note: We've had a broken implementation here. b/117863065
+          // Do not draw on the old code if we ever want to bring MIN/MAX reduction back.
+          LOG(FATAL) << "Unsupported reduction type.";
       }
       break;
     case DataType::Type::kInt64: {
       DCHECK_EQ(2u, instruction->GetVectorLength());
       XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ movaps(tmp, src);
           __ movaps(dst, src);
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 87d0106..9c28827 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -188,8 +188,8 @@
   CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
   // Long reduction or min/max require a temporary.
   if (instruction->GetPackedType() == DataType::Type::kInt64 ||
-      instruction->GetKind() == HVecReduce::kMin ||
-      instruction->GetKind() == HVecReduce::kMax) {
+      instruction->GetReductionKind() == HVecReduce::kMin ||
+      instruction->GetReductionKind() == HVecReduce::kMax) {
     instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
   }
 }
@@ -201,38 +201,23 @@
   switch (instruction->GetPackedType()) {
     case DataType::Type::kInt32:
       DCHECK_EQ(4u, instruction->GetVectorLength());
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ movaps(dst, src);
           __ phaddd(dst, dst);
           __ phaddd(dst, dst);
           break;
-        case HVecReduce::kMin: {
-          XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
-          __ movaps(tmp, src);
-          __ movaps(dst, src);
-          __ psrldq(tmp, Immediate(8));
-          __ pminsd(dst, tmp);
-          __ psrldq(tmp, Immediate(4));
-          __ pminsd(dst, tmp);
-          break;
-        }
-        case HVecReduce::kMax: {
-          XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
-          __ movaps(tmp, src);
-          __ movaps(dst, src);
-          __ psrldq(tmp, Immediate(8));
-          __ pmaxsd(dst, tmp);
-          __ psrldq(tmp, Immediate(4));
-          __ pmaxsd(dst, tmp);
-          break;
-        }
+        case HVecReduce::kMin:
+        case HVecReduce::kMax:
+          // Historical note: We've had a broken implementation here. b/117863065
+          // Do not draw on the old code if we ever want to bring MIN/MAX reduction back.
+          LOG(FATAL) << "Unsupported reduction type.";
       }
       break;
     case DataType::Type::kInt64: {
       DCHECK_EQ(2u, instruction->GetVectorLength());
       XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
-      switch (instruction->GetKind()) {
+      switch (instruction->GetReductionKind()) {
         case HVecReduce::kSum:
           __ movaps(tmp, src);
           __ movaps(dst, src);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 9f34a51..fba4da6 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3497,6 +3497,27 @@
   }
 }
 
+void InstructionCodeGeneratorX86::RemByPowerOfTwo(HRem* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+
+  Register out = locations->Out().AsRegister<Register>();
+  Register numerator = locations->InAt(0).AsRegister<Register>();
+
+  int32_t imm = Int64FromConstant(second.GetConstant());
+  DCHECK(IsPowerOfTwo(AbsOrMin(imm)));
+  uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
+
+  Register tmp = locations->GetTemp(0).AsRegister<Register>();
+  NearLabel done;
+  __ movl(out, numerator);
+  __ andl(out, Immediate(abs_imm-1));
+  __ j(Condition::kZero, &done);
+  __ leal(tmp, Address(out, static_cast<int32_t>(~(abs_imm-1))));
+  __ testl(numerator, numerator);
+  __ cmovl(Condition::kLess, out, tmp);
+  __ Bind(&done);
+}
 
 void InstructionCodeGeneratorX86::DivByPowerOfTwo(HDiv* instruction) {
   LocationSummary* locations = instruction->GetLocations();
@@ -3610,8 +3631,12 @@
           // Do not generate anything for 0. DivZeroCheck would forbid any generated code.
         } else if (imm == 1 || imm == -1) {
           DivRemOneOrMinusOne(instruction);
-        } else if (is_div && IsPowerOfTwo(AbsOrMin(imm))) {
-          DivByPowerOfTwo(instruction->AsDiv());
+        } else if (IsPowerOfTwo(AbsOrMin(imm))) {
+          if (is_div) {
+            DivByPowerOfTwo(instruction->AsDiv());
+          } else {
+            RemByPowerOfTwo(instruction->AsRem());
+          }
         } else {
           DCHECK(imm <= -2 || imm >= 2);
           GenerateDivRemWithAnyConstant(instruction);
@@ -7424,6 +7449,61 @@
   }
 }
 
+void LocationsBuilderX86::VisitX86AndNot(HX86AndNot* instruction) {
+  DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
+  DCHECK(DataType::IsIntOrLongType(instruction->GetType())) << instruction->GetType();
+  LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86::VisitX86AndNot(HX86AndNot* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  Location dest = locations->Out();
+  if (instruction->GetResultType() == DataType::Type::kInt32) {
+    __ andn(dest.AsRegister<Register>(),
+            first.AsRegister<Register>(),
+            second.AsRegister<Register>());
+  } else {
+    DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
+    __ andn(dest.AsRegisterPairLow<Register>(),
+            first.AsRegisterPairLow<Register>(),
+            second.AsRegisterPairLow<Register>());
+    __ andn(dest.AsRegisterPairHigh<Register>(),
+            first.AsRegisterPairHigh<Register>(),
+            second.AsRegisterPairHigh<Register>());
+  }
+}
+
+void LocationsBuilderX86::VisitX86MaskOrResetLeastSetBit(HX86MaskOrResetLeastSetBit* instruction) {
+  DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
+  DCHECK(instruction->GetType() == DataType::Type::kInt32) << instruction->GetType();
+  LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86::VisitX86MaskOrResetLeastSetBit(
+    HX86MaskOrResetLeastSetBit* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Location src = locations->InAt(0);
+  Location dest = locations->Out();
+  DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
+  switch (instruction->GetOpKind()) {
+    case HInstruction::kAnd:
+      __ blsr(dest.AsRegister<Register>(), src.AsRegister<Register>());
+      break;
+    case HInstruction::kXor:
+      __ blsmsk(dest.AsRegister<Register>(), src.AsRegister<Register>());
+      break;
+    default:
+      LOG(FATAL) << "Unreachable";
+  }
+}
+
 void LocationsBuilderX86::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction); }
 void LocationsBuilderX86::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction); }
 void LocationsBuilderX86::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); }
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 93b0461..deeef88 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -159,6 +159,7 @@
 
   FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_CONCRETE_INSTRUCTION_X86(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
@@ -190,6 +191,7 @@
 
   FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_CONCRETE_INSTRUCTION_X86(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
@@ -216,6 +218,7 @@
   void GenerateDivRemIntegral(HBinaryOperation* instruction);
   void DivRemOneOrMinusOne(HBinaryOperation* instruction);
   void DivByPowerOfTwo(HDiv* instruction);
+  void RemByPowerOfTwo(HRem* instruction);
   void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
   void GenerateRemFP(HRem* rem);
   void HandleCondition(HCondition* condition);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index dac2dba..14cff05 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -3560,7 +3560,40 @@
       LOG(FATAL) << "Unexpected type for div by (-)1 " << instruction->GetResultType();
   }
 }
+void InstructionCodeGeneratorX86_64::RemByPowerOfTwo(HRem* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+  CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+  CpuRegister numerator = locations->InAt(0).AsRegister<CpuRegister>();
+  int64_t imm = Int64FromConstant(second.GetConstant());
+  DCHECK(IsPowerOfTwo(AbsOrMin(imm)));
+  uint64_t abs_imm = AbsOrMin(imm);
+  CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
+  if (instruction->GetResultType() == DataType::Type::kInt32) {
+    NearLabel done;
+    __ movl(out, numerator);
+    __ andl(out, Immediate(abs_imm-1));
+    __ j(Condition::kZero, &done);
+    __ leal(tmp, Address(out, static_cast<int32_t>(~(abs_imm-1))));
+    __ testl(numerator, numerator);
+    __ cmov(Condition::kLess, out, tmp, false);
+    __ Bind(&done);
 
+  } else {
+    DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
+    codegen_->Load64BitValue(tmp, abs_imm - 1);
+    NearLabel done;
+
+    __ movq(out, numerator);
+    __ andq(out, tmp);
+    __ j(Condition::kZero, &done);
+    __ movq(tmp, numerator);
+    __ sarq(tmp, Immediate(63));
+    __ shlq(tmp, Immediate(WhichPowerOf2(abs_imm)));
+    __ orq(out, tmp);
+    __ Bind(&done);
+  }
+}
 void InstructionCodeGeneratorX86_64::DivByPowerOfTwo(HDiv* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Location second = locations->InAt(1);
@@ -3737,8 +3770,12 @@
       // Do not generate anything. DivZeroCheck would prevent any code to be executed.
     } else if (imm == 1 || imm == -1) {
       DivRemOneOrMinusOne(instruction);
-    } else if (instruction->IsDiv() && IsPowerOfTwo(AbsOrMin(imm))) {
-      DivByPowerOfTwo(instruction->AsDiv());
+    } else if (IsPowerOfTwo(AbsOrMin(imm))) {
+      if (is_div) {
+        DivByPowerOfTwo(instruction->AsDiv());
+      } else {
+        RemByPowerOfTwo(instruction->AsRem());
+      }
     } else {
       DCHECK(imm <= -2 || imm >= 2);
       GenerateDivRemWithAnyConstant(instruction);
@@ -6734,6 +6771,48 @@
   }
 }
 
+void LocationsBuilderX86_64::VisitX86AndNot(HX86AndNot* instruction) {
+  DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
+  DCHECK(DataType::IsIntOrLongType(instruction->GetType())) << instruction->GetType();
+  LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  // There is no immediate variant of negated bitwise and in X86.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void LocationsBuilderX86_64::VisitX86MaskOrResetLeastSetBit(HX86MaskOrResetLeastSetBit* instruction) {
+  DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
+  DCHECK(DataType::IsIntOrLongType(instruction->GetType())) << instruction->GetType();
+  LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86_64::VisitX86AndNot(HX86AndNot* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  Location dest = locations->Out();
+  __ andn(dest.AsRegister<CpuRegister>(), first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
+}
+
+void InstructionCodeGeneratorX86_64::VisitX86MaskOrResetLeastSetBit(HX86MaskOrResetLeastSetBit* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Location src = locations->InAt(0);
+  Location dest = locations->Out();
+  switch (instruction->GetOpKind()) {
+    case HInstruction::kAnd:
+      __ blsr(dest.AsRegister<CpuRegister>(), src.AsRegister<CpuRegister>());
+      break;
+    case HInstruction::kXor:
+      __ blsmsk(dest.AsRegister<CpuRegister>(), src.AsRegister<CpuRegister>());
+      break;
+    default:
+      LOG(FATAL) << "Unreachable";
+  }
+}
+
 void LocationsBuilderX86_64::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction); }
 void LocationsBuilderX86_64::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction); }
 void LocationsBuilderX86_64::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); }
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 1e71397..f74e130 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -161,6 +161,7 @@
 
   FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_CONCRETE_INSTRUCTION_X86_64(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
@@ -192,6 +193,7 @@
 
   FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
   FOR_EACH_CONCRETE_INSTRUCTION_X86_64(DECLARE_VISIT_INSTRUCTION)
+  FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(DECLARE_VISIT_INSTRUCTION)
 
 #undef DECLARE_VISIT_INSTRUCTION
 
@@ -213,6 +215,7 @@
   void GenerateRemFP(HRem* rem);
   void DivRemOneOrMinusOne(HBinaryOperation* instruction);
   void DivByPowerOfTwo(HDiv* instruction);
+  void RemByPowerOfTwo(HRem* instruction);
   void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
   void GenerateDivRemIntegral(HBinaryOperation* instruction);
   void HandleCondition(HCondition* condition);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index f56f9cb..45d9167 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -39,7 +39,6 @@
 #include "mirror/object_array-alloc-inl.h"
 #include "mirror/object_array-inl.h"
 #include "nodes.h"
-#include "optimizing_compiler.h"
 #include "reference_type_propagation.h"
 #include "register_allocator_linear_scan.h"
 #include "scoped_thread_state_change-inl.h"
@@ -151,13 +150,13 @@
 
   // If we're compiling with a core image (which is only used for
   // test purposes), honor inlining directives in method names:
-  // - if a method's name contains the substring "$inline$", ensure
-  //   that this method is actually inlined;
   // - if a method's name contains the substring "$noinline$", do not
-  //   inline that method.
+  //   inline that method;
+  // - if a method's name contains the substring "$inline$", ensure
+  //   that this method is actually inlined.
   // We limit the latter to AOT compilation, as the JIT may or may not inline
   // depending on the state of classes at runtime.
-  const bool honor_noinline_directives = IsCompilingWithCoreImage();
+  const bool honor_noinline_directives = codegen_->GetCompilerOptions().CompilingWithCoreImage();
   const bool honor_inline_directives =
       honor_noinline_directives && Runtime::Current()->IsAotCompiler();
 
@@ -680,7 +679,7 @@
     /*out*/Handle<mirror::ObjectArray<mirror::Class>>* inline_cache)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(Runtime::Current()->IsAotCompiler());
-  const ProfileCompilationInfo* pci = compiler_driver_->GetProfileCompilationInfo();
+  const ProfileCompilationInfo* pci = codegen_->GetCompilerOptions().GetProfileCompilationInfo();
   if (pci == nullptr) {
     return kInlineCacheNoData;
   }
@@ -1419,10 +1418,6 @@
 static inline bool MayInline(const CompilerOptions& compiler_options,
                              const DexFile& inlined_from,
                              const DexFile& inlined_into) {
-  if (kIsTargetBuild) {
-    return true;
-  }
-
   // We're not allowed to inline across dex files if we're the no-inline-from dex file.
   if (!IsSameDexFile(inlined_from, inlined_into) &&
       ContainsElement(compiler_options.GetNoInlineFromDexFile(), &inlined_from)) {
@@ -1647,7 +1642,7 @@
         }
       }
       if (needs_constructor_barrier) {
-        // See CompilerDriver::RequiresConstructorBarrier for more details.
+        // See DexCompilationUnit::RequiresConstructorBarrier for more details.
         DCHECK(obj != nullptr) << "only non-static methods can have a constructor fence";
 
         HConstructorFence* constructor_fence =
@@ -1737,6 +1732,21 @@
   return (object != hint.Get()) ? handles->NewHandle(object) : hint;
 }
 
+static bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (!Runtime::Current()->IsAotCompiler()) {
+    // JIT can always encode methods in stack maps.
+    return true;
+  }
+  if (IsSameDexFile(caller_dex_file, *callee->GetDexFile())) {
+    return true;
+  }
+  // TODO(ngeoffray): Support more AOT cases for inlining:
+  // - methods in multidex
+  // - methods in boot image for on-device non-PIC compilation.
+  return false;
+}
+
 bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
                                        ArtMethod* resolved_method,
                                        ReferenceTypeInfo receiver_type,
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 24fbb6c..f968c19 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -202,6 +202,11 @@
     return;
   }
 
+  // TODO: Support intermediate address for object arrays on arm.
+  if (type == DataType::Type::kReference) {
+    return;
+  }
+
   if (type == DataType::Type::kInt64
       || type == DataType::Type::kFloat32
       || type == DataType::Type::kFloat64) {
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
index ccdcb35..0f30f66 100644
--- a/compiler/optimizing/instruction_simplifier_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -245,11 +245,11 @@
     return false;
   }
   if (kEmitCompilerReadBarrier &&
+      !kUseBakerReadBarrier &&
       access->IsArrayGet() &&
       access->GetType() == DataType::Type::kReference) {
-    // For object arrays, the read barrier instrumentation requires
+    // For object arrays, the non-Baker read barrier instrumentation requires
     // the original array pointer.
-    // TODO: This can be relaxed for Baker CC.
     return false;
   }
 
diff --git a/compiler/optimizing/instruction_simplifier_x86.cc b/compiler/optimizing/instruction_simplifier_x86.cc
new file mode 100644
index 0000000..2d8f94a
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86.cc
@@ -0,0 +1,88 @@
+/* Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instruction_simplifier_x86.h"
+#include "instruction_simplifier_x86_shared.h"
+#include "code_generator_x86.h"
+
+namespace art {
+
+namespace x86 {
+
+class InstructionSimplifierX86Visitor : public HGraphVisitor {
+ public:
+  InstructionSimplifierX86Visitor(HGraph* graph,
+                                  CodeGenerator* codegen,
+                                  OptimizingCompilerStats* stats)
+      : HGraphVisitor(graph),
+        codegen_(down_cast<CodeGeneratorX86*>(codegen)),
+        stats_(stats) {}
+
+  void RecordSimplification() {
+    MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch);
+  }
+
+  bool HasAVX2() {
+    return (codegen_->GetInstructionSetFeatures().HasAVX2());
+  }
+
+  void VisitBasicBlock(HBasicBlock* block) override {
+    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+      HInstruction* instruction = it.Current();
+      if (instruction->IsInBlock()) {
+        instruction->Accept(this);
+      }
+    }
+  }
+
+  void VisitAnd(HAnd * instruction) override;
+  void VisitXor(HXor* instruction) override;
+
+ private:
+  CodeGeneratorX86* codegen_;
+  OptimizingCompilerStats* stats_;
+};
+
+
+void InstructionSimplifierX86Visitor::VisitAnd(HAnd* instruction) {
+  if (TryCombineAndNot(instruction)) {
+    RecordSimplification();
+  } else if (instruction->GetResultType() == DataType::Type::kInt32) {
+    if (TryGenerateResetLeastSetBit(instruction)) {
+      RecordSimplification();
+    }
+  }
+}
+
+void InstructionSimplifierX86Visitor::VisitXor(HXor* instruction) {
+  if (instruction->GetResultType() == DataType::Type::kInt32) {
+    if (TryGenerateMaskUptoLeastSetBit(instruction)) {
+      RecordSimplification();
+    }
+  }
+}
+
+bool InstructionSimplifierX86::Run() {
+  InstructionSimplifierX86Visitor visitor(graph_, codegen_, stats_);
+  if (visitor.HasAVX2()) {
+    visitor.VisitReversePostOrder();
+    return true;
+  }
+  return false;
+}
+
+}  // namespace x86
+}  // namespace art
+
diff --git a/compiler/optimizing/instruction_simplifier_x86.h b/compiler/optimizing/instruction_simplifier_x86.h
new file mode 100644
index 0000000..6f10006
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86.h
@@ -0,0 +1,44 @@
+/*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_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class CodeGenerator;
+namespace x86 {
+
+class InstructionSimplifierX86 : public HOptimization {
+ public:
+  InstructionSimplifierX86(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats)
+      : HOptimization(graph, kInstructionSimplifierX86PassName, stats),
+        codegen_(codegen) {}
+
+  static constexpr const char* kInstructionSimplifierX86PassName = "instruction_simplifier_x86";
+
+  bool Run() override;
+
+ private:
+  CodeGenerator* codegen_;
+};
+
+}  // namespace x86
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
diff --git a/compiler/optimizing/instruction_simplifier_x86_64.cc b/compiler/optimizing/instruction_simplifier_x86_64.cc
new file mode 100644
index 0000000..56c6b41
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86_64.cc
@@ -0,0 +1,82 @@
+/* Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instruction_simplifier_x86_64.h"
+#include "instruction_simplifier_x86_shared.h"
+#include "code_generator_x86_64.h"
+
+namespace art {
+
+namespace x86_64 {
+
+class InstructionSimplifierX86_64Visitor : public HGraphVisitor {
+ public:
+  InstructionSimplifierX86_64Visitor(HGraph* graph,
+                                     CodeGenerator* codegen,
+                                     OptimizingCompilerStats* stats)
+      : HGraphVisitor(graph),
+        codegen_(down_cast<CodeGeneratorX86_64*>(codegen)),
+        stats_(stats) {}
+
+  void RecordSimplification() {
+    MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch);
+  }
+
+  bool HasAVX2() {
+    return codegen_->GetInstructionSetFeatures().HasAVX2();
+  }
+
+  void VisitBasicBlock(HBasicBlock* block) override {
+    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+      HInstruction* instruction = it.Current();
+      if (instruction->IsInBlock()) {
+        instruction->Accept(this);
+      }
+    }
+  }
+
+  void VisitAnd(HAnd* instruction) override;
+  void VisitXor(HXor* instruction) override;
+
+ private:
+  CodeGeneratorX86_64* codegen_;
+  OptimizingCompilerStats* stats_;
+};
+
+void InstructionSimplifierX86_64Visitor::VisitAnd(HAnd* instruction) {
+  if (TryCombineAndNot(instruction)) {
+    RecordSimplification();
+  } else if (TryGenerateResetLeastSetBit(instruction)) {
+    RecordSimplification();
+  }
+}
+
+
+void InstructionSimplifierX86_64Visitor::VisitXor(HXor* instruction) {
+  if (TryGenerateMaskUptoLeastSetBit(instruction)) {
+    RecordSimplification();
+  }
+}
+
+bool InstructionSimplifierX86_64::Run() {
+  InstructionSimplifierX86_64Visitor visitor(graph_, codegen_, stats_);
+  if (visitor.HasAVX2()) {
+    visitor.VisitReversePostOrder();
+    return true;
+  }
+  return false;
+}
+}  // namespace x86_64
+}  // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_x86_64.h b/compiler/optimizing/instruction_simplifier_x86_64.h
new file mode 100644
index 0000000..6cae24d
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86_64.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class CodeGenerator;
+
+namespace x86_64 {
+
+class InstructionSimplifierX86_64 : public HOptimization {
+ public:
+  InstructionSimplifierX86_64(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats)
+      : HOptimization(graph, kInstructionSimplifierX86_64PassName, stats),
+        codegen_(codegen) {}
+
+  static constexpr const char* kInstructionSimplifierX86_64PassName =
+      "instruction_simplifier_x86_64";
+
+  bool Run() override;
+
+ private:
+  CodeGenerator* codegen_;
+};
+
+}  // namespace x86_64
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
+
+
diff --git a/compiler/optimizing/instruction_simplifier_x86_shared.cc b/compiler/optimizing/instruction_simplifier_x86_shared.cc
new file mode 100644
index 0000000..2805abb
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86_shared.cc
@@ -0,0 +1,137 @@
+/* Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instruction_simplifier_x86_shared.h"
+#include "nodes_x86.h"
+
+namespace art {
+
+bool TryCombineAndNot(HAnd* instruction) {
+  DataType::Type type = instruction->GetType();
+  if (!DataType::IsIntOrLongType(type)) {
+    return false;
+  }
+  // Replace code looking like
+  //    Not tmp, y
+  //    And dst, x, tmp
+  //  with
+  //    AndNot dst, x, y
+  HInstruction* left = instruction->GetLeft();
+  HInstruction* right = instruction->GetRight();
+  // Perform simplication only when either left or right
+  // is Not. When both are Not, instruction should be simplified with
+  // DeMorgan's Laws.
+  if (left->IsNot() ^ right->IsNot()) {
+    bool left_is_not = left->IsNot();
+    HInstruction* other_ins = (left_is_not ? right : left);
+    HNot* not_ins = (left_is_not ? left : right)->AsNot();
+    // Only do the simplification if instruction has only one use
+    // and thus can be safely removed.
+    if (not_ins->HasOnlyOneNonEnvironmentUse()) {
+      ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetAllocator();
+      HX86AndNot* and_not = new (arena) HX86AndNot(type,
+                                                   not_ins->GetInput(),
+                                                   other_ins,
+                                                   instruction->GetDexPc());
+      instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, and_not);
+      DCHECK(!not_ins->HasUses());
+      not_ins->GetBlock()->RemoveInstruction(not_ins);
+      return true;
+    }
+  }
+  return false;
+}
+
+bool TryGenerateResetLeastSetBit(HAnd* instruction) {
+  DataType::Type type = instruction->GetType();
+  if (!DataType::IsIntOrLongType(type)) {
+    return false;
+  }
+  // Replace code looking like
+  //    Add tmp, x, -1 or Sub tmp, x, 1
+  //    And dest x, tmp
+  //  with
+  //    MaskOrResetLeastSetBit dest, x
+  HInstruction* candidate = nullptr;
+  HInstruction* other = nullptr;
+  HInstruction* left = instruction->GetLeft();
+  HInstruction* right = instruction->GetRight();
+  if (AreLeastSetBitInputs(left, right)) {
+    candidate = left;
+    other = right;
+  } else if (AreLeastSetBitInputs(right, left)) {
+    candidate = right;
+    other = left;
+  }
+  if (candidate != nullptr && candidate->HasOnlyOneNonEnvironmentUse()) {
+    ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetAllocator();
+    HX86MaskOrResetLeastSetBit* lsb = new (arena) HX86MaskOrResetLeastSetBit(
+        type, HInstruction::kAnd, other, instruction->GetDexPc());
+    instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, lsb);
+    DCHECK(!candidate->HasUses());
+    candidate->GetBlock()->RemoveInstruction(candidate);
+    return true;
+  }
+  return false;
+}
+
+bool TryGenerateMaskUptoLeastSetBit(HXor* instruction) {
+  DataType::Type type = instruction->GetType();
+  if (!DataType::IsIntOrLongType(type)) {
+    return false;
+  }
+  // Replace code looking like
+  //    Add tmp, x, -1 or Sub tmp, x, 1
+  //    Xor dest x, tmp
+  //  with
+  //    MaskOrResetLeastSetBit dest, x
+  HInstruction* left = instruction->GetLeft();
+  HInstruction* right = instruction->GetRight();
+  HInstruction* other = nullptr;
+  HInstruction* candidate = nullptr;
+  if (AreLeastSetBitInputs(left, right)) {
+    candidate = left;
+    other = right;
+  } else if (AreLeastSetBitInputs(right, left)) {
+    candidate = right;
+    other = left;
+  }
+  if (candidate != nullptr && candidate->HasOnlyOneNonEnvironmentUse()) {
+    ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetAllocator();
+    HX86MaskOrResetLeastSetBit* lsb = new (arena) HX86MaskOrResetLeastSetBit(
+        type, HInstruction::kXor, other, instruction->GetDexPc());
+    instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, lsb);
+    DCHECK(!candidate->HasUses());
+    candidate->GetBlock()->RemoveInstruction(candidate);
+    return true;
+  }
+  return false;
+}
+
+bool AreLeastSetBitInputs(HInstruction* to_test, HInstruction* other) {
+  if (to_test->IsAdd()) {
+    HAdd* add = to_test->AsAdd();
+    HConstant* cst = add->GetConstantRight();
+    return cst != nullptr && cst->IsMinusOne() && other == add->GetLeastConstantLeft();
+  }
+  if (to_test->IsSub()) {
+    HSub* sub = to_test->AsSub();
+    HConstant* cst = sub->GetConstantRight();
+    return cst != nullptr && cst->IsOne() && other == sub->GetLeastConstantLeft();
+  }
+  return false;
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_x86_shared.h b/compiler/optimizing/instruction_simplifier_x86_shared.h
new file mode 100644
index 0000000..7f94d7e
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86_shared.h
@@ -0,0 +1,29 @@
+/* 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_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
+
+#include "nodes.h"
+
+namespace art {
+bool TryCombineAndNot(HAnd* instruction);
+bool TryGenerateResetLeastSetBit(HAnd* instruction);
+bool TryGenerateMaskUptoLeastSetBit(HXor* instruction);
+bool AreLeastSetBitInputs(HInstruction* to_test, HInstruction* other);
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
+
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 7684dc7..6d04b0e 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2916,6 +2916,40 @@
 
 void IntrinsicCodeGeneratorARM64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
 
+void IntrinsicLocationsBuilderARM64::VisitCRC32Update(HInvoke* invoke) {
+  if (!codegen_->GetInstructionSetFeatures().HasCRC()) {
+    return;
+  }
+
+  LocationSummary* locations = new (allocator_) LocationSummary(invoke,
+                                                                LocationSummary::kNoCall,
+                                                                kIntrinsified);
+
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+// Lower the invoke of CRC32.update(int crc, int b).
+void IntrinsicCodeGeneratorARM64::VisitCRC32Update(HInvoke* invoke) {
+  DCHECK(codegen_->GetInstructionSetFeatures().HasCRC());
+
+  MacroAssembler* masm = GetVIXLAssembler();
+
+  Register crc = InputRegisterAt(invoke, 0);
+  Register val = InputRegisterAt(invoke, 1);
+  Register out = OutputRegister(invoke);
+
+  // The general algorithm of the CRC32 calculation is:
+  //   crc = ~crc
+  //   result = crc32_for_byte(crc, b)
+  //   crc = ~result
+  // It is directly lowered to three instructions.
+  __ Mvn(out, crc);
+  __ Crc32b(out, out, val);
+  __ Mvn(out, out);
+}
+
 UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
 
 UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 38e4c89..4d45a99 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -446,7 +446,7 @@
 void IntrinsicCodeGeneratorARMVIXL::VisitMathRint(HInvoke* invoke) {
   DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
   ArmVIXLAssembler* assembler = GetAssembler();
-  __ Vrintn(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+  __ Vrintn(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
@@ -481,7 +481,7 @@
   // Handle input < 0 cases.
   // If input is negative but not a tie, previous result (round to nearest) is valid.
   // If input is a negative tie, change rounding direction to positive infinity, out_reg += 1.
-  __ Vrinta(F32, F32, temp1, in_reg);
+  __ Vrinta(F32, temp1, in_reg);
   __ Vmov(temp2, 0.5);
   __ Vsub(F32, temp1, in_reg, temp1);
   __ Vcmp(F32, temp1, temp2);
@@ -2945,7 +2945,7 @@
 void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
   DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
-  __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+  __ Vrintp(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
@@ -2957,7 +2957,7 @@
 void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
   DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
-  __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+  __ Vrintm(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
@@ -3059,6 +3059,7 @@
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong)     // High register pressure.
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32Update)
 
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 6f7f5e4..21fb7d7 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2696,6 +2696,8 @@
 UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
 
+UNIMPLEMENTED_INTRINSIC(MIPS, CRC32Update)
+
 UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter);
 UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferAppend);
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 2eb2529..4b86f5d 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2346,6 +2346,7 @@
 
 UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
+UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32Update)
 
 UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 3504d7a..6dd4681 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2967,6 +2967,7 @@
 UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit)
 UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit)
 UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit)
+UNIMPLEMENTED_INTRINSIC(X86, CRC32Update)
 
 UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 96f6eaa..7db26dc 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2732,6 +2732,7 @@
 UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite)
 UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite)
+UNIMPLEMENTED_INTRINSIC(X86_64, CRC32Update)
 
 UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 2124380..6108522 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1497,6 +1497,14 @@
   M(X86PackedSwitch, Instruction)
 #endif
 
+#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
+#define FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(M)                     \
+  M(X86AndNot, Instruction)                                                \
+  M(X86MaskOrResetLeastSetBit, Instruction)
+#else
+#define FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(M)
+#endif
+
 #define FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
 
 #define FOR_EACH_CONCRETE_INSTRUCTION(M)                                \
@@ -1507,7 +1515,8 @@
   FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M)                                 \
   FOR_EACH_CONCRETE_INSTRUCTION_MIPS64(M)                               \
   FOR_EACH_CONCRETE_INSTRUCTION_X86(M)                                  \
-  FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
+  FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)                               \
+  FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(M)
 
 #define FOR_EACH_ABSTRACT_INSTRUCTION(M)                                \
   M(Condition, BinaryOperation)                                         \
@@ -7766,7 +7775,7 @@
 #ifdef ART_ENABLE_CODEGEN_mips
 #include "nodes_mips.h"
 #endif
-#ifdef ART_ENABLE_CODEGEN_x86
+#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
 #include "nodes_x86.h"
 #endif
 
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 597e399..cd4f45e 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -384,21 +384,21 @@
              HInstruction* input,
              DataType::Type packed_type,
              size_t vector_length,
-             ReductionKind kind,
+             ReductionKind reduction_kind,
              uint32_t dex_pc)
       : HVecUnaryOperation(kVecReduce, allocator, input, packed_type, vector_length, dex_pc),
-        kind_(kind) {
+        reduction_kind_(reduction_kind) {
     DCHECK(HasConsistentPackedTypes(input, packed_type));
   }
 
-  ReductionKind GetKind() const { return kind_; }
+  ReductionKind GetReductionKind() const { return reduction_kind_; }
 
   bool CanBeMoved() const override { return true; }
 
   bool InstructionDataEquals(const HInstruction* other) const override {
     DCHECK(other->IsVecReduce());
     const HVecReduce* o = other->AsVecReduce();
-    return HVecOperation::InstructionDataEquals(o) && GetKind() == o->GetKind();
+    return HVecOperation::InstructionDataEquals(o) && GetReductionKind() == o->GetReductionKind();
   }
 
   DECLARE_INSTRUCTION(VecReduce);
@@ -407,7 +407,7 @@
   DEFAULT_COPY_CONSTRUCTOR(VecReduce);
 
  private:
-  const ReductionKind kind_;
+  const ReductionKind reduction_kind_;
 };
 
 // Converts every component in the vector,
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
index af13449..b0a665d 100644
--- a/compiler/optimizing/nodes_vector_test.cc
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -401,9 +401,9 @@
   EXPECT_TRUE(v2->CanBeMoved());
   EXPECT_TRUE(v3->CanBeMoved());
 
-  EXPECT_EQ(HVecReduce::kSum, v1->GetKind());
-  EXPECT_EQ(HVecReduce::kMin, v2->GetKind());
-  EXPECT_EQ(HVecReduce::kMax, v3->GetKind());
+  EXPECT_EQ(HVecReduce::kSum, v1->GetReductionKind());
+  EXPECT_EQ(HVecReduce::kMin, v2->GetReductionKind());
+  EXPECT_EQ(HVecReduce::kMax, v3->GetReductionKind());
 
   EXPECT_TRUE(v1->Equals(v1));
   EXPECT_TRUE(v2->Equals(v2));
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index a551104..8e8fbc1 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -128,6 +128,92 @@
   const int32_t num_entries_;
 };
 
+class HX86AndNot final : public HBinaryOperation {
+ public:
+  HX86AndNot(DataType::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(kX86AndNot, result_type, left, right, SideEffects::None(), dex_pc) {
+  }
+
+  bool IsCommutative() const override { return false; }
+
+  template <typename T> static T Compute(T x, T y) { return ~x & y; }
+
+  HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const override {
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
+  }
+  HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const override {
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
+  }
+  HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+                      HFloatConstant* y ATTRIBUTE_UNUSED) const override {
+    LOG(FATAL) << DebugName() << " is not defined for float values";
+    UNREACHABLE();
+  }
+  HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+                      HDoubleConstant* y ATTRIBUTE_UNUSED) const override {
+    LOG(FATAL) << DebugName() << " is not defined for double values";
+    UNREACHABLE();
+  }
+
+  DECLARE_INSTRUCTION(X86AndNot);
+
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(X86AndNot);
+};
+
+class HX86MaskOrResetLeastSetBit final : public HUnaryOperation {
+ public:
+  HX86MaskOrResetLeastSetBit(DataType::Type result_type, InstructionKind op,
+                             HInstruction* input, uint32_t dex_pc = kNoDexPc)
+      : HUnaryOperation(kX86MaskOrResetLeastSetBit, result_type, input, dex_pc),
+        op_kind_(op) {
+    DCHECK_EQ(result_type, DataType::Kind(input->GetType()));
+    DCHECK(op == HInstruction::kAnd || op == HInstruction::kXor) << op;
+  }
+  template <typename T>
+  auto Compute(T x) const -> decltype(x & (x-1)) {
+    static_assert(std::is_same<decltype(x & (x-1)), decltype(x ^(x-1))>::value,
+                  "Inconsistent  bitwise types");
+    switch (op_kind_) {
+      case HInstruction::kAnd:
+        return x & (x-1);
+      case HInstruction::kXor:
+        return x ^ (x-1);
+      default:
+        LOG(FATAL) << "Unreachable";
+        UNREACHABLE();
+    }
+  }
+
+  HConstant* Evaluate(HIntConstant* x) const override {
+    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()), GetDexPc());
+  }
+  HConstant* Evaluate(HLongConstant* x) const override {
+    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc());
+  }
+  HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED) const override {
+    LOG(FATAL) << DebugName() << "is not defined for float values";
+    UNREACHABLE();
+  }
+  HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED) const override {
+    LOG(FATAL) << DebugName() << "is not defined for double values";
+    UNREACHABLE();
+  }
+  InstructionKind GetOpKind() const { return op_kind_; }
+
+  DECLARE_INSTRUCTION(X86MaskOrResetLeastSetBit);
+
+ protected:
+  const InstructionKind op_kind_;
+
+  DEFAULT_COPY_CONSTRUCTOR(X86MaskOrResetLeastSetBit);
+};
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_NODES_X86_H_
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 4b0941b..0f971e1 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -28,10 +28,14 @@
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86
 #include "pc_relative_fixups_x86.h"
+#include "instruction_simplifier_x86.h"
 #endif
 #if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
 #include "x86_memory_gen.h"
 #endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+#include "instruction_simplifier_x86_64.h"
+#endif
 
 #include "bounds_check_elimination.h"
 #include "cha_guard_optimization.h"
@@ -113,6 +117,12 @@
 #ifdef ART_ENABLE_CODEGEN_x86
     case OptimizationPass::kPcRelativeFixupsX86:
       return x86::PcRelativeFixups::kPcRelativeFixupsX86PassName;
+    case OptimizationPass::kInstructionSimplifierX86:
+      return x86::InstructionSimplifierX86::kInstructionSimplifierX86PassName;
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+    case OptimizationPass::kInstructionSimplifierX86_64:
+      return x86_64::InstructionSimplifierX86_64::kInstructionSimplifierX86_64PassName;
 #endif
 #if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
     case OptimizationPass::kX86MemoryOperandGeneration:
@@ -311,6 +321,14 @@
         DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name";
         opt = new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats);
         break;
+      case OptimizationPass::kInstructionSimplifierX86:
+       opt = new (allocator) x86::InstructionSimplifierX86(graph, codegen, stats);
+       break;
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+      case OptimizationPass::kInstructionSimplifierX86_64:
+        opt = new (allocator) x86_64::InstructionSimplifierX86_64(graph, codegen, stats);
+        break;
 #endif
       case OptimizationPass::kNone:
         LOG(FATAL) << "kNone does not represent an actual pass";
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index ced383f..490007d 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -96,6 +96,10 @@
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86
   kPcRelativeFixupsX86,
+  kInstructionSimplifierX86,
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+  kInstructionSimplifierX86_64,
 #endif
 #if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
   kX86MemoryOperandGeneration,
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 4f495b6..fe6abd4 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -579,6 +579,7 @@
 #ifdef ART_ENABLE_CODEGEN_x86
     case InstructionSet::kX86: {
       OptimizationDef x86_optimizations[] = {
+        OptDef(OptimizationPass::kInstructionSimplifierX86),
         OptDef(OptimizationPass::kSideEffectsAnalysis),
         OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"),
         OptDef(OptimizationPass::kPcRelativeFixupsX86),
@@ -595,6 +596,7 @@
 #ifdef ART_ENABLE_CODEGEN_x86_64
     case InstructionSet::kX86_64: {
       OptimizationDef x86_64_optimizations[] = {
+        OptDef(OptimizationPass::kInstructionSimplifierX86_64),
         OptDef(OptimizationPass::kSideEffectsAnalysis),
         OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"),
         OptDef(OptimizationPass::kX86MemoryOperandGeneration)
@@ -1134,7 +1136,7 @@
   }
 
   if (kIsDebugBuild &&
-      IsCompilingWithCoreImage() &&
+      compiler_driver->GetCompilerOptions().CompilingWithCoreImage() &&
       IsInstructionSetSupported(compiler_driver->GetCompilerOptions().GetInstructionSet())) {
     // For testing purposes, we put a special marker on method names
     // that should be compiled with this compiler (when the
@@ -1232,30 +1234,11 @@
   return new OptimizingCompiler(driver);
 }
 
-bool IsCompilingWithCoreImage() {
-  const std::string& image = Runtime::Current()->GetImageLocation();
-  return CompilerDriver::IsCoreImageFilename(image);
-}
-
 bool EncodeArtMethodInInlineInfo(ArtMethod* method ATTRIBUTE_UNUSED) {
   // Note: the runtime is null only for unit testing.
   return Runtime::Current() == nullptr || !Runtime::Current()->IsAotCompiler();
 }
 
-bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee) {
-  if (!Runtime::Current()->IsAotCompiler()) {
-    // JIT can always encode methods in stack maps.
-    return true;
-  }
-  if (IsSameDexFile(caller_dex_file, *callee->GetDexFile())) {
-    return true;
-  }
-  // TODO(ngeoffray): Support more AOT cases for inlining:
-  // - methods in multidex
-  // - methods in boot image for on-device non-PIC compilation.
-  return false;
-}
-
 bool OptimizingCompiler::JitCompile(Thread* self,
                                     jit::JitCodeCache* code_cache,
                                     ArtMethod* method,
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index 6ee9c70..f5279e8 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -29,14 +29,7 @@
 
 Compiler* CreateOptimizingCompiler(CompilerDriver* driver);
 
-// Returns whether we are compiling against a "core" image, which
-// is an indicative we are running tests. The compiler will use that
-// information for checking invariants.
-bool IsCompilingWithCoreImage();
-
 bool EncodeArtMethodInInlineInfo(ArtMethod* method);
-bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
 }  // namespace art
 
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index c864951..885a08d 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -233,7 +233,7 @@
 
   // Try to assign a type check bitstring.
   MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
-  if ((false) &&  // FIXME: Inliner does not respect CompilerDriver::IsClassToCompile()
+  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() &&
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 053e202..3d26296 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -125,7 +125,7 @@
   // Assemble the .S
   snprintf(cmd, sizeof(cmd), "%sas %s -o %s.o", toolsdir.c_str(), filename, filename);
   int cmd_result = system(cmd);
-  ASSERT_EQ(cmd_result, 0) << strerror(errno);
+  ASSERT_EQ(cmd_result, 0) << cmd << strerror(errno);
 
   // Disassemble.
   snprintf(cmd, sizeof(cmd), "%sobjdump -D -M force-thumb --section=.text %s.o  | grep '^  *[0-9a-f][0-9a-f]*:'",
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 666db42..8ce96a4 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -71,11 +71,8 @@
     generated_sources: ["art_dex2oat_operator_srcs"],
     shared_libs: [
         "libbase",
-        "liblz4",
-        "liblzma",
     ],
     include_dirs: [
-        "external/lz4/lib",
         "external/zlib",
     ],
     export_include_dirs: ["."],
@@ -98,8 +95,6 @@
     },
     static_libs: [
         "libbase",
-        "liblz4",
-        "liblzma",
     ],
 }
 
@@ -183,7 +178,7 @@
 
     target: {
         android: {
-            // Use the 32-bit version of dex2oat on devices
+            // Use the 32-bit version of dex2oat on devices.
             compile_multilib: "prefer32",
         },
     },
@@ -247,7 +242,6 @@
         "libdexfile",
         "libartbase",
         "libbase",
-        "liblz4",
         "libsigchain",
     ],
     static_libs: [
@@ -285,7 +279,6 @@
         "libdexfiled",
         "libartbased",
         "libbase",
-        "liblz4",
         "libsigchain",
     ],
     static_libs: [
@@ -315,7 +308,6 @@
     ],
     static_libs: [
         "libbase",
-        "liblz4",
         "libsigchain_dummy",
     ],
 }
@@ -411,11 +403,11 @@
         "external/zlib",
     ],
     shared_libs: [
-        "libprofiled",
+        "libartbased",
         "libartd-compiler",
         "libartd-dexlayout",
         "libbase",
-        "liblz4",
+        "libprofiled",
         "libsigchain",
         "libziparchive",
     ],
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index f0f2b3e..dc123e4 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -186,7 +186,8 @@
 
   // Construct the final output.
   if (command.size() <= 1U) {
-    // It seems only "/system/bin/dex2oat" is left, or not even that. Use a pretty line.
+    // It seems only "/apex/com.android.runtime/bin/dex2oat" is left, or not
+    // even that. Use a pretty line.
     return "Starting dex2oat.";
   }
   return android::base::Join(command, ' ');
@@ -731,11 +732,6 @@
     compiler_options_->boot_image_ = !image_filenames_.empty();
     compiler_options_->app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
 
-    if (IsBootImage() && image_filenames_.size() == 1) {
-      const std::string& boot_image_filename = image_filenames_[0];
-      compiler_options_->core_image_ = CompilerDriver::IsCoreImageFilename(boot_image_filename);
-    }
-
     if (IsAppImage() && IsBootImage()) {
       Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
     }
@@ -950,6 +946,9 @@
       }
     }
     compiler_options_->passes_to_run_ = passes_to_run_.get();
+    compiler_options_->compiling_with_core_image_ =
+        !boot_image_filename_.empty() &&
+        CompilerDriver::IsCoreImageFilename(boot_image_filename_);
   }
 
   static bool SupportsDeterministicCompilation() {
@@ -1045,8 +1044,8 @@
   }
 
   void InsertCompileOptions(int argc, char** argv) {
-    std::ostringstream oss;
     if (!avoid_storing_invocation_) {
+      std::ostringstream oss;
       for (int i = 0; i < argc; ++i) {
         if (i > 0) {
           oss << ' ';
@@ -1054,10 +1053,7 @@
         oss << argv[i];
       }
       key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
-      oss.str("");  // Reset.
     }
-    oss << kRuntimeISA;
-    key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str());
     key_value_store_->Put(
         OatHeader::kDebuggableKey,
         compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
@@ -1513,15 +1509,6 @@
         std::vector<gc::space::ImageSpace*> image_spaces =
             Runtime::Current()->GetHeap()->GetBootImageSpaces();
         image_file_location_oat_checksum_ = image_spaces[0]->GetImageHeader().GetOatChecksum();
-        // Store the boot image filename(s).
-        std::vector<std::string> image_filenames;
-        for (const gc::space::ImageSpace* image_space : image_spaces) {
-          image_filenames.push_back(image_space->GetImageFilename());
-        }
-        std::string image_file_location = android::base::Join(image_filenames, ':');
-        if (!image_file_location.empty()) {
-          key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location);
-        }
       } else {
         image_file_location_oat_checksum_ = 0u;
       }
@@ -1792,14 +1779,14 @@
         compiler_options_->no_inline_from_.swap(no_inline_from_dex_files);
       }
     }
+    compiler_options_->profile_compilation_info_ = profile_compilation_info_.get();
 
     driver_.reset(new CompilerDriver(compiler_options_.get(),
                                      verification_results_.get(),
                                      compiler_kind_,
                                      &compiler_options_->image_classes_,
                                      thread_count_,
-                                     swap_fd_,
-                                     profile_compilation_info_.get()));
+                                     swap_fd_));
     if (!IsBootImage()) {
       driver_->SetClasspathDexFiles(class_loader_context_->FlattenOpenedDexFiles());
     }
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index d22b301..baeebd9 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -23,6 +23,7 @@
 #include <unistd.h>
 
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 
 #include "common_runtime_test.h"
@@ -90,6 +91,10 @@
     args.push_back("--runtime-arg");
     args.push_back("-Xnorelocate");
 
+    // Unless otherwise stated, use a small amount of threads, so that potential aborts are
+    // shorter. This can be overridden with extra_args.
+    args.push_back("-j4");
+
     args.insert(args.end(), extra_args.begin(), extra_args.end());
 
     int status = Dex2Oat(args, error_msg);
@@ -99,33 +104,33 @@
     return status;
   }
 
-  void GenerateOdexForTest(
+  ::testing::AssertionResult GenerateOdexForTest(
       const std::string& dex_location,
       const std::string& odex_location,
       CompilerFilter::Filter filter,
       const std::vector<std::string>& extra_args = {},
       bool expect_success = true,
-      bool use_fd = false) {
-    GenerateOdexForTest(dex_location,
-                        odex_location,
-                        filter,
-                        extra_args,
-                        expect_success,
-                        use_fd,
-                        [](const OatFile&) {});
+      bool use_fd = false) WARN_UNUSED {
+    return GenerateOdexForTest(dex_location,
+                               odex_location,
+                               filter,
+                               extra_args,
+                               expect_success,
+                               use_fd,
+                               [](const OatFile&) {});
   }
 
   bool test_accepts_odex_file_on_failure = false;
 
   template <typename T>
-  void GenerateOdexForTest(
+  ::testing::AssertionResult GenerateOdexForTest(
       const std::string& dex_location,
       const std::string& odex_location,
       CompilerFilter::Filter filter,
       const std::vector<std::string>& extra_args,
       bool expect_success,
       bool use_fd,
-      T check_oat) {
+      T check_oat) WARN_UNUSED {
     std::string error_msg;
     int status = GenerateOdexForTestWithStatus({dex_location},
                                                odex_location,
@@ -135,7 +140,10 @@
                                                use_fd);
     bool success = (WIFEXITED(status) && WEXITSTATUS(status) == 0);
     if (expect_success) {
-      ASSERT_TRUE(success) << error_msg << std::endl << output_;
+      if (!success) {
+        return ::testing::AssertionFailure()
+            << "Failed to compile odex: " << error_msg << std::endl << output_;
+      }
 
       // Verify the odex file was generated as expected.
       std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
@@ -146,12 +154,16 @@
                                                        dex_location.c_str(),
                                                        /*reservation=*/ nullptr,
                                                        &error_msg));
-      ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+      if (odex_file == nullptr) {
+        return ::testing::AssertionFailure() << "Could not open odex file: " << error_msg;
+      }
 
       CheckFilter(filter, odex_file->GetCompilerFilter());
       check_oat(*(odex_file.get()));
     } else {
-      ASSERT_FALSE(success) << output_;
+      if (success) {
+        return ::testing::AssertionFailure() << "Succeeded to compile odex: " << output_;
+      }
 
       error_msg_ = error_msg;
 
@@ -165,9 +177,12 @@
                                                          dex_location.c_str(),
                                                          /*reservation=*/ nullptr,
                                                          &error_msg));
-        ASSERT_TRUE(odex_file.get() == nullptr);
+        if (odex_file != nullptr) {
+          return ::testing::AssertionFailure() << "Could open odex file: " << error_msg;
+        }
       }
     }
+    return ::testing::AssertionSuccess();
   }
 
   // Check the input compiler filter against the generated oat file's filter. May be overridden
@@ -265,7 +280,7 @@
       std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
       copy.push_back("--swap-file=" + swap_location);
     }
-    GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy);
+    ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy));
 
     CheckValidity();
     ASSERT_TRUE(success_);
@@ -490,7 +505,7 @@
 
     std::vector<std::string> new_args(extra_args);
     new_args.push_back("--app-image-file=" + app_image_file);
-    GenerateOdexForTest(dex_location, odex_location, filter, new_args);
+    ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, filter, new_args));
 
     CheckValidity();
     ASSERT_TRUE(success_);
@@ -681,12 +696,12 @@
         copy.push_back("--app-image-file=" + app_image_file_name);
       }
     }
-    GenerateOdexForTest(dex_location,
-                        odex_location,
-                        CompilerFilter::kSpeedProfile,
-                        copy,
-                        expect_success,
-                        use_fd);
+    ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                    odex_location,
+                                    CompilerFilter::kSpeedProfile,
+                                    copy,
+                                    expect_success,
+                                    use_fd));
     if (app_image_file != nullptr) {
       ASSERT_EQ(app_image_file->FlushCloseOrErase(), 0) << "Could not flush and close art file";
     }
@@ -877,24 +892,24 @@
     {
       std::string input_vdex = "--input-vdex-fd=-1";
       std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd());
-      GenerateOdexForTest(dex_location,
-                          odex_location,
-                          CompilerFilter::kQuicken,
-                          { input_vdex, output_vdex },
-                          /*expect_success=*/ true,
-                          /*use_fd=*/ true);
+      ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                      odex_location,
+                                      CompilerFilter::kQuicken,
+                                      { input_vdex, output_vdex },
+                                      /* expect_success= */ true,
+                                      /* 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());
       std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd());
-      GenerateOdexForTest(dex_location,
-                          odex_location,
-                          CompilerFilter::kVerify,
-                          { input_vdex, output_vdex, kDisableCompactDex },
-                          /*expect_success=*/ true,
-                          /*use_fd=*/ true);
+      ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                      odex_location,
+                                      CompilerFilter::kVerify,
+                                      { input_vdex, output_vdex, kDisableCompactDex },
+                                      /* expect_success= */ true,
+                                      /* use_fd= */ true));
     }
     ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
     CheckResult(dex_location, odex_location);
@@ -918,12 +933,12 @@
     {
       std::string input_vdex = "--input-vdex-fd=-1";
       std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd());
-      GenerateOdexForTest(dex_location,
-                          odex_location,
-                          CompilerFilter::kQuicken,
-                          { input_vdex, output_vdex, "--compact-dex-level=fast"},
-                          /*expect_success=*/ true,
-                          /*use_fd=*/ true);
+      ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                      odex_location,
+                                      CompilerFilter::kQuicken,
+                                      { input_vdex, output_vdex, "--compact-dex-level=fast"},
+                                      /* expect_success= */ true,
+                                      /* use_fd= */ true));
       EXPECT_GT(vdex_file1->GetLength(), 0u);
     }
 
@@ -931,12 +946,12 @@
     {
       std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd());
       std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file2->Fd());
-      GenerateOdexForTest(dex_location,
-                          odex_location2,
-                          CompilerFilter::kVerify,
-                          { input_vdex, output_vdex, "--compact-dex-level=none"},
-                          /*expect_success=*/ true,
-                          /*use_fd=*/ true);
+      ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                      odex_location2,
+                                      CompilerFilter::kVerify,
+                                      { input_vdex, output_vdex, "--compact-dex-level=none"},
+                                      /* expect_success= */ true,
+                                      /* use_fd= */ true));
     }
     ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
     ASSERT_EQ(vdex_file2->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
@@ -992,11 +1007,11 @@
     std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
     copy.push_back("--swap-file=" + swap_location);
     copy.push_back("-j512");  // Excessive idle threads just slow down dex2oat.
-    GenerateOdexForTest(dex_location,
-                        odex_location,
-                        CompilerFilter::kSpeed,
-                        copy,
-                        expect_success);
+    ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                    odex_location,
+                                    CompilerFilter::kSpeed,
+                                    copy,
+                                    expect_success));
   }
 
   std::string GetTestDexFileName() {
@@ -1072,13 +1087,13 @@
       ASSERT_STREQ(expected_classpath_key, classpath);
     };
 
-    GenerateOdexForTest(dex_location,
-                        odex_location,
-                        CompilerFilter::kQuicken,
-                        extra_args,
-                        expected_success,
-                        /*use_fd*/ false,
-                        check_oat);
+    ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                    odex_location,
+                                    CompilerFilter::kQuicken,
+                                    extra_args,
+                                    expected_success,
+                                    /*use_fd*/ false,
+                                    check_oat));
   }
 
   std::string GetUsedDexLocation() {
@@ -1135,11 +1150,11 @@
 
   Copy(GetDexSrc1(), stripped_classpath);
 
-  GenerateOdexForTest(stripped_classpath,
-                      odex_for_classpath,
-                      CompilerFilter::kQuicken,
-                      {},
-                      true);
+  ASSERT_TRUE(GenerateOdexForTest(stripped_classpath,
+                                  odex_for_classpath,
+                                  CompilerFilter::kQuicken,
+                                  {},
+                                  true));
 
   // Strip the dex file
   Copy(GetStrippedDexSrc1(), stripped_classpath);
@@ -1216,7 +1231,7 @@
       CompilerFilter::Filter::kQuicken,
       &error_msg,
       {"--force-determinism", "--avoid-storing-invocation"});
-  EXPECT_EQ(res, 0);
+  ASSERT_EQ(res, 0);
   Copy(base_oat_name, unload_oat_name);
   Copy(base_vdex_name, unload_vdex_name);
   std::unique_ptr<File> unload_oat(OS::OpenFileForReading(unload_oat_name.c_str()));
@@ -1233,7 +1248,7 @@
       CompilerFilter::Filter::kQuicken,
       &error_msg,
       {"--force-determinism", "--avoid-storing-invocation", "--app-image-file=" + app_image_name});
-  EXPECT_EQ(res2, 0);
+  ASSERT_EQ(res2, 0);
   Copy(base_oat_name, no_unload_oat_name);
   Copy(base_vdex_name, no_unload_vdex_name);
   std::unique_ptr<File> no_unload_oat(OS::OpenFileForReading(no_unload_oat_name.c_str()));
@@ -1535,26 +1550,26 @@
   std::string out_dir = GetScratchDir();
   const std::string base_oat_name = out_dir + "/base.oat";
   size_t no_dedupe_size = 0;
-  GenerateOdexForTest(dex->GetLocation(),
-                      base_oat_name,
-                      CompilerFilter::Filter::kSpeed,
-                      { "--deduplicate-code=false" },
-                      true,  // expect_success
-                      false,  // use_fd
-                      [&no_dedupe_size](const OatFile& o) {
-                        no_dedupe_size = o.Size();
-                      });
+  ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(),
+                                  base_oat_name,
+                                  CompilerFilter::Filter::kSpeed,
+                                  { "--deduplicate-code=false" },
+                                  true,  // expect_success
+                                  false,  // use_fd
+                                  [&no_dedupe_size](const OatFile& o) {
+                                    no_dedupe_size = o.Size();
+                                  }));
 
   size_t dedupe_size = 0;
-  GenerateOdexForTest(dex->GetLocation(),
-                      base_oat_name,
-                      CompilerFilter::Filter::kSpeed,
-                      { "--deduplicate-code=true" },
-                      true,  // expect_success
-                      false,  // use_fd
-                      [&dedupe_size](const OatFile& o) {
-                        dedupe_size = o.Size();
-                      });
+  ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(),
+                                  base_oat_name,
+                                  CompilerFilter::Filter::kSpeed,
+                                  { "--deduplicate-code=true" },
+                                  true,  // expect_success
+                                  false,  // use_fd
+                                  [&dedupe_size](const OatFile& o) {
+                                    dedupe_size = o.Size();
+                                  }));
 
   EXPECT_LT(dedupe_size, no_dedupe_size);
 }
@@ -1563,15 +1578,15 @@
   std::unique_ptr<const DexFile> dex(OpenTestDexFile("MainUncompressed"));
   std::string out_dir = GetScratchDir();
   const std::string base_oat_name = out_dir + "/base.oat";
-  GenerateOdexForTest(dex->GetLocation(),
-                      base_oat_name,
-                      CompilerFilter::Filter::kQuicken,
-                      { },
-                      true,  // expect_success
-                      false,  // use_fd
-                      [](const OatFile& o) {
-                        CHECK(!o.ContainsDexCode());
-                      });
+  ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(),
+                                  base_oat_name,
+                                  CompilerFilter::Filter::kQuicken,
+                                  { },
+                                  true,  // expect_success
+                                  false,  // use_fd
+                                  [](const OatFile& o) {
+                                    CHECK(!o.ContainsDexCode());
+                                  }));
 }
 
 TEST_F(Dex2oatTest, EmptyUncompressedDexTest) {
@@ -1667,15 +1682,15 @@
   std::string out_dir = GetScratchDir();
   const std::string oat_filename = out_dir + "/base.oat";
   // The dex won't pass the method verifier, only use the verify filter.
-  GenerateOdexForTest(temp_dex.GetFilename(),
-                      oat_filename,
-                      CompilerFilter::Filter::kVerify,
-                      { },
-                      true,  // expect_success
-                      false,  // use_fd
-                      [](const OatFile& o) {
-                        CHECK(o.ContainsDexCode());
-                      });
+  ASSERT_TRUE(GenerateOdexForTest(temp_dex.GetFilename(),
+                                  oat_filename,
+                                  CompilerFilter::Filter::kVerify,
+                                  { },
+                                  true,  // expect_success
+                                  false,  // use_fd
+                                  [](const OatFile& o) {
+                                    CHECK(o.ContainsDexCode());
+                                  }));
   // Open our generated oat file.
   std::string error_msg;
   std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
@@ -1718,11 +1733,11 @@
   }
   const std::string& dex_location = apk_file.GetFilename();
   const std::string odex_location = GetOdexDir() + "/output.odex";
-  GenerateOdexForTest(dex_location,
-                      odex_location,
-                      CompilerFilter::kQuicken,
-                      { "--compact-dex-level=fast" },
-                      true);
+  ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                  odex_location,
+                                  CompilerFilter::kQuicken,
+                                  { "--compact-dex-level=fast" },
+                                  true));
 }
 
 TEST_F(Dex2oatTest, StderrLoggerOutput) {
@@ -1732,11 +1747,11 @@
   // Test file doesn't matter.
   Copy(GetDexSrc1(), dex_location);
 
-  GenerateOdexForTest(dex_location,
-                      odex_location,
-                      CompilerFilter::kQuicken,
-                      { "--runtime-arg", "-Xuse-stderr-logger" },
-                      true);
+  ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                  odex_location,
+                                  CompilerFilter::kQuicken,
+                                  { "--runtime-arg", "-Xuse-stderr-logger" },
+                                  true));
   // Look for some random part of dex2oat logging. With the stderr logger this should be captured,
   // even on device.
   EXPECT_NE(std::string::npos, output_.find("dex2oat took"));
@@ -1749,11 +1764,11 @@
   // Test file doesn't matter.
   Copy(GetDexSrc1(), dex_location);
 
-  GenerateOdexForTest(dex_location,
-                      odex_location,
-                      CompilerFilter::kVerify,
-                      { "--compilation-reason=install" },
-                      true);
+  ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                  odex_location,
+                                  CompilerFilter::kVerify,
+                                  { "--compilation-reason=install" },
+                                  true));
   std::string error_msg;
   std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
                                                    odex_location.c_str(),
@@ -1774,11 +1789,11 @@
   // Test file doesn't matter.
   Copy(GetDexSrc1(), dex_location);
 
-  GenerateOdexForTest(dex_location,
-                      odex_location,
-                      CompilerFilter::kVerify,
-                      {},
-                      true);
+  ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                  odex_location,
+                                  CompilerFilter::kVerify,
+                                  {},
+                                  true));
   std::string error_msg;
   std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
                                                    odex_location.c_str(),
@@ -1799,14 +1814,13 @@
   const std::string dex_location = dex->GetLocation();
   const std::string odex_location = out_dir + "/base.oat";
   const std::string vdex_location = out_dir + "/base.vdex";
-  GenerateOdexForTest(dex_location,
-                      odex_location,
-                      CompilerFilter::Filter::kVerify,
-                      { "--copy-dex-files=false" },
-                      true,  // expect_success
-                      false,  // use_fd
-                      [](const OatFile&) {
-                      });
+  ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                  odex_location,
+                                  CompilerFilter::Filter::kVerify,
+                                  { "--copy-dex-files=false" },
+                                  true,  // expect_success
+                                  false,  // use_fd
+                                  [](const OatFile&) {}));
   {
     // Check the vdex doesn't have dex.
     std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location.c_str(),
@@ -1856,19 +1870,21 @@
   }
 
   auto generate_and_check = [&](CompilerFilter::Filter filter) {
-    GenerateOdexForTest(dex_location,
-                        odex_location,
-                        filter,
-                        { "--dump-timings",
-                          "--dm-file=" + dm_file.GetFilename(),
-                          // Pass -Xuse-stderr-logger have dex2oat output in output_ on target.
-                          "--runtime-arg",
-                          "-Xuse-stderr-logger" },
-                        true,  // expect_success
-                        false,  // use_fd
-                        [](const OatFile& o) {
-                          CHECK(o.ContainsDexCode());
-                        });
+    output_.clear();
+    ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                    odex_location,
+                                    filter,
+                                    { "--dump-timings",
+                                      "--dm-file=" + dm_file.GetFilename(),
+                                      // Pass -Xuse-stderr-logger have dex2oat output in output_ on
+                                      // target.
+                                      "--runtime-arg",
+                                      "-Xuse-stderr-logger" },
+                                    true,  // expect_success
+                                    false,  // use_fd
+                                    [](const OatFile& o) {
+                                      CHECK(o.ContainsDexCode());
+                                    }));
     // Check the output for "Fast verify", this is printed from --dump-timings.
     std::istringstream iss(output_);
     std::string line;
@@ -1916,14 +1932,14 @@
   {
     std::string input_vdex = "--input-vdex-fd=-1";
     std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_output->Fd());
-    GenerateOdexForTest(dex_location,
-                        odex_location,
-                        CompilerFilter::kQuicken,
-                        // Disable cdex since we want to compare against the original dex file
-                        // after unquickening.
-                        { input_vdex, output_vdex, kDisableCompactDex },
-                        /*expect_success=*/ true,
-                        /*use_fd=*/ true);
+    ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                    odex_location,
+                                    CompilerFilter::kQuicken,
+                                    // Disable cdex since we want to compare against the original
+                                    // dex file after unquickening.
+                                    { input_vdex, output_vdex, kDisableCompactDex },
+                                    /* expect_success= */ true,
+                                    /* use_fd= */ true));
   }
   // Unquicken by running the verify compiler filter on the vdex file and verify it matches.
   std::string odex_location2 = GetOdexDir() + "/unquickened.odex";
@@ -1932,13 +1948,14 @@
   {
     std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_output->Fd());
     std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_unquickened->Fd());
-    GenerateOdexForTest(dex_location,
-                        odex_location2,
-                        CompilerFilter::kVerify,
-                        // Disable cdex to avoid needing to write out the shared section.
-                        { input_vdex, output_vdex, kDisableCompactDex },
-                        /*expect_success=*/ true,
-                        /*use_fd=*/ true);
+    ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                    odex_location2,
+                                    CompilerFilter::kVerify,
+                                    // Disable cdex to avoid needing to write out the shared
+                                    // section.
+                                    { input_vdex, output_vdex, kDisableCompactDex },
+                                    /* expect_success= */ true,
+                                    /* use_fd= */ true));
   }
   ASSERT_EQ(vdex_unquickened->Flush(), 0) << "Could not flush and close vdex file";
   ASSERT_TRUE(success_);
@@ -2046,13 +2063,13 @@
   ScratchFile app_image_file;
   const std::string out_dir = GetScratchDir();
   const std::string odex_location = out_dir + "/base.odex";
-  GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
-                      odex_location,
-                      CompilerFilter::Filter::kSpeedProfile,
-                      { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) },
-                      true,  // expect_success
-                      false,  // use_fd
-                      [](const OatFile&) {});
+  ASSERT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+                                  odex_location,
+                                  CompilerFilter::Filter::kSpeedProfile,
+                                  { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) },
+                                  true,  // expect_success
+                                  false,  // use_fd
+                                  [](const OatFile&) {}));
   // Open our generated oat file.
   std::string error_msg;
   std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
@@ -2105,15 +2122,15 @@
   const std::string out_dir = GetScratchDir();
   const std::string odex_location = out_dir + "/base.odex";
   const std::string app_image_location = out_dir + "/base.art";
-  GenerateOdexForTest(GetTestDexFileName("StringLiterals"),
-                      odex_location,
-                      CompilerFilter::Filter::kSpeedProfile,
-                      { "--app-image-file=" + app_image_location,
-                        "--resolve-startup-const-strings=true",
-                        "--profile-file=" + profile_file.GetFilename()},
-                      /*expect_success=*/ true,
-                      /*use_fd=*/ false,
-                      [](const OatFile&) {});
+  ASSERT_TRUE(GenerateOdexForTest(GetTestDexFileName("StringLiterals"),
+                                  odex_location,
+                                  CompilerFilter::Filter::kSpeedProfile,
+                                  { "--app-image-file=" + app_image_location,
+                                    "--resolve-startup-const-strings=true",
+                                    "--profile-file=" + profile_file.GetFilename()},
+                                  /* expect_success= */ true,
+                                  /* use_fd= */ false,
+                                  [](const OatFile&) {}));
   // Open our generated oat file.
   std::string error_msg;
   std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
@@ -2220,27 +2237,27 @@
   }
   expected_stored_context +=    + "]";
   // The class path should not be valid and should fail being stored.
-  GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
-                      odex_location,
-                      CompilerFilter::Filter::kQuicken,
-                      { "--class-loader-context=" + stored_context },
-                      true,  // expect_success
-                      false,  // use_fd
-                      [&](const OatFile& oat_file) {
+  EXPECT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+                                  odex_location,
+                                  CompilerFilter::Filter::kQuicken,
+                                  { "--class-loader-context=" + stored_context },
+                                  true,  // expect_success
+                                  false,  // use_fd
+                                  [&](const OatFile& oat_file) {
     EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context) << output_;
     EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context) << output_;
-  });
+  }));
   // The stored context should match what we expect even though it's invalid.
-  GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
-                      odex_location,
-                      CompilerFilter::Filter::kQuicken,
-                      { "--class-loader-context=" + valid_context,
-                        "--stored-class-loader-context=" + stored_context },
-                      true,  // expect_success
-                      false,  // use_fd
-                      [&](const OatFile& oat_file) {
+  EXPECT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+                                  odex_location,
+                                  CompilerFilter::Filter::kQuicken,
+                                  { "--class-loader-context=" + valid_context,
+                                    "--stored-class-loader-context=" + stored_context },
+                                  true,  // expect_success
+                                  false,  // use_fd
+                                  [&](const OatFile& oat_file) {
     EXPECT_EQ(oat_file.GetClassLoaderContext(), expected_stored_context) << output_;
-  });
+  }));
 }
 
 }  // namespace art
diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc
index b628c9e..69dac19 100644
--- a/dex2oat/linker/image_test.cc
+++ b/dex2oat/linker/image_test.cc
@@ -99,9 +99,9 @@
 TEST_F(ImageTest, TestDefaultMethods) {
   CompilationHelper helper;
   Compile(ImageHeader::kStorageModeUncompressed,
-      helper,
-      "DefaultMethods",
-      {"LIface;", "LImpl;", "LIterableBase;"});
+          helper,
+          "DefaultMethods",
+          {"LIface;", "LImpl;", "LIterableBase;"});
 
   PointerSize pointer_size = class_linker_->GetImagePointerSize();
   Thread* self = Thread::Current();
@@ -152,5 +152,17 @@
   ASSERT_TRUE(class_linker_->IsQuickToInterpreterBridge(code));
 }
 
+// Regression test for dex2oat crash for soft verification failure during
+// class initialization check from the transactional interpreter while
+// running the class initializer for another class.
+TEST_F(ImageTest, TestSoftVerificationFailureDuringClassInitialization) {
+  CompilationHelper helper;
+  Compile(ImageHeader::kStorageModeUncompressed,
+          helper,
+          "VerifySoftFailDuringClinit",
+          /*image_classes=*/ {"LClassToInitialize;"},
+          /*image_classes_failing_aot_clinit=*/ {"LClassToInitialize;"});
+}
+
 }  // namespace linker
 }  // namespace art
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 443ee52..9d1a4e7 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -28,6 +28,7 @@
 #include "art_method-inl.h"
 #include "base/file_utils.h"
 #include "base/hash_set.h"
+#include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "base/utils.h"
 #include "class_linker-inl.h"
@@ -81,7 +82,8 @@
   void Compile(ImageHeader::StorageMode storage_mode,
                /*out*/ CompilationHelper& out_helper,
                const std::string& extra_dex = "",
-               const std::initializer_list<std::string>& image_classes = {});
+               const std::initializer_list<std::string>& image_classes = {},
+               const std::initializer_list<std::string>& image_classes_failing_aot_clinit = {});
 
   void SetUpRuntimeOptions(RuntimeOptions* options) override {
     CommonCompilerTest::SetUpRuntimeOptions(options);
@@ -370,10 +372,15 @@
   }
 }
 
-inline void ImageTest::Compile(ImageHeader::StorageMode storage_mode,
-                        CompilationHelper& helper,
-                        const std::string& extra_dex,
-                        const std::initializer_list<std::string>& image_classes) {
+inline void ImageTest::Compile(
+    ImageHeader::StorageMode storage_mode,
+    CompilationHelper& helper,
+    const std::string& extra_dex,
+    const std::initializer_list<std::string>& image_classes,
+    const std::initializer_list<std::string>& image_classes_failing_aot_clinit) {
+  for (const std::string& image_class : image_classes_failing_aot_clinit) {
+    ASSERT_TRUE(ContainsElement(image_classes, image_class));
+  }
   for (const std::string& image_class : image_classes) {
     image_classes_.insert(image_class);
   }
@@ -394,7 +401,12 @@
       ObjPtr<mirror::Class> klass =
           class_linker->FindSystemClass(Thread::Current(), image_class.c_str());
       EXPECT_TRUE(klass != nullptr);
-      EXPECT_TRUE(klass->IsInitialized());
+      EXPECT_TRUE(klass->IsResolved());
+      if (ContainsElement(image_classes_failing_aot_clinit, image_class)) {
+        EXPECT_FALSE(klass->IsInitialized());
+      } else {
+        EXPECT_TRUE(klass->IsInitialized());
+      }
     }
   }
 }
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 5ca7f07..e4fbd17 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -613,8 +613,22 @@
   CHECK(!oat_filenames.empty());
   CHECK_EQ(image_filenames.size(), oat_filenames.size());
 
+  Thread* self = Thread::Current();
   {
-    ScopedObjectAccess soa(Thread::Current());
+    // Preload deterministic contents to the dex cache arrays we're going to write.
+    ScopedObjectAccess soa(self);
+    ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
+    std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
+    for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
+      if (compile_app_image_ && IsInBootImage(dex_cache.Ptr())) {
+        continue;  // Boot image DexCache is not written to the app image.
+      }
+      PreloadDexCache(dex_cache, class_loader);
+    }
+  }
+
+  {
+    ScopedObjectAccess soa(self);
     for (size_t i = 0; i < oat_filenames.size(); ++i) {
       CreateHeader(i);
       CopyAndFixupNativeData(i);
@@ -623,7 +637,7 @@
 
   {
     // TODO: heap validation can't handle these fix up passes.
-    ScopedObjectAccess soa(Thread::Current());
+    ScopedObjectAccess soa(self);
     Runtime::Current()->GetHeap()->DisableObjectValidation();
     CopyAndFixupObjects();
   }
@@ -1409,8 +1423,79 @@
   Runtime::Current()->GetClassLinker()->VisitClassLoaders(visitor);
 }
 
-void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
-                                          ObjPtr<mirror::ClassLoader> class_loader) {
+void ImageWriter::PruneDexCache(ObjPtr<mirror::DexCache> dex_cache,
+                                ObjPtr<mirror::ClassLoader> class_loader) {
+  Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  const DexFile& dex_file = *dex_cache->GetDexFile();
+  // Prune methods.
+  dex::TypeIndex last_class_idx;  // Initialized to invalid index.
+  ObjPtr<mirror::Class> last_class = nullptr;
+  mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
+  for (size_t slot_idx = 0, num = dex_cache->NumResolvedMethods(); slot_idx != num; ++slot_idx) {
+    auto pair =
+        mirror::DexCache::GetNativePairPtrSize(resolved_methods, slot_idx, target_ptr_size_);
+    uint32_t stored_index = pair.index;
+    ArtMethod* method = pair.object;
+    if (method == nullptr) {
+      continue;  // Empty entry.
+    }
+    // Check if the referenced class is in the image. Note that we want to check the referenced
+    // class rather than the declaring class to preserve the semantics, i.e. using a MethodId
+    // results in resolving the referenced class and that can for example throw OOME.
+    const DexFile::MethodId& method_id = dex_file.GetMethodId(stored_index);
+    if (method_id.class_idx_ != last_class_idx) {
+      last_class_idx = method_id.class_idx_;
+      last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
+      if (last_class != nullptr && !KeepClass(last_class)) {
+        last_class = nullptr;
+      }
+    }
+    if (last_class == nullptr) {
+      dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_);
+    }
+  }
+  // Prune fields.
+  mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
+  last_class_idx = dex::TypeIndex();  // Initialized to invalid index.
+  last_class = nullptr;
+  for (size_t slot_idx = 0, num = dex_cache->NumResolvedFields(); slot_idx != num; ++slot_idx) {
+    auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, slot_idx, target_ptr_size_);
+    uint32_t stored_index = pair.index;
+    ArtField* field = pair.object;
+    if (field == nullptr) {
+      continue;  // Empty entry.
+    }
+    // Check if the referenced class is in the image. Note that we want to check the referenced
+    // class rather than the declaring class to preserve the semantics, i.e. using a FieldId
+    // results in resolving the referenced class and that can for example throw OOME.
+    const DexFile::FieldId& field_id = dex_file.GetFieldId(stored_index);
+    if (field_id.class_idx_ != last_class_idx) {
+      last_class_idx = field_id.class_idx_;
+      last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
+      if (last_class != nullptr && !KeepClass(last_class)) {
+        last_class = nullptr;
+      }
+    }
+    if (last_class == nullptr) {
+      dex_cache->ClearResolvedField(stored_index, target_ptr_size_);
+    }
+  }
+  // Prune types.
+  for (size_t slot_idx = 0, num = dex_cache->NumResolvedTypes(); slot_idx != num; ++slot_idx) {
+    mirror::TypeDexCachePair pair =
+        dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed);
+    uint32_t stored_index = pair.index;
+    ObjPtr<mirror::Class> klass = pair.object.Read();
+    if (klass != nullptr && !KeepClass(klass)) {
+      dex_cache->ClearResolvedType(dex::TypeIndex(stored_index));
+    }
+  }
+  // Strings do not need pruning.
+}
+
+void ImageWriter::PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
+                                  ObjPtr<mirror::ClassLoader> class_loader) {
   // To ensure deterministic contents of the hash-based arrays, each slot shall contain
   // the candidate with the lowest index. As we're processing entries in increasing index
   // order, this means trying to look up the entry for the current index if the slot is
@@ -1419,7 +1504,7 @@
   Runtime* runtime = Runtime::Current();
   ClassLinker* class_linker = runtime->GetClassLinker();
   const DexFile& dex_file = *dex_cache->GetDexFile();
-  // Prune methods.
+  // Preload the methods array and make the contents deterministic.
   mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
   dex::TypeIndex last_class_idx;  // Initialized to invalid index.
   ObjPtr<mirror::Class> last_class = nullptr;
@@ -1439,27 +1524,20 @@
     if (method_id.class_idx_ != last_class_idx) {
       last_class_idx = method_id.class_idx_;
       last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
-      if (last_class != nullptr && !KeepClass(last_class)) {
-        last_class = nullptr;
-      }
     }
     if (method == nullptr || i < stored_index) {
       if (last_class != nullptr) {
         // Try to resolve the method with the class linker, which will insert
         // it into the dex cache if successful.
         method = class_linker->FindResolvedMethod(last_class, dex_cache, class_loader, i);
-        // If the referenced class is in the image, the defining class must also be there.
-        DCHECK(method == nullptr || KeepClass(method->GetDeclaringClass()));
         DCHECK(method == nullptr || dex_cache->GetResolvedMethod(i, target_ptr_size_) == method);
       }
     } else {
       DCHECK_EQ(i, stored_index);
-      if (last_class == nullptr) {
-        dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_);
-      }
+      DCHECK(last_class != nullptr);
     }
   }
-  // Prune fields and make the contents of the field array deterministic.
+  // Preload the fields array and make the contents deterministic.
   mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
   last_class_idx = dex::TypeIndex();  // Initialized to invalid index.
   last_class = nullptr;
@@ -1484,19 +1562,17 @@
     }
     if (field == nullptr || i < stored_index) {
       if (last_class != nullptr) {
+        // Try to resolve the field with the class linker, which will insert
+        // it into the dex cache if successful.
         field = class_linker->FindResolvedFieldJLS(last_class, dex_cache, class_loader, i);
-        // If the referenced class is in the image, the defining class must also be there.
-        DCHECK(field == nullptr || KeepClass(field->GetDeclaringClass()));
         DCHECK(field == nullptr || dex_cache->GetResolvedField(i, target_ptr_size_) == field);
       }
     } else {
       DCHECK_EQ(i, stored_index);
-      if (last_class == nullptr) {
-        dex_cache->ClearResolvedField(stored_index, target_ptr_size_);
-      }
+      DCHECK(last_class != nullptr);
     }
   }
-  // Prune types and make the contents of the type array deterministic.
+  // Preload the types array and make the contents deterministic.
   // This is done after fields and methods as their lookup can touch the types array.
   for (size_t i = 0, end = dex_cache->GetDexFile()->NumTypeIds(); i < end; ++i) {
     dex::TypeIndex type_idx(i);
@@ -1507,15 +1583,10 @@
     ObjPtr<mirror::Class> klass = pair.object.Read();
     if (klass == nullptr || i < stored_index) {
       klass = class_linker->LookupResolvedType(type_idx, dex_cache, class_loader);
-      if (klass != nullptr) {
-        DCHECK_EQ(dex_cache->GetResolvedType(type_idx), klass);
-        stored_index = i;  // For correct clearing below if not keeping the `klass`.
-      }
-    } else if (i == stored_index && !KeepClass(klass)) {
-      dex_cache->ClearResolvedType(dex::TypeIndex(stored_index));
+      DCHECK(klass == nullptr || dex_cache->GetResolvedType(type_idx) == klass);
     }
   }
-  // Strings do not need pruning, but the contents of the string array must be deterministic.
+  // Preload the strings array and make the contents deterministic.
   for (size_t i = 0, end = dex_cache->GetDexFile()->NumStringIds(); i < end; ++i) {
     dex::StringIndex string_idx(i);
     uint32_t slot_idx = dex_cache->StringSlotIndex(string_idx);
@@ -1564,21 +1635,11 @@
   }
 
   // Clear references to removed classes from the DexCaches.
-  std::vector<ObjPtr<mirror::DexCache>> dex_caches;
-  {
-    ReaderMutexLock mu2(self, *Locks::dex_lock_);
-    dex_caches.reserve(class_linker->GetDexCachesData().size());
-    for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
-      if (self->IsJWeakCleared(data.weak_root)) {
-        continue;
-      }
-      dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache());
-    }
-  }
+  std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
   for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
     // Pass the class loader associated with the DexCache. This can either be
     // the app's `class_loader` or `nullptr` if boot class loader.
-    PruneAndPreloadDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader);
+    PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader);
   }
 
   // Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
@@ -1588,6 +1649,20 @@
   prune_class_memo_.clear();
 }
 
+std::vector<ObjPtr<mirror::DexCache>> ImageWriter::FindDexCaches(Thread* self) {
+  std::vector<ObjPtr<mirror::DexCache>> dex_caches;
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  ReaderMutexLock mu2(self, *Locks::dex_lock_);
+  dex_caches.reserve(class_linker->GetDexCachesData().size());
+  for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+    if (self->IsJWeakCleared(data.weak_root)) {
+      continue;
+    }
+    dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache());
+  }
+  return dex_caches;
+}
+
 void ImageWriter::CheckNonImageClassesRemoved() {
   auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
     if (obj->IsClass() && !IsInBootImage(obj)) {
@@ -2670,10 +2745,8 @@
 
 void ImageWriter::FixupPointerArray(mirror::Object* dst,
                                     mirror::PointerArray* arr,
-                                    mirror::Class* klass,
                                     Bin array_type) {
-  CHECK(klass->IsArrayClass());
-  CHECK(arr->IsIntArray() || arr->IsLongArray()) << klass->PrettyClass() << " " << arr;
+  CHECK(arr->IsIntArray() || arr->IsLongArray()) << arr->GetClass()->PrettyClass() << " " << arr;
   // Fixup int and long pointers for the ArtMethod or ArtField arrays.
   const size_t num_elements = arr->GetLength();
   CopyAndFixupReference(
@@ -2879,13 +2952,12 @@
   if (kUseBakerReadBarrier) {
     orig->AssertReadBarrierState();
   }
-  auto* klass = orig->GetClass();
-  if (klass->IsIntArrayClass() || klass->IsLongArrayClass()) {
+  if (orig->IsIntArray() || orig->IsLongArray()) {
     // Is this a native pointer array?
     auto it = pointer_arrays_.find(down_cast<mirror::PointerArray*>(orig));
     if (it != pointer_arrays_.end()) {
       // Should only need to fixup every pointer array exactly once.
-      FixupPointerArray(copy, down_cast<mirror::PointerArray*>(orig), klass, it->second);
+      FixupPointerArray(copy, down_cast<mirror::PointerArray*>(orig), it->second);
       pointer_arrays_.erase(it);
       return;
     }
@@ -2895,6 +2967,7 @@
   } else {
     ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots =
         Runtime::Current()->GetClassLinker()->GetClassRoots();
+    ObjPtr<mirror::Class> klass = orig->GetClass();
     if (klass == GetClassRoot<mirror::Method>(class_roots) ||
         klass == GetClassRoot<mirror::Constructor>(class_roots)) {
       // Need to go update the ArtMethod.
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 06c694c..bf89665 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -460,9 +460,18 @@
   // Remove unwanted classes from various roots.
   void PruneNonImageClasses() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Remove unwanted classes from the DexCache roots and preload deterministic DexCache contents.
-  void PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
-                               ObjPtr<mirror::ClassLoader> class_loader)
+  // Remove unwanted classes from the DexCache roots.
+  void PruneDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::classlinker_classes_lock_);
+
+  // Preload deterministic DexCache contents.
+  void PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::classlinker_classes_lock_);
+
+  // Find dex caches for pruning or preloading.
+  std::vector<ObjPtr<mirror::DexCache>> FindDexCaches(Thread* self)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::classlinker_classes_lock_);
 
@@ -541,7 +550,6 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
   void FixupPointerArray(mirror::Object* dst,
                          mirror::PointerArray* arr,
-                         mirror::Class* klass,
                          Bin array_type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 01c24fc..7f2877f 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -766,10 +766,6 @@
   bss_start_ = (bss_size_ != 0u) ? RoundUp(oat_size_, kPageSize) : 0u;
 
   CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
-  if (GetCompilerOptions().IsBootImage()) {
-    CHECK_EQ(image_writer_ != nullptr,
-             oat_header_->GetStoreValueByKey(OatHeader::kImageLocationKey) == nullptr);
-  }
 
   write_state_ = WriteState::kWriteRoData;
 }
@@ -1504,7 +1500,8 @@
     }
     ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), *dex_file);
     const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
-    mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_);
+    ObjPtr<mirror::Class> klass =
+        class_linker_->LookupResolvedType(class_def.class_idx_, dex_cache, class_loader_);
     if (klass != nullptr) {
       for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) {
         // Find origin method. Declaring class and dex_method_idx
@@ -1554,24 +1551,11 @@
     ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(self, *dex_file_);
     ArtMethod* resolved_method;
     if (writer_->GetCompilerOptions().IsBootImage()) {
-      const InvokeType invoke_type = method.GetInvokeType(
-          dex_file_->GetClassDef(class_def_index_).access_flags_);
-      // Unchecked as we hold mutator_lock_ on entry.
-      ScopedObjectAccessUnchecked soa(self);
-      StackHandleScope<1> hs(self);
-      resolved_method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
-          method.GetIndex(),
-          hs.NewHandle(dex_cache),
-          ScopedNullHandle<mirror::ClassLoader>(),
-          /* referrer */ nullptr,
-          invoke_type);
+      resolved_method = class_linker_->LookupResolvedMethod(
+          method.GetIndex(), dex_cache, /*class_loader=*/ nullptr);
       if (resolved_method == nullptr) {
-        LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: "
+        LOG(FATAL) << "Unexpected failure to look up a method: "
             << dex_file_->PrettyMethod(method.GetIndex(), true);
-        self->AssertPendingException();
-        mirror::Throwable* exc = self->GetException();
-        std::string dump = exc->Dump();
-        LOG(FATAL) << dump;
         UNREACHABLE();
       }
     } else {
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 83fb17c..af02bfc 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -394,7 +394,6 @@
 
   ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
-  key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
   bool success = WriteElf(tmp_vdex.GetFile(),
                           tmp_oat.GetFile(),
                           class_linker->GetBootClassPath(),
@@ -418,7 +417,6 @@
   ASSERT_TRUE(oat_header.IsValid());
   ASSERT_EQ(class_linker->GetBootClassPath().size(), oat_header.GetDexFileCount());  // core
   ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum());
-  ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey)));
 
   ASSERT_TRUE(java_lang_dex_file_ != nullptr);
   const DexFile& dex_file = *java_lang_dex_file_;
@@ -517,7 +515,6 @@
 
   ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
-  key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
   bool success = WriteElf(tmp_vdex.GetFile(),
                           tmp_oat.GetFile(),
                           dex_files,
@@ -584,7 +581,6 @@
 
   ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
-  key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
   std::unique_ptr<ProfileCompilationInfo>
       profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr);
   success = WriteElf(tmp_vdex.GetFile(),
@@ -714,7 +710,6 @@
   ASSERT_TRUE(success) << strerror(errno);
 
   SafeMap<std::string, std::string> key_value_store;
-  key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
   {
     // Test using the AddDexFileSource() interface with the zip file.
     std::vector<const char*> input_filenames = { zip_file.GetFilename().c_str() };
@@ -831,7 +826,6 @@
   ASSERT_TRUE(success) << strerror(errno);
 
   SafeMap<std::string, std::string> key_value_store;
-  key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
   std::vector<const char*> input_filenames = { zip_file.GetFilename().c_str() };
   ScratchFile oat_file, vdex_file(oat_file, ".vdex");
   std::unique_ptr<ProfileCompilationInfo> profile_compilation_info(new ProfileCompilationInfo());
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
index 99a11cd..72896c8 100644
--- a/dexoptanalyzer/Android.bp
+++ b/dexoptanalyzer/Android.bp
@@ -24,6 +24,7 @@
 
     target: {
         android: {
+            // Use the 32-bit version of dexoptanalyzer on devices.
             compile_multilib: "prefer32",
         },
     },
diff --git a/disassembler/Android.bp b/disassembler/Android.bp
index 241b191..5aa159e 100644
--- a/disassembler/Android.bp
+++ b/disassembler/Android.bp
@@ -20,11 +20,39 @@
     host_supported: true,
     srcs: [
         "disassembler.cc",
-        "disassembler_arm.cc",
-        "disassembler_arm64.cc",
         "disassembler_mips.cc",
         "disassembler_x86.cc",
     ],
+    codegen: {
+        arm: {
+            srcs: ["disassembler_arm.cc"]
+        },
+        arm64: {
+            srcs: ["disassembler_arm64.cc"]
+        },
+        // TODO: We should also conditionally include the MIPS32/MIPS64 and the
+        // x86/x86-64 disassembler definitions (b/119090273). However, using the
+        // following syntax here:
+        //
+        //   mips: {
+        //       srcs: ["disassembler_mips.cc"]
+        //   },
+        //   mips64: {
+        //       srcs: ["disassembler_mips.cc"]
+        //   },
+        //   x86: {
+        //       srcs: ["disassembler_x86.cc"]
+        //   },
+        //   x86_64: {
+        //       srcs: ["disassembler_x86.cc"]
+        //   },
+        //
+        // does not work, as it generates a file rejected by ninja with this
+        // error message (e.g. on host, where we include all the back ends by
+        // default):
+        //
+        //   FAILED: ninja: out/soong/build.ninja:320768: multiple rules generate out/soong/.intermediates/art/disassembler/libart-disassembler/linux_glibc_x86_64_static/obj/art/disassembler/disassembler_mips.o [-w dupbuild=err]
+    },
     include_dirs: ["art/runtime"],
 
     shared_libs: [
@@ -41,8 +69,7 @@
     defaults: ["libart-disassembler-defaults"],
     shared_libs: [
         // For disassembler_arm*.
-        "libvixl-arm",
-        "libvixl-arm64",
+        "libvixl",
     ],
 }
 
@@ -54,7 +81,6 @@
     ],
     shared_libs: [
         // For disassembler_arm*.
-        "libvixld-arm",
-        "libvixld-arm64",
+        "libvixld",
     ],
 }
diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc
index 262e815..aee690e 100644
--- a/disassembler/disassembler.cc
+++ b/disassembler/disassembler.cc
@@ -21,10 +21,21 @@
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 
-#include "disassembler_arm.h"
-#include "disassembler_arm64.h"
-#include "disassembler_mips.h"
-#include "disassembler_x86.h"
+#ifdef ART_ENABLE_CODEGEN_arm
+# include "disassembler_arm.h"
+#endif
+
+#ifdef ART_ENABLE_CODEGEN_arm64
+# include "disassembler_arm64.h"
+#endif
+
+#if defined(ART_ENABLE_CODEGEN_mips) || defined(ART_ENABLE_CODEGEN_mips64)
+# include "disassembler_mips.h"
+#endif
+
+#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
+# include "disassembler_x86.h"
+#endif
 
 using android::base::StringPrintf;
 
@@ -36,21 +47,35 @@
 }
 
 Disassembler* Disassembler::Create(InstructionSet instruction_set, DisassemblerOptions* options) {
-  if (instruction_set == InstructionSet::kArm || instruction_set == InstructionSet::kThumb2) {
-    return new arm::DisassemblerArm(options);
-  } else if (instruction_set == InstructionSet::kArm64) {
-    return new arm64::DisassemblerArm64(options);
-  } else if (instruction_set == InstructionSet::kMips) {
-    return new mips::DisassemblerMips(options, /* is_o32_abi= */ true);
-  } else if (instruction_set == InstructionSet::kMips64) {
-    return new mips::DisassemblerMips(options, /* is_o32_abi= */ false);
-  } else if (instruction_set == InstructionSet::kX86) {
-    return new x86::DisassemblerX86(options, false);
-  } else if (instruction_set == InstructionSet::kX86_64) {
-    return new x86::DisassemblerX86(options, true);
-  } else {
-    UNIMPLEMENTED(FATAL) << static_cast<uint32_t>(instruction_set);
-    return nullptr;
+  switch (instruction_set) {
+#ifdef ART_ENABLE_CODEGEN_arm
+    case InstructionSet::kArm:
+    case InstructionSet::kThumb2:
+      return new arm::DisassemblerArm(options);
+#endif
+#ifdef ART_ENABLE_CODEGEN_arm64
+    case InstructionSet::kArm64:
+      return new arm64::DisassemblerArm64(options);
+#endif
+#ifdef ART_ENABLE_CODEGEN_mips
+    case InstructionSet::kMips:
+      return new mips::DisassemblerMips(options, /* is_o32_abi= */ true);
+#endif
+#ifdef ART_ENABLE_CODEGEN_mips64
+    case InstructionSet::kMips64:
+      return new mips::DisassemblerMips(options, /* is_o32_abi= */ false);
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86
+    case InstructionSet::kX86:
+      return new x86::DisassemblerX86(options, /* supports_rex= */ false);
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+    case InstructionSet::kX86_64:
+      return new x86::DisassemblerX86(options, /* supports_rex= */ true);
+#endif
+    default:
+      UNIMPLEMENTED(FATAL) << static_cast<uint32_t>(instruction_set);
+      return nullptr;
   }
 }
 
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 5010f68..6a667bc 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -54,6 +54,10 @@
                 "libziparchive",
                 "libz",
             ],
+            // Exclude the version script from Darwin host since it's not
+            // supported by the linker there. That means ASan checks on Darwin
+            // might trigger ODR violations.
+            version_script: "libartbase.map",
         },
         host: {
             shared_libs: [
@@ -61,16 +65,31 @@
                 "libz",
             ],
         },
+        linux_glibc: {
+            version_script: "libartbase.map",
+        },
+        windows: {
+            version_script: "libartbase.map",
+        },
     },
     generated_sources: ["art_libartbase_operator_srcs"],
     cflags: ["-DBUILDING_LIBART=1"],
     shared_libs: [
         "liblog",
-	// For ashmem.
+        // For ashmem.
         "libcutils",
         // For common macros.
         "libbase",
     ],
+
+    // Utilities used by various ART libs and tools are linked in statically
+    // here to avoid shared lib dependencies outside the ART APEX. No target
+    // there should depend on these separately.
+    whole_static_libs: [
+        "liblz4",
+        "liblzma",
+    ],
+
     export_include_dirs: ["."],
     // ART's macros.h depends on libbase's macros.h.
     // Note: runtime_options.h depends on cmdline. But we don't really want to export this
diff --git a/libartbase/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc
index f7c9c5e..c917307 100644
--- a/libartbase/base/file_utils_test.cc
+++ b/libartbase/base/file_utils_test.cc
@@ -83,11 +83,15 @@
   ASSERT_EQ(0, unsetenv("ANDROID_ROOT"));
   std::string android_root3 = GetAndroidRootSafe(&error_msg);
   // This should be the same as the other root (modulo realpath), otherwise the test setup is
-  // broken.
-  UniqueCPtr<char> real_root(realpath(android_root.c_str(), nullptr));
+  // broken. On non-bionic. On bionic we can be running with a different libart that lives outside
+  // of ANDROID_ROOT
   UniqueCPtr<char> real_root3(realpath(android_root3.c_str(), nullptr));
+#if !defined(__BIONIC__ ) || defined(__ANDROID__)
+  UniqueCPtr<char> real_root(realpath(android_root.c_str(), nullptr));
   EXPECT_STREQ(real_root.get(), real_root3.get());
-
+#else
+  EXPECT_STRNE(real_root3.get(), "");
+#endif
 
   // Reset ANDROID_ROOT, as other things may depend on it.
   ASSERT_EQ(0, setenv("ANDROID_ROOT", android_root_env.c_str(), /* overwrite */ 1));
diff --git a/libartbase/base/logging.h b/libartbase/base/logging.h
index d2c0a02..9ded082 100644
--- a/libartbase/base/logging.h
+++ b/libartbase/base/logging.h
@@ -77,12 +77,12 @@
 // performed.
 extern const char* GetCmdLine();
 
-// The command used to start the ART runtime, such as "/system/bin/dalvikvm". If InitLogging hasn't
-// been performed then just returns "art"
+// The command used to start the ART runtime, such as "/apex/com.android.runtime/bin/dalvikvm". If
+// InitLogging hasn't been performed then just returns "art".
 extern const char* ProgramInvocationName();
 
 // A short version of the command used to start the ART runtime, such as "dalvikvm". If InitLogging
-// hasn't been performed then just returns "art"
+// hasn't been performed then just returns "art".
 extern const char* ProgramInvocationShortName();
 
 class LogHelper {
diff --git a/libartbase/base/membarrier.cc b/libartbase/base/membarrier.cc
index def949e..4c86b6b 100644
--- a/libartbase/base/membarrier.cc
+++ b/libartbase/base/membarrier.cc
@@ -25,7 +25,6 @@
 #if defined(__BIONIC__)
 
 #include <atomic>
-#include <android/get_device_api_level.h>
 #include <linux/membarrier.h>
 
 #define CHECK_MEMBARRIER_CMD(art_value, membarrier_value) \
diff --git a/libartbase/libartbase.map b/libartbase/libartbase.map
new file mode 100644
index 0000000..6249930
--- /dev/null
+++ b/libartbase/libartbase.map
@@ -0,0 +1,15 @@
+# This is used only to hide data symbols that get imported through
+# whole_static_libs, or else they might trigger the ASan odr-violation check.
+# Before adding symbols here, please make sure that it doesn't give rise to a
+# real ODR problem. All these symbols are either in .rodata or .data.rel.ro
+# sections.
+LIBARTBASE {
+  local:
+    PPMD7_kExpEscape;
+    XZ_SIG;
+    g_AlignedAlloc;
+    g_Alloc;
+    g_BigAlloc;
+    g_MidAlloc;
+    k7zSignature;
+};
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
index ad8a184..4b38904 100644
--- a/libdexfile/dex/dex_instruction.h
+++ b/libdexfile/dex/dex_instruction.h
@@ -173,7 +173,7 @@
   };
 
   enum VerifyFlag : uint32_t {
-    kVerifyNone               = 0x0000000,
+    kVerifyNothing            = 0x0000000,
     kVerifyRegA               = 0x0000001,
     kVerifyRegAWide           = 0x0000002,
     kVerifyRegB               = 0x0000004,
diff --git a/libdexfile/dex/dex_instruction_list.h b/libdexfile/dex/dex_instruction_list.h
index 9f0aba4..b9540a6 100644
--- a/libdexfile/dex/dex_instruction_list.h
+++ b/libdexfile/dex/dex_instruction_list.h
@@ -19,7 +19,7 @@
 
 // V(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags);
 #define DEX_INSTRUCTION_LIST(V) \
-  V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNone) \
+  V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNothing) \
   V(0x01, MOVE, "move", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
   V(0x02, MOVE_FROM16, "move/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
   V(0x03, MOVE_16, "move/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
@@ -33,7 +33,7 @@
   V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, kIndexNone, kContinue, 0, kVerifyRegAWide) \
   V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
   V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
-  V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
+  V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNothing) \
   V(0x0F, RETURN, "return", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
   V(0x10, RETURN_WIDE, "return-wide", k11x, kIndexNone, kReturn, 0, kVerifyRegAWide) \
   V(0x11, RETURN_OBJECT, "return-object", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
@@ -134,7 +134,7 @@
   V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
   V(0x71, INVOKE_STATIC, "invoke-static", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArg) \
   V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
-  V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
+  V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNothing) \
   V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
   V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
   V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
diff --git a/libdexfile/dex/dex_instruction_test.cc b/libdexfile/dex/dex_instruction_test.cc
index 02400f4..3f79abf 100644
--- a/libdexfile/dex/dex_instruction_test.cc
+++ b/libdexfile/dex/dex_instruction_test.cc
@@ -26,7 +26,7 @@
   EXPECT_EQ(Instruction::k10x, Instruction::FormatOf(nop));
   EXPECT_EQ(Instruction::kIndexNone, Instruction::IndexTypeOf(nop));
   EXPECT_EQ(Instruction::kContinue, Instruction::FlagsOf(nop));
-  EXPECT_EQ(Instruction::kVerifyNone, Instruction::VerifyFlagsOf(nop));
+  EXPECT_EQ(Instruction::kVerifyNothing, Instruction::VerifyFlagsOf(nop));
 }
 
 static void Build45cc(uint8_t num_args, uint16_t method_idx, uint16_t proto_idx,
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 596a946..45f853b 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -100,8 +100,7 @@
     ],
     static_libs: [
         "libart-disassembler",
-        "libvixl-arm",
-        "libvixl-arm64",
+        "libvixl",
     ],
 }
 
@@ -124,8 +123,7 @@
     },
     static_libs: [
         "libartd-disassembler",
-        "libvixld-arm",
-        "libvixld-arm64",
+        "libvixld",
     ],
     group_static_libs: true,
 }
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index 8bac38a..d456d83 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -139,6 +139,9 @@
         // OnLoad since the runtime hasn't started up sufficiently. This is only expected to happen
         // on userdebug/eng builds.
         LOG(INFO) << "Attempting to start jit for openjdkjvmti plugin.";
+        // Note: use rwx allowed = true, because if this is the system server, we will not be
+        //       allowed to allocate any JIT code cache, anyways.
+        runtime->CreateJitCodeCache(/*rwx_memory_allowed=*/true);
         runtime->CreateJit();
         if (runtime->GetJit() == nullptr) {
           LOG(WARNING) << "Could not start jit for openjdkjvmti plugin. This process might be "
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 48df53a..a96436e 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -32,6 +32,7 @@
 #include "events-inl.h"
 
 #include <array>
+#include <sys/time.h>
 
 #include "art_field-inl.h"
 #include "art_jvmti.h"
@@ -56,6 +57,7 @@
 #include "thread-inl.h"
 #include "thread_list.h"
 #include "ti_phase.h"
+#include "well_known_classes.h"
 
 namespace openjdkjvmti {
 
@@ -410,14 +412,103 @@
   EventHandler* handler_;
 };
 
-static void SetupMonitorListener(art::MonitorCallback* listener, bool enable) {
+class JvmtiParkListener : public art::ParkCallback {
+ public:
+  explicit JvmtiParkListener(EventHandler* handler) : handler_(handler) {}
+
+  void ThreadParkStart(bool is_absolute, int64_t timeout)
+      override REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWait)) {
+      art::Thread* self = art::Thread::Current();
+      art::JNIEnvExt* jnienv = self->GetJniEnv();
+      art::ArtField* parkBlockerField = art::jni::DecodeArtField(
+          art::WellKnownClasses::java_lang_Thread_parkBlocker);
+      art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+      if (blocker_obj.IsNull()) {
+        blocker_obj = self->GetPeer();
+      }
+      int64_t timeout_ms;
+      if (!is_absolute) {
+        if (timeout == 0) {
+          timeout_ms = 0;
+        } else {
+          timeout_ms = timeout / 1000000;
+          if (timeout_ms == 0) {
+            // If we were instructed to park for a nonzero number of nanoseconds, but not enough
+            // to be a full millisecond, round up to 1 ms. A nonzero park() call will return
+            // soon, but a 0 wait or park call will wait indefinitely.
+            timeout_ms = 1;
+          }
+        }
+      } else {
+        struct timeval tv;
+        gettimeofday(&tv, (struct timezone *) nullptr);
+        int64_t now = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
+        if (now < timeout) {
+          timeout_ms = timeout - now;
+        } else {
+          // Waiting for 0 ms is an indefinite wait; parking until a time in
+          // the past or the current time will return immediately, so emulate
+          // the shortest possible wait event.
+          timeout_ms = 1;
+        }
+      }
+      ScopedLocalRef<jobject> blocker(jnienv, AddLocalRef<jobject>(jnienv, blocker_obj.Ptr()));
+      RunEventCallback<ArtJvmtiEvent::kMonitorWait>(
+          handler_,
+          self,
+          jnienv,
+          blocker.get(),
+          static_cast<jlong>(timeout_ms));
+    }
+  }
+
+
+  // Our interpretation of the spec is that the JVMTI_EVENT_MONITOR_WAITED will be sent immediately
+  // after a thread has woken up from a sleep caused by a call to Object#wait. If the thread will
+  // never go to sleep (due to not having the lock, having bad arguments, or having an exception
+  // propogated from JVMTI_EVENT_MONITOR_WAIT) we will not send this event.
+  //
+  // This does not fully match the RI semantics. Specifically, we will not send the
+  // JVMTI_EVENT_MONITOR_WAITED event in one situation where the RI would, there was an exception in
+  // the JVMTI_EVENT_MONITOR_WAIT event but otherwise the call was fine. In that case the RI would
+  // send this event and return without going to sleep.
+  //
+  // See b/65558434 for more discussion.
+  void ThreadParkFinished(bool timeout) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWaited)) {
+      art::Thread* self = art::Thread::Current();
+      art::JNIEnvExt* jnienv = self->GetJniEnv();
+      art::ArtField* parkBlockerField = art::jni::DecodeArtField(
+          art::WellKnownClasses::java_lang_Thread_parkBlocker);
+      art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+      if (blocker_obj.IsNull()) {
+        blocker_obj = self->GetPeer();
+      }
+      ScopedLocalRef<jobject> blocker(jnienv, AddLocalRef<jobject>(jnienv, blocker_obj.Ptr()));
+      RunEventCallback<ArtJvmtiEvent::kMonitorWaited>(
+          handler_,
+          self,
+          jnienv,
+          blocker.get(),
+          static_cast<jboolean>(timeout));
+    }
+  }
+
+ private:
+  EventHandler* handler_;
+};
+
+static void SetupMonitorListener(art::MonitorCallback* monitor_listener, art::ParkCallback* park_listener, bool enable) {
   // We must not hold the mutator lock here, but if we're in FastJNI, for example, we might. For
   // now, do a workaround: (possibly) acquire and release.
   art::ScopedObjectAccess soa(art::Thread::Current());
   if (enable) {
-    art::Runtime::Current()->GetRuntimeCallbacks()->AddMonitorCallback(listener);
+    art::Runtime::Current()->GetRuntimeCallbacks()->AddMonitorCallback(monitor_listener);
+    art::Runtime::Current()->GetRuntimeCallbacks()->AddParkCallback(park_listener);
   } else {
-    art::Runtime::Current()->GetRuntimeCallbacks()->RemoveMonitorCallback(listener);
+    art::Runtime::Current()->GetRuntimeCallbacks()->RemoveMonitorCallback(monitor_listener);
+    art::Runtime::Current()->GetRuntimeCallbacks()->RemoveParkCallback(park_listener);
   }
 }
 
@@ -1053,7 +1144,7 @@
     case ArtJvmtiEvent::kMonitorWait:
     case ArtJvmtiEvent::kMonitorWaited:
       if (!OtherMonitorEventsEnabledAnywhere(event)) {
-        SetupMonitorListener(monitor_listener_.get(), enable);
+        SetupMonitorListener(monitor_listener_.get(), park_listener_.get(), enable);
       }
       return;
     default:
@@ -1204,6 +1295,7 @@
   gc_pause_listener_.reset(new JvmtiGcPauseListener(this));
   method_trace_listener_.reset(new JvmtiMethodTraceListener(this));
   monitor_listener_.reset(new JvmtiMonitorListener(this));
+  park_listener_.reset(new JvmtiParkListener(this));
 }
 
 EventHandler::~EventHandler() {
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index 9f91a08..abb15cc 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -35,6 +35,7 @@
 class JvmtiGcPauseListener;
 class JvmtiMethodTraceListener;
 class JvmtiMonitorListener;
+class JvmtiParkListener;
 
 // an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between
 // retransformation capable and incapable loading
@@ -331,6 +332,7 @@
   std::unique_ptr<JvmtiGcPauseListener> gc_pause_listener_;
   std::unique_ptr<JvmtiMethodTraceListener> method_trace_listener_;
   std::unique_ptr<JvmtiMonitorListener> monitor_listener_;
+  std::unique_ptr<JvmtiParkListener> park_listener_;
 
   // True if frame pop has ever been enabled. Since we store pointers to stack frames we need to
   // continue to listen to this event even if it has been disabled.
diff --git a/profman/Android.bp b/profman/Android.bp
index 5aaccb0..89e1f7e 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -26,6 +26,7 @@
 
     target: {
         android: {
+            // Use the 32-bit version of profman on devices.
             compile_multilib: "prefer32",
         },
     },
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 86ee952..410901e 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -377,7 +377,6 @@
     include_dirs: [
         "art/sigchainlib",
         "external/icu/icu4c/source/common",
-        "external/lz4/lib",
         "external/zlib",
     ],
     header_libs: [
@@ -390,7 +389,6 @@
         "libnativebridge",
         "libnativeloader",
         "libbacktrace",
-        "liblz4",
         "liblog",
         // For atrace, properties, ashmem, set_sched_policy.
         "libcutils",
@@ -422,8 +420,6 @@
         "libbase",
         "libcutils",
         "liblog",
-        "liblz4",
-        "liblzma",
         "libnativebridge",
         "libnativeloader",
         "libunwindstack",
@@ -661,8 +657,7 @@
     ],
     shared_libs: [
         "libartd-compiler",
-        "libvixld-arm",
-        "libvixld-arm64",
+        "libvixld",
     ],
 }
 
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index e9c17ee..d8da912 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -402,6 +402,7 @@
       case Intrinsics::kUnsafeLoadFence:
       case Intrinsics::kUnsafeStoreFence:
       case Intrinsics::kUnsafeFullFence:
+      case Intrinsics::kCRC32Update:
         // These intrinsics are on the light greylist and will fail a DCHECK in
         // SetIntrinsic() if their flags change on the respective dex methods.
         // Note that the DCHECK currently won't fail if the dex methods are
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index e775fe4..5daead9 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -91,6 +91,15 @@
     CheckUnattachedThread(level_);
     return;
   }
+  LockLevel level = level_;
+  // It would be nice to avoid this condition checking in the non-debug case,
+  // but that would make the various methods that check if a mutex is held not
+  // work properly for thread wait locks. Since the vast majority of lock
+  // acquisitions are not thread wait locks, this check should not be too
+  // expensive.
+  if (UNLIKELY(level == kThreadWaitLock) && self->GetHeldMutex(kThreadWaitLock) != nullptr) {
+    level = kThreadWaitWakeLock;
+  }
   if (kDebugLocking) {
     // Check if a bad Mutex of this level or lower is held.
     bool bad_mutexes_held = false;
@@ -98,13 +107,13 @@
     // mutator_lock_ exclusive. This is because we suspending when holding locks at this level is
     // not allowed and if we hold the mutator_lock_ exclusive we must unsuspend stuff eventually
     // so there are no deadlocks.
-    if (level_ == kTopLockLevel &&
+    if (level == kTopLockLevel &&
         Locks::mutator_lock_->IsSharedHeld(self) &&
         !Locks::mutator_lock_->IsExclusiveHeld(self)) {
       LOG(ERROR) << "Lock level violation: holding \"" << Locks::mutator_lock_->name_ << "\" "
                   << "(level " << kMutatorLock << " - " << static_cast<int>(kMutatorLock)
                   << ") non-exclusive while locking \"" << name_ << "\" "
-                  << "(level " << level_ << " - " << static_cast<int>(level_) << ") a top level"
+                  << "(level " << level << " - " << static_cast<int>(level) << ") a top level"
                   << "mutex. This is not allowed.";
       bad_mutexes_held = true;
     } else if (this == Locks::mutator_lock_ && self->GetHeldMutex(kTopLockLevel) != nullptr) {
@@ -113,10 +122,10 @@
                  << "not allowed.";
       bad_mutexes_held = true;
     }
-    for (int i = level_; i >= 0; --i) {
+    for (int i = level; i >= 0; --i) {
       LockLevel lock_level_i = static_cast<LockLevel>(i);
       BaseMutex* held_mutex = self->GetHeldMutex(lock_level_i);
-      if (level_ == kTopLockLevel &&
+      if (level == kTopLockLevel &&
           lock_level_i == kMutatorLock &&
           Locks::mutator_lock_->IsExclusiveHeld(self)) {
         // This is checked above.
@@ -125,7 +134,7 @@
         LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" "
                    << "(level " << lock_level_i << " - " << i
                    << ") while locking \"" << name_ << "\" "
-                   << "(level " << level_ << " - " << static_cast<int>(level_) << ")";
+                   << "(level " << level << " - " << static_cast<int>(level) << ")";
         if (lock_level_i > kAbortLock) {
           // Only abort in the check below if this is more than abort level lock.
           bad_mutexes_held = true;
@@ -138,8 +147,8 @@
   }
   // Don't record monitors as they are outside the scope of analysis. They may be inspected off of
   // the monitor list.
-  if (level_ != kMonitorLock) {
-    self->SetHeldMutex(level_, this);
+  if (level != kMonitorLock) {
+    self->SetHeldMutex(level, this);
   }
 }
 
@@ -149,10 +158,17 @@
     return;
   }
   if (level_ != kMonitorLock) {
-    if (kDebugLocking && gAborting == 0) {  // Avoid recursive aborts.
-      CHECK(self->GetHeldMutex(level_) == this) << "Unlocking on unacquired mutex: " << name_;
+    auto level = level_;
+    if (UNLIKELY(level == kThreadWaitLock) && self->GetHeldMutex(kThreadWaitWakeLock) == this) {
+      level = kThreadWaitWakeLock;
     }
-    self->SetHeldMutex(level_, nullptr);
+    if (kDebugLocking && gAborting == 0) {  // Avoid recursive aborts.
+      if (level == kThreadWaitWakeLock) {
+        CHECK(self->GetHeldMutex(kThreadWaitLock) != nullptr) << "Held " << kThreadWaitWakeLock << " without " << kThreadWaitLock;;
+      }
+      CHECK(self->GetHeldMutex(level) == this) << "Unlocking on unacquired mutex: " << name_;
+    }
+    self->SetHeldMutex(level, nullptr);
   }
 }
 
@@ -214,7 +230,11 @@
   if (kDebugLocking) {
     // Sanity debug check that if we think it is locked we have it in our held mutexes.
     if (result && self != nullptr && level_ != kMonitorLock && !gAborting) {
-      CHECK_EQ(self->GetHeldMutex(level_), this);
+      if (level_ == kThreadWaitLock && self->GetHeldMutex(kThreadWaitLock) != this) {
+        CHECK_EQ(self->GetHeldMutex(kThreadWaitWakeLock), this);
+      } else {
+        CHECK_EQ(self->GetHeldMutex(level_), this);
+      }
     }
   }
   return result;
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 7711be9..0c8fe58 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -68,6 +68,14 @@
   // A generic lock level for mutexs that should not allow any additional mutexes to be gained after
   // acquiring it.
   kGenericBottomLock,
+  // Tracks the second acquisition at the same lock level for kThreadWaitLock. This is an exception
+  // to the normal lock ordering, used to implement Monitor::Wait - while holding one kThreadWait
+  // level lock, it is permitted to acquire a second one - with internal safeguards to ensure that
+  // the second lock acquisition does not result in deadlock. This is implemented in the lock
+  // order by treating the second acquisition of a kThreadWaitLock as a kThreadWaitWakeLock
+  // acquisition. Thus, acquiring kThreadWaitWakeLock requires holding kThreadWaitLock.
+  kThreadWaitWakeLock,
+  kThreadWaitLock,
   kJdwpAdbStateLock,
   kJdwpSocketLock,
   kRegionSpaceRegionLock,
diff --git a/runtime/cha.cc b/runtime/cha.cc
index de4aebe..8e06fda 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -115,7 +115,7 @@
   // if they have SingleImplementations methods defined by 'klass'.
   // Skip all virtual methods that do not override methods from super class since they cannot be
   // SingleImplementations for anything.
-  int32_t vtbl_size = super->GetVTableLength<kDefaultVerifyFlags, kWithoutReadBarrier>();
+  int32_t vtbl_size = super->GetVTableLength<kDefaultVerifyFlags>();
   ObjPtr<mirror::ClassLoader> loader =
       klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>();
   for (int vtbl_index = 0; vtbl_index < vtbl_size; ++vtbl_index) {
@@ -131,7 +131,7 @@
     // so start with a superclass and move up looking into a corresponding vtbl slot.
     for (ObjPtr<mirror::Class> super_it = super;
          super_it != nullptr &&
-             super_it->GetVTableLength<kDefaultVerifyFlags, kWithoutReadBarrier>() > vtbl_index;
+             super_it->GetVTableLength<kDefaultVerifyFlags>() > vtbl_index;
          super_it = super_it->GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>()) {
       // Skip superclasses that are also going to be unloaded.
       ObjPtr<mirror::ClassLoader> super_loader = super_it->
@@ -158,7 +158,7 @@
 
   // Check all possible interface methods too.
   ObjPtr<mirror::IfTable> iftable = klass->GetIfTable<kDefaultVerifyFlags, kWithoutReadBarrier>();
-  const size_t ifcount = klass->GetIfTableCount<kDefaultVerifyFlags, kWithoutReadBarrier>();
+  const size_t ifcount = klass->GetIfTableCount<kDefaultVerifyFlags>();
   for (size_t i = 0; i < ifcount; ++i) {
     ObjPtr<mirror::Class> interface =
         iftable->GetInterface<kDefaultVerifyFlags, kWithoutReadBarrier>(i);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index e3dfdb3..7d4bac3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -965,9 +965,6 @@
       runtime->GetOatFileManager().RegisterImageOatFiles(spaces);
   DCHECK(!oat_files.empty());
   const OatHeader& default_oat_header = oat_files[0]->GetOatHeader();
-  const char* image_file_location = oat_files[0]->GetOatHeader().
-      GetStoreValueByKey(OatHeader::kImageLocationKey);
-  CHECK(image_file_location == nullptr || *image_file_location == 0);
   quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline();
   quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline();
   quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline();
@@ -1495,18 +1492,44 @@
   intern_table->AddImageStringsToTable(space, [&](InternTable::UnorderedSet& interns)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(Locks::intern_table_lock_) {
+    const size_t non_boot_image_strings = intern_table->CountInterns(
+        /*visit_boot_images=*/false,
+        /*visit_non_boot_images=*/true);
     VLOG(image) << "AppImage:stringsInInternTableSize = " << interns.size();
-    for (auto it = interns.begin(); it != interns.end(); ) {
-      ObjPtr<mirror::String> string = it->Read();
-      ObjPtr<mirror::String> existing = intern_table->LookupWeakLocked(string);
-      if (existing == nullptr) {
-        existing = intern_table->LookupStrongLocked(string);
+    VLOG(image) << "AppImage:nonBootImageInternStrings = " << non_boot_image_strings;
+    // Visit the smaller of the two sets to compute the intersection.
+    if (interns.size() < non_boot_image_strings) {
+      for (auto it = interns.begin(); it != interns.end(); ) {
+        ObjPtr<mirror::String> string = it->Read();
+        ObjPtr<mirror::String> existing = intern_table->LookupWeakLocked(string);
+        if (existing == nullptr) {
+          existing = intern_table->LookupStrongLocked(string);
+        }
+        if (existing != nullptr) {
+          intern_remap.Put(string.Ptr(), existing.Ptr());
+          it = interns.erase(it);
+        } else {
+          ++it;
+        }
       }
-      if (existing != nullptr) {
-        intern_remap.Put(string.Ptr(), existing.Ptr());
-        it = interns.erase(it);
-      } else {
-        ++it;
+    } else {
+      intern_table->VisitInterns([&](const GcRoot<mirror::String>& root)
+          REQUIRES_SHARED(Locks::mutator_lock_)
+          REQUIRES(Locks::intern_table_lock_) {
+        auto it = interns.find(root);
+        if (it != interns.end()) {
+          ObjPtr<mirror::String> existing = root.Read();
+          intern_remap.Put(it->Read(), existing.Ptr());
+          it = interns.erase(it);
+        }
+      }, /*visit_boot_images=*/false, /*visit_non_boot_images=*/true);
+    }
+    // Sanity check to ensure correctness.
+    if (kIsDebugBuild) {
+      for (GcRoot<mirror::String>& root : interns) {
+        ObjPtr<mirror::String> string = root.Read();
+        CHECK(intern_table->LookupWeakLocked(string) == nullptr) << string->ToModifiedUtf8();
+        CHECK(intern_table->LookupStrongLocked(string) == nullptr) << string->ToModifiedUtf8();
       }
     }
   });
@@ -1520,26 +1543,6 @@
   }
 }
 
-// Update the class loader. Should only be used on classes in the image space.
-class UpdateClassLoaderVisitor {
- public:
-  UpdateClassLoaderVisitor(gc::space::ImageSpace* space, ObjPtr<mirror::ClassLoader> class_loader)
-      : space_(space),
-        class_loader_(class_loader) {}
-
-  bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    // Do not update class loader for boot image classes where the app image
-    // class loader is only the initiating loader but not the defining loader.
-    if (klass->GetClassLoader() != nullptr) {
-      klass->SetClassLoader(class_loader_);
-    }
-    return true;
-  }
-
-  gc::space::ImageSpace* const space_;
-  ObjPtr<mirror::ClassLoader> const class_loader_;
-};
-
 static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
                                                      const char* location,
                                                      std::string* error_msg)
@@ -2008,11 +2011,23 @@
   }
   if (app_image) {
     AppImageLoadingHelper::Update(this, space, class_loader, dex_caches, &temp_set);
-    // Update class loader and resolved strings. If added_class_table is false, the resolved
-    // strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
-    UpdateClassLoaderVisitor visitor(space, class_loader.Get());
-    for (const ClassTable::TableSlot& root : temp_set) {
-      visitor(root.Read());
+
+    {
+      ScopedTrace trace("AppImage:UpdateClassLoaders");
+      // Update class loader and resolved strings. If added_class_table is false, the resolved
+      // strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
+      ObjPtr<mirror::ClassLoader> loader(class_loader.Get());
+      for (const ClassTable::TableSlot& root : temp_set) {
+        // Note: We probably don't need the read barrier unless we copy the app image objects into
+        // the region space.
+        ObjPtr<mirror::Class> klass(root.Read());
+        // Do not update class loader for boot image classes where the app image
+        // class loader is only the initiating loader but not the defining loader.
+        // Avoid read barrier since we are comparing against null.
+        if (klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+          klass->SetClassLoader</*kCheckTransaction=*/ false>(loader);
+        }
+      }
     }
 
     if (kBitstringSubtypeCheckEnabled) {
@@ -3085,7 +3100,7 @@
     return (jit == nullptr) || !jit->GetCodeCache()->ContainsPc(quick_code);
   }
 
-  if (runtime->IsNativeDebuggable()) {
+  if (runtime->IsNativeDebuggableZygoteOK()) {
     DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse());
     // If we are doing native debugging, ignore application's AOT code,
     // since we want to JIT it (at first use) with extra stackmaps for native
@@ -3985,33 +4000,31 @@
   return existing;
 }
 
-ObjPtr<mirror::Class> ClassLinker::FindPrimitiveClass(char type) {
-  ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots = GetClassRoots();
+ObjPtr<mirror::Class> ClassLinker::LookupPrimitiveClass(char type) {
+  ClassRoot class_root;
   switch (type) {
-    case 'B':
-      return GetClassRoot(ClassRoot::kPrimitiveByte, class_roots);
-    case 'C':
-      return GetClassRoot(ClassRoot::kPrimitiveChar, class_roots);
-    case 'D':
-      return GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots);
-    case 'F':
-      return GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots);
-    case 'I':
-      return GetClassRoot(ClassRoot::kPrimitiveInt, class_roots);
-    case 'J':
-      return GetClassRoot(ClassRoot::kPrimitiveLong, class_roots);
-    case 'S':
-      return GetClassRoot(ClassRoot::kPrimitiveShort, class_roots);
-    case 'Z':
-      return GetClassRoot(ClassRoot::kPrimitiveBoolean, class_roots);
-    case 'V':
-      return GetClassRoot(ClassRoot::kPrimitiveVoid, class_roots);
+    case 'B': class_root = ClassRoot::kPrimitiveByte; break;
+    case 'C': class_root = ClassRoot::kPrimitiveChar; break;
+    case 'D': class_root = ClassRoot::kPrimitiveDouble; break;
+    case 'F': class_root = ClassRoot::kPrimitiveFloat; break;
+    case 'I': class_root = ClassRoot::kPrimitiveInt; break;
+    case 'J': class_root = ClassRoot::kPrimitiveLong; break;
+    case 'S': class_root = ClassRoot::kPrimitiveShort; break;
+    case 'Z': class_root = ClassRoot::kPrimitiveBoolean; break;
+    case 'V': class_root = ClassRoot::kPrimitiveVoid; break;
     default:
-      break;
+      return nullptr;
   }
-  std::string printable_type(PrintableChar(type));
-  ThrowNoClassDefFoundError("Not a primitive type: %s", printable_type.c_str());
-  return nullptr;
+  return GetClassRoot(class_root, this);
+}
+
+ObjPtr<mirror::Class> ClassLinker::FindPrimitiveClass(char type) {
+  ObjPtr<mirror::Class> result = LookupPrimitiveClass(type);
+  if (UNLIKELY(result == nullptr)) {
+    std::string printable_type(PrintableChar(type));
+    ThrowNoClassDefFoundError("Not a primitive type: %s", printable_type.c_str());
+  }
+  return result;
 }
 
 ObjPtr<mirror::Class> ClassLinker::InsertClass(const char* descriptor,
@@ -4903,7 +4916,10 @@
         } else {
           CHECK(Runtime::Current()->IsAotCompiler());
           CHECK_EQ(klass->GetStatus(), ClassStatus::kRetryVerificationAtRuntime);
+          self->AssertNoPendingException();
+          self->SetException(Runtime::Current()->GetPreAllocatedNoClassDefFoundError());
         }
+        self->AssertPendingException();
         return false;
       } else {
         self->AssertNoPendingException();
@@ -5042,8 +5058,10 @@
       ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
       if (resolved_field == nullptr) {
         // Populating cache of a dex file which defines `klass` should always be allowed.
-        DCHECK_EQ(hiddenapi::GetMemberAction(
-            field, class_loader.Get(), dex_cache.Get(), hiddenapi::kNone), hiddenapi::kAllow);
+        DCHECK(!hiddenapi::ShouldDenyAccessToMember(
+            field,
+            hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()),
+            hiddenapi::AccessMethod::kNone));
         dex_cache->SetResolvedField(field_idx, field, image_pointer_size_);
       } else {
         DCHECK_EQ(field, resolved_field);
@@ -8004,7 +8022,7 @@
   if (descriptor[1] == '\0') {
     // only the descriptors of primitive types should be 1 character long, also avoid class lookup
     // for primitive classes that aren't backed by dex files.
-    type = FindPrimitiveClass(descriptor[0]);
+    type = LookupPrimitiveClass(descriptor[0]);
   } else {
     Thread* const self = Thread::Current();
     DCHECK(self != nullptr);
@@ -8074,8 +8092,9 @@
   }
   DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
   if (resolved != nullptr &&
-      hiddenapi::GetMemberAction(
-          resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+      hiddenapi::ShouldDenyAccessToMember(resolved,
+                                          hiddenapi::AccessContext(class_loader, dex_cache),
+                                          hiddenapi::AccessMethod::kLinking)) {
     resolved = nullptr;
   }
   if (resolved != nullptr) {
@@ -8105,11 +8124,9 @@
                               ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_) {
   return method == nullptr ||
-         hiddenapi::GetMemberAction(method,
-                                    class_loader,
-                                    dex_cache,
-                                    hiddenapi::kNone)  // do not print warnings
-             == hiddenapi::kDeny;
+         hiddenapi::ShouldDenyAccessToMember(method,
+                                             hiddenapi::AccessContext(class_loader, dex_cache),
+                                             hiddenapi::AccessMethod::kNone);  // no warnings
 }
 
 ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
@@ -8245,8 +8262,10 @@
     resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_);
   }
   if (resolved != nullptr &&
-      hiddenapi::GetMemberAction(
-          resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
+      hiddenapi::ShouldDenyAccessToMember(
+          resolved,
+          hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()),
+          hiddenapi::AccessMethod::kLinking)) {
     resolved = nullptr;
   }
   return resolved;
@@ -8345,8 +8364,9 @@
   }
 
   if (resolved != nullptr &&
-      hiddenapi::GetMemberAction(
-          resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+      hiddenapi::ShouldDenyAccessToMember(resolved,
+                                          hiddenapi::AccessContext(class_loader, dex_cache),
+                                          hiddenapi::AccessMethod::kLinking)) {
     resolved = nullptr;
   }
 
@@ -8371,8 +8391,9 @@
   resolved = mirror::Class::FindField(self, klass, name, type);
 
   if (resolved != nullptr &&
-      hiddenapi::GetMemberAction(
-          resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+      hiddenapi::ShouldDenyAccessToMember(resolved,
+                                          hiddenapi::AccessContext(class_loader, dex_cache),
+                                          hiddenapi::AccessMethod::kLinking)) {
     resolved = nullptr;
   }
 
@@ -8533,7 +8554,7 @@
   switch (handle_type) {
     case DexFile::MethodHandleType::kStaticPut: {
       method_params->Set(0, target_field->ResolveType());
-      return_type = hs.NewHandle(FindPrimitiveClass('V'));
+      return_type = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, this));
       break;
     }
     case DexFile::MethodHandleType::kStaticGet: {
@@ -8543,7 +8564,7 @@
     case DexFile::MethodHandleType::kInstancePut: {
       method_params->Set(0, target_field->GetDeclaringClass());
       method_params->Set(1, target_field->ResolveType());
-      return_type = hs.NewHandle(FindPrimitiveClass('V'));
+      return_type = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, this));
       break;
     }
     case DexFile::MethodHandleType::kInstanceGet: {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 7afd575..60e68d5 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -196,6 +196,7 @@
       REQUIRES(!Locks::classlinker_classes_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  ObjPtr<mirror::Class> LookupPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_);
   ObjPtr<mirror::Class> FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_);
 
   void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::classlinker_classes_lock_);
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index de9fe22..0bae60a 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -338,6 +338,7 @@
   // no dex files. So that we can distinguish the real failures...
   const ArtDexFileLoader dex_file_loader;
   std::vector<ClassLoaderInfo*> work_list;
+  CHECK(class_loader_chain_ != nullptr);
   work_list.push_back(class_loader_chain_.get());
   while (!work_list.empty()) {
     ClassLoaderInfo* info = work_list.back();
@@ -908,7 +909,9 @@
   // collision check.
   if (expected_context.special_shared_library_) {
     // Special case where we are the only entry in the class path.
-    if (class_loader_chain_->parent == nullptr && class_loader_chain_->classpath.size() == 0) {
+    if (class_loader_chain_ != nullptr &&
+        class_loader_chain_->parent == nullptr &&
+        class_loader_chain_->classpath.size() == 0) {
       return VerificationResult::kVerifies;
     }
     return VerificationResult::kForcedToSkipChecks;
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index cb3dc65..f3e2ac0 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -735,6 +735,17 @@
             ClassLoaderContext::VerificationResult::kMismatch);
 }
 
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchSpecial) {
+  std::string context_spec = "&";
+  std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+  // Pretend that we successfully open the dex files to pass the DCHECKS.
+  // (as it's much easier to test all the corner cases without relying on actual dex files).
+  PretendContextOpenedDexFiles(context.get());
+
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
+            ClassLoaderContext::VerificationResult::kForcedToSkipChecks);
+}
+
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithSL) {
   std::string context_spec =
       "PCL[a.dex*123:b.dex*456]{PCL[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}"
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index ed3a18d..b11e368 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -66,7 +66,7 @@
   }
   runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
 
-  if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
+  if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kDisabled) {
     argv.push_back("--runtime-arg");
     argv.push_back("-Xhidden-api-checks");
   }
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index e0bbf43..aba1c5a 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -93,6 +93,8 @@
       from_space_num_bytes_at_first_pause_(0),
       mark_stack_mode_(kMarkStackModeOff),
       weak_ref_access_enabled_(true),
+      copied_live_bytes_ratio_sum_(0.f),
+      gc_count_(0),
       young_gen_(young_gen),
       skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock),
       measure_read_barrier_slow_path_(measure_read_barrier_slow_path),
@@ -1658,7 +1660,9 @@
         << " type=" << to_ref->PrettyTypeOf()
         << " young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha
         << " space=" << heap_->DumpSpaceNameFromAddress(to_ref)
-        << " region_type=" << rtype;
+        << " region_type=" << rtype
+        // TODO: Temporary; remove this when this is no longer needed (b/116087961).
+        << " runtime->sentinel=" << Runtime::Current()->GetSentinel().Read<kWithoutReadBarrier>();
   }
   bool add_to_live_bytes = false;
   // Invariant: There should be no object from a newly-allocated
@@ -1702,7 +1706,9 @@
         << " type=" << to_ref->PrettyTypeOf()
         << " young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha
         << " space=" << heap_->DumpSpaceNameFromAddress(to_ref)
-        << " region_type=" << rtype;
+        << " region_type=" << rtype
+        // TODO: Temporary; remove this when this is no longer needed (b/116087961).
+        << " runtime->sentinel=" << Runtime::Current()->GetSentinel().Read<kWithoutReadBarrier>();
   }
 #ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
   mirror::Object* referent = nullptr;
@@ -2034,6 +2040,11 @@
     }
     CHECK_LE(to_objects, from_objects);
     CHECK_LE(to_bytes, from_bytes);
+    if (from_bytes > 0) {
+      copied_live_bytes_ratio_sum_ += static_cast<float>(to_bytes) / from_bytes;
+      gc_count_++;
+    }
+
     // Cleared bytes and objects, populated by the call to RegionSpace::ClearFromSpace below.
     uint64_t cleared_bytes;
     uint64_t cleared_objects;
@@ -3200,6 +3211,15 @@
   if (rb_slow_path_count_gc_total_ > 0) {
     os << "GC slow path count " << rb_slow_path_count_gc_total_ << "\n";
   }
+  float average_ratio = copied_live_bytes_ratio_sum_ / gc_count_;
+
+  if (young_gen_) {
+    os << "Average minor GC copied live bytes ratio "
+       << average_ratio << " over " << gc_count_ << " minor GCs\n";
+  } else {
+    os << "Average major GC copied live bytes ratio "
+       << average_ratio << " over " << gc_count_ << " major GCs\n";
+  }
   os << "Cumulative bytes moved "
      << cumulative_bytes_moved_.load(std::memory_order_relaxed) << "\n";
   os << "Cumulative objects moved "
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 1a7464a..cd086c4 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -359,6 +359,18 @@
   Atomic<uint64_t> cumulative_bytes_moved_;
   Atomic<uint64_t> cumulative_objects_moved_;
 
+  // copied_live_bytes_ratio_sum_ and gc_count_ are read and written by CC per
+  // GC, in ReclaimPhase, and are read by DumpPerformanceInfo (potentially from
+  // another thread). However, at present, DumpPerformanceInfo is only called
+  // when the runtime shuts down, so no concurrent access.
+  // The sum of of all copied live bytes ratio (to_bytes/from_bytes)
+  float copied_live_bytes_ratio_sum_;
+  // The number of GC counts, used to calculate the average above. (It doesn't
+  // include GC where from_bytes is zero, IOW, from-space is empty, which is
+  // possible for minor GC if all allocated objects are in non-moving
+  // space.)
+  size_t gc_count_;
+
   // Generational "sticky", only trace through dirty objects in region space.
   const bool young_gen_;
   // If true, the GC thread is done scanning marked objects on dirty and aged
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index b46abfb..0766999 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -832,7 +832,7 @@
           reinterpret_cast<uintptr_t>(array) + kObjectAlignment);
       // If the bit is not set then the contents have not yet been updated.
       if (!visited_->Test(contents_bit)) {
-        array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, pointer_size_, visitor);
+        array->Fixup<kVerifyNone>(array, pointer_size_, visitor);
         visited_->Set(contents_bit);
       }
     }
@@ -1884,7 +1884,7 @@
           }
           auto* iftable = klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>();
           if (iftable != nullptr) {
-            int32_t ifcount = klass->GetIfTableCount<kVerifyNone, kWithoutReadBarrier>();
+            int32_t ifcount = klass->GetIfTableCount<kVerifyNone>();
             for (int32_t i = 0; i != ifcount; ++i) {
               mirror::PointerArray* unpatched_ifarray =
                   iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i);
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index f355276..3b7b938 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -44,38 +44,53 @@
 
 static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
   switch (value) {
-    case kNone:
+    case AccessMethod::kNone:
       LOG(FATAL) << "Internal access to hidden API should not be logged";
       UNREACHABLE();
-    case kReflection:
+    case AccessMethod::kReflection:
       os << "reflection";
       break;
-    case kJNI:
+    case AccessMethod::kJNI:
       os << "JNI";
       break;
-    case kLinking:
+    case AccessMethod::kLinking:
       os << "linking";
       break;
   }
   return os;
 }
 
-static constexpr bool EnumsEqual(EnforcementPolicy policy, hiddenapi::ApiList apiList) {
-  return static_cast<int>(policy) == static_cast<int>(apiList);
-}
-
-// GetMemberAction-related static_asserts.
-static_assert(
-    EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, hiddenapi::ApiList::kDarkGreylist) &&
-    EnumsEqual(EnforcementPolicy::kBlacklistOnly, hiddenapi::ApiList::kBlacklist),
-    "Mismatch between EnforcementPolicy and ApiList enums");
-static_assert(
-    EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList &&
-    EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
-    "EnforcementPolicy values ordering not correct");
-
 namespace detail {
 
+// Do not change the values of items in this enum, as they are written to the
+// event log for offline analysis. Any changes will interfere with that analysis.
+enum AccessContextFlags {
+  // Accessed member is a field if this bit is set, else a method
+  kMemberIsField = 1 << 0,
+  // Indicates if access was denied to the member, instead of just printing a warning.
+  kAccessDenied  = 1 << 1,
+};
+
+static int32_t GetMaxAllowedSdkVersionForApiList(ApiList api_list) {
+  SdkCodes sdk = SdkCodes::kVersionNone;
+  switch (api_list) {
+    case ApiList::kWhitelist:
+    case ApiList::kLightGreylist:
+      sdk = SdkCodes::kVersionUnlimited;
+      break;
+    case ApiList::kDarkGreylist:
+      sdk = SdkCodes::kVersionO_MR1;
+      break;
+    case ApiList::kBlacklist:
+      sdk = SdkCodes::kVersionNone;
+      break;
+    case ApiList::kNoList:
+      LOG(FATAL) << "Unexpected value";
+      UNREACHABLE();
+  }
+  return static_cast<int32_t>(sdk);
+}
+
 MemberSignature::MemberSignature(ArtField* field) {
   class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_);
   member_name_ = field->GetName();
@@ -137,6 +152,7 @@
   LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ")
                << Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")";
 }
+
 #ifdef ART_TARGET_ANDROID
 // Convert an AccessMethod enum to a value for logging from the proto enum.
 // This method may look odd (the enum values are current the same), but it
@@ -145,13 +161,13 @@
 // future.
 inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
   switch (access_method) {
-    case kNone:
+    case AccessMethod::kNone:
       return android::metricslogger::ACCESS_METHOD_NONE;
-    case kReflection:
+    case AccessMethod::kReflection:
       return android::metricslogger::ACCESS_METHOD_REFLECTION;
-    case kJNI:
+    case AccessMethod::kJNI:
       return android::metricslogger::ACCESS_METHOD_JNI;
-    case kLinking:
+    case AccessMethod::kLinking:
       return android::metricslogger::ACCESS_METHOD_LINKING;
     default:
       DCHECK(false);
@@ -159,9 +175,9 @@
 }
 #endif
 
-void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) {
+void MemberSignature::LogAccessToEventLog(AccessMethod access_method, bool access_denied) {
 #ifdef ART_TARGET_ANDROID
-  if (access_method == kLinking || access_method == kNone) {
+  if (access_method == AccessMethod::kLinking || access_method == AccessMethod::kNone) {
     // Linking warnings come from static analysis/compilation of the bytecode
     // and can contain false positives (i.e. code that is never run). We choose
     // not to log these in the event log.
@@ -170,7 +186,7 @@
   }
   ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED);
   log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method));
-  if (action_taken == kDeny) {
+  if (access_denied) {
     log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
   }
   const std::string& package_name = Runtime::Current()->GetProcessPackageName();
@@ -183,10 +199,42 @@
   log_maker.Record();
 #else
   UNUSED(access_method);
-  UNUSED(action_taken);
+  UNUSED(access_denied);
 #endif
 }
 
+void MemberSignature::NotifyHiddenApiListener(AccessMethod access_method) {
+  if (access_method != AccessMethod::kReflection && access_method != AccessMethod::kJNI) {
+    // We can only up-call into Java during reflection and JNI down-calls.
+    return;
+  }
+
+  Runtime* runtime = Runtime::Current();
+  if (!runtime->IsAotCompiler()) {
+    ScopedObjectAccessUnchecked soa(Thread::Current());
+
+    ScopedLocalRef<jobject> consumer_object(soa.Env(),
+        soa.Env()->GetStaticObjectField(
+            WellKnownClasses::dalvik_system_VMRuntime,
+            WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
+    // If the consumer is non-null, we call back to it to let it know that we
+    // have encountered an API that's in one of our lists.
+    if (consumer_object != nullptr) {
+      std::ostringstream member_signature_str;
+      Dump(member_signature_str);
+
+      ScopedLocalRef<jobject> signature_str(
+          soa.Env(),
+          soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
+
+      // Call through to Consumer.accept(String memberSignature);
+      soa.Env()->CallVoidMethod(consumer_object.get(),
+                                WellKnownClasses::java_util_function_Consumer_accept,
+                                signature_str.get());
+    }
+  }
+}
+
 static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
   return true;
 }
@@ -205,116 +253,68 @@
 }
 
 template<typename T>
-Action GetMemberActionImpl(T* member,
-                           hiddenapi::ApiList api_list,
-                           Action action,
-                           AccessMethod access_method) {
-  DCHECK_NE(action, kAllow);
-
-  // Get the signature, we need it later.
-  MemberSignature member_signature(member);
+bool ShouldDenyAccessToMemberImpl(T* member,
+                                  hiddenapi::ApiList api_list,
+                                  AccessMethod access_method) {
+  DCHECK(member != nullptr);
 
   Runtime* runtime = Runtime::Current();
+  EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy();
+
+  const bool deny_access =
+      (policy == EnforcementPolicy::kEnabled) &&
+      (runtime->GetTargetSdkVersion() > GetMaxAllowedSdkVersionForApiList(api_list));
+
+  MemberSignature member_signature(member);
 
   // Check for an exemption first. Exempted APIs are treated as white list.
-  // We only do this if we're about to deny, or if the app is debuggable. This is because:
-  // - we only print a warning for light greylist violations for debuggable apps
-  // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs.
-  // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever
-  //   possible.
-  const bool shouldWarn = kLogAllAccesses || runtime->IsJavaDebuggable();
-  if (shouldWarn || action == kDeny) {
-    if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
-      action = kAllow;
-      // Avoid re-examining the exemption list next time.
-      // Note this results in no warning for the member, which seems like what one would expect.
-      // Exemptions effectively adds new members to the whitelist.
-      MaybeWhitelistMember(runtime, member);
-      return kAllow;
-    }
+  if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
+    // Avoid re-examining the exemption list next time.
+    // Note this results in no warning for the member, which seems like what one would expect.
+    // Exemptions effectively adds new members to the whitelist.
+    MaybeWhitelistMember(runtime, member);
+    return false;
+  }
 
-    if (access_method != kNone) {
-      // Print a log message with information about this class member access.
-      // We do this if we're about to block access, or the app is debuggable.
+  if (access_method != AccessMethod::kNone) {
+    // Print a log message with information about this class member access.
+    // We do this if we're about to deny access, or the app is debuggable.
+    if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
       member_signature.WarnAboutAccess(access_method, api_list);
     }
-  }
 
-  if (kIsTargetBuild && !kIsTargetLinux) {
-    uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
-    // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
-    static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
-    if (eventLogSampleRate != 0 &&
-        (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
-      member_signature.LogAccessToEventLog(access_method, action);
+    // If there is a StrictMode listener, notify it about this violation.
+    member_signature.NotifyHiddenApiListener(access_method);
+
+    // If event log sampling is enabled, report this violation.
+    if (kIsTargetBuild && !kIsTargetLinux) {
+      uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
+      // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
+      static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
+      if (eventLogSampleRate != 0 &&
+          (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
+        member_signature.LogAccessToEventLog(access_method, deny_access);
+      }
+    }
+
+    // If this access was not denied, move the member into whitelist and skip
+    // the warning the next time the member is accessed.
+    if (!deny_access) {
+      MaybeWhitelistMember(runtime, member);
     }
   }
 
-  if (action == kDeny) {
-    // Block access
-    return action;
-  }
-
-  // Allow access to this member but print a warning.
-  DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
-
-  if (access_method != kNone) {
-    // Depending on a runtime flag, we might move the member into whitelist and
-    // skip the warning the next time the member is accessed.
-    MaybeWhitelistMember(runtime, member);
-
-    // If this action requires a UI warning, set the appropriate flag.
-    if (shouldWarn &&
-        (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag())) {
-      runtime->SetPendingHiddenApiWarning(true);
-    }
-  }
-
-  return action;
+  return deny_access;
 }
 
 // Need to instantiate this.
-template Action GetMemberActionImpl<ArtField>(ArtField* member,
-                                              hiddenapi::ApiList api_list,
-                                              Action action,
-                                              AccessMethod access_method);
-template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member,
-                                               hiddenapi::ApiList api_list,
-                                               Action action,
-                                               AccessMethod access_method);
+template bool ShouldDenyAccessToMemberImpl<ArtField>(ArtField* member,
+                                                     hiddenapi::ApiList api_list,
+                                                     AccessMethod access_method);
+template bool ShouldDenyAccessToMemberImpl<ArtMethod>(ArtMethod* member,
+                                                      hiddenapi::ApiList api_list,
+                                                      AccessMethod access_method);
 }  // namespace detail
 
-template<typename T>
-void NotifyHiddenApiListener(T* member) {
-  Runtime* runtime = Runtime::Current();
-  if (!runtime->IsAotCompiler()) {
-    ScopedObjectAccessUnchecked soa(Thread::Current());
-
-    ScopedLocalRef<jobject> consumer_object(soa.Env(),
-        soa.Env()->GetStaticObjectField(
-            WellKnownClasses::dalvik_system_VMRuntime,
-            WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
-    // If the consumer is non-null, we call back to it to let it know that we
-    // have encountered an API that's in one of our lists.
-    if (consumer_object != nullptr) {
-      detail::MemberSignature member_signature(member);
-      std::ostringstream member_signature_str;
-      member_signature.Dump(member_signature_str);
-
-      ScopedLocalRef<jobject> signature_str(
-          soa.Env(),
-          soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
-
-      // Call through to Consumer.accept(String memberSignature);
-      soa.Env()->CallVoidMethod(consumer_object.get(),
-                                WellKnownClasses::java_util_function_Consumer_accept,
-                                signature_str.get());
-    }
-  }
-}
-
-template void NotifyHiddenApiListener<ArtMethod>(ArtMethod* member);
-template void NotifyHiddenApiListener<ArtField>(ArtField* member);
-
 }  // namespace hiddenapi
 }  // namespace art
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 57f1a59..ed00e2a 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -32,11 +32,10 @@
 // This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in
 // frameworks/base/core/java/android/content/pm/ApplicationInfo.java
 enum class EnforcementPolicy {
-  kNoChecks             = 0,
+  kDisabled             = 0,
   kJustWarn             = 1,  // keep checks enabled, but allow everything (enables logging)
-  kDarkGreyAndBlackList = 2,  // ban dark grey & blacklist
-  kBlacklistOnly        = 3,  // ban blacklist violations only
-  kMax = kBlacklistOnly,
+  kEnabled              = 2,  // ban dark grey & blacklist
+  kMax = kEnabled,
 };
 
 inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) {
@@ -45,56 +44,59 @@
   return static_cast<EnforcementPolicy>(api_policy_int);
 }
 
-enum Action {
-  kAllow,
-  kAllowButWarn,
-  kAllowButWarnAndToast,
-  kDeny
-};
-
-enum AccessMethod {
+enum class AccessMethod {
   kNone,  // internal test that does not correspond to an actual access by app
   kReflection,
   kJNI,
   kLinking,
 };
 
-// Do not change the values of items in this enum, as they are written to the
-// event log for offline analysis. Any changes will interfere with that analysis.
-enum AccessContextFlags {
-  // Accessed member is a field if this bit is set, else a method
-  kMemberIsField = 1 << 0,
-  // Indicates if access was denied to the member, instead of just printing a warning.
-  kAccessDenied  = 1 << 1,
+struct AccessContext {
+ public:
+  explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {}
+
+  explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {}
+
+  AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache)
+      : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {}
+
+  bool IsTrusted() const { return is_trusted_; }
+
+ private:
+  static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader,
+                           ObjPtr<mirror::DexCache> dex_cache)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    // Trust if the caller is in is boot class loader.
+    if (class_loader.IsNull()) {
+      return true;
+    }
+
+    // Trust if caller is in a platform dex file.
+    if (!dex_cache.IsNull()) {
+      const DexFile* dex_file = dex_cache->GetDexFile();
+      if (dex_file != nullptr && dex_file->IsPlatformDexFile()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(!klass.IsNull());
+
+    if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) {
+      // Class is known, it is marked trusted and we are in debuggable mode.
+      return true;
+    }
+
+    // Check other aspects of the context.
+    return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache());
+  }
+
+  bool is_trusted_;
 };
 
-inline Action GetActionFromAccessFlags(ApiList api_list) {
-  if (api_list == ApiList::kWhitelist) {
-    return kAllow;
-  }
-
-  EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
-  if (policy == EnforcementPolicy::kNoChecks) {
-    // Exit early. Nothing to enforce.
-    return kAllow;
-  }
-
-  // if policy is "just warn", always warn. We returned above for whitelist APIs.
-  if (policy == EnforcementPolicy::kJustWarn) {
-    return kAllowButWarn;
-  }
-  DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList);
-  // The logic below relies on equality of values in the enums EnforcementPolicy and
-  // ApiList, and their ordering. Assertions are in hidden_api.cc.
-  if (static_cast<int>(policy) > static_cast<int>(api_list)) {
-    return api_list == ApiList::kDarkGreylist
-        ? kAllowButWarnAndToast
-        : kAllowButWarn;
-  } else {
-    return kDeny;
-  }
-}
-
 class ScopedHiddenApiEnforcementPolicySetting {
  public:
   explicit ScopedHiddenApiEnforcementPolicySetting(EnforcementPolicy new_policy)
@@ -114,6 +116,13 @@
 // Implementation details. DO NOT ACCESS DIRECTLY.
 namespace detail {
 
+enum class SdkCodes {
+  kVersionNone      = std::numeric_limits<int32_t>::min(),
+  kVersionUnlimited = std::numeric_limits<int32_t>::max(),
+  kVersionO_MR1     = 27,
+  kVersionP         = 28,
+};
+
 // Class to encapsulate the signature of a member (ArtField or ArtMethod). This
 // is used as a helper when matching prefixes, and when logging the signature.
 class MemberSignature {
@@ -146,59 +155,31 @@
 
   void WarnAboutAccess(AccessMethod access_method, ApiList list);
 
-  void LogAccessToEventLog(AccessMethod access_method, Action action_taken);
+  void LogAccessToEventLog(AccessMethod access_method, bool access_denied);
+
+  // Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
+  // |member| was accessed. This is usually called when an API is on the black,
+  // dark grey or light grey lists. Given that the callback can execute arbitrary
+  // code, a call to this method can result in thread suspension.
+  void NotifyHiddenApiListener(AccessMethod access_method);
 };
 
 template<typename T>
-Action GetMemberActionImpl(T* member,
-                           ApiList api_list,
-                           Action action,
-                           AccessMethod access_method)
+bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-// Returns true if the caller is either loaded by the boot strap class loader or comes from
-// a dex file located in ${ANDROID_ROOT}/framework/.
-ALWAYS_INLINE
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller,
-                            ObjPtr<mirror::ClassLoader> caller_class_loader,
-                            ObjPtr<mirror::DexCache> caller_dex_cache)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (caller_class_loader.IsNull()) {
-    // Boot class loader.
-    return true;
-  }
-
-  if (!caller_dex_cache.IsNull()) {
-    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
-    if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) {
-      // Caller is in a platform dex file.
-      return true;
-    }
-  }
-
-  if (!caller.IsNull() &&
-      caller->ShouldSkipHiddenApiChecks() &&
-      Runtime::Current()->IsJavaDebuggable()) {
-    // We are in debuggable mode and this caller has been marked trusted.
-    return true;
-  }
-
-  return false;
-}
-
 }  // namespace detail
 
-// Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is trusted or
-// not. Because different users of this function determine this in a different
-// way, `fn_caller_is_trusted(self)` is called and should return true if the
-// caller is allowed to access the platform.
+// Returns true if access to `member` should be denied in the given context.
+// The decision is based on whether the caller is in a trusted context or not.
+// Because determining the access context can be expensive, a lambda function
+// "fn_get_access_context" is lazily invoked after other criteria have been
+// considered.
 // This function might print warnings into the log if the member is hidden.
 template<typename T>
-inline Action GetMemberAction(T* member,
-                              Thread* self,
-                              std::function<bool(Thread*)> fn_caller_is_trusted,
-                              AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+                                     std::function<AccessContext()> fn_get_access_context,
+                                     AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
 
@@ -210,53 +191,34 @@
   // results, e.g. print whitelist warnings (b/78327881).
   ApiList api_list = member->GetHiddenApiAccessFlags();
 
-  Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags());
-  if (action == kAllow) {
-    // Nothing to do.
-    return action;
+  // Exit early if member is on the whitelist.
+  if (api_list == ApiList::kWhitelist) {
+    return false;
   }
 
-  // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
+  // Check if caller is exempted from access checks.
   // This can be *very* expensive. Save it for last.
-  if (fn_caller_is_trusted(self)) {
-    // Caller is trusted. Exit.
-    return kAllow;
+  if (fn_get_access_context().IsTrusted()) {
+    return false;
   }
 
-  // Member is hidden and caller is not in the platform.
-  return detail::GetMemberActionImpl(member, api_list, action, access_method);
+  // Member is hidden and caller is not exempted. Enter slow path.
+  return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
 }
 
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) {
-  return !caller.IsNull() &&
-      detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache());
-}
-
-// Returns true if access to `member` should be denied to a caller loaded with
-// `caller_class_loader`.
-// This function might print warnings into the log if the member is hidden.
+// Helper method for callers where access context can be determined beforehand.
+// Wraps AccessContext in a lambda and passes it to the real ShouldDenyAccessToMember.
 template<typename T>
-inline Action GetMemberAction(T* member,
-                              ObjPtr<mirror::ClassLoader> caller_class_loader,
-                              ObjPtr<mirror::DexCache> caller_dex_cache,
-                              AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+                                     AccessContext access_context,
+                                     AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  bool is_caller_trusted =
-      detail::IsCallerTrusted(/* caller= */ nullptr, caller_class_loader, caller_dex_cache);
-  return GetMemberAction(member,
-                         /* thread= */ nullptr,
-                         [is_caller_trusted] (Thread*) { return is_caller_trusted; },
-                         access_method);
+  return ShouldDenyAccessToMember(
+      member,
+      [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; },
+      access_method);
 }
 
-// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
-// |member| was accessed. This is usually called when an API is on the black,
-// dark grey or light grey lists. Given that the callback can execute arbitrary
-// code, a call to this method can result in thread suspension.
-template<typename T> void NotifyHiddenApiListener(T* member)
-    REQUIRES_SHARED(Locks::mutator_lock_);
-
-
 }  // namespace hiddenapi
 }  // namespace art
 
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 1727af0..627d9a7 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -23,7 +23,7 @@
 namespace art {
 
 using hiddenapi::detail::MemberSignature;
-using hiddenapi::GetActionFromAccessFlags;
+using hiddenapi::detail::ShouldDenyAccessToMemberImpl;
 
 class HiddenApiTest : public CommonRuntimeTest {
  protected:
@@ -68,6 +68,15 @@
     return art_field;
   }
 
+  bool ShouldDenyAccess(hiddenapi::ApiList list) REQUIRES_SHARED(Locks::mutator_lock_) {
+    // Choose parameters such that there are no side effects (AccessMethod::kNone)
+    // and that the member is not on the exemptions list (here we choose one which
+    // is not even in boot class path).
+    return ShouldDenyAccessToMemberImpl(/* member= */ class1_field1_,
+                                        list,
+                                        /* access_method= */ hiddenapi::AccessMethod::kNone);
+  }
+
  protected:
   Thread* self_;
   jobject jclass_loader_;
@@ -88,41 +97,33 @@
 };
 
 TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
-  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist), hiddenapi::kAllow);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist), hiddenapi::kAllow);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist), hiddenapi::kAllow);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist), hiddenapi::kAllow);
+  ScopedObjectAccess soa(self_);
+
+  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
 
   runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
-            hiddenapi::kAllow);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
-            hiddenapi::kAllowButWarn);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
-            hiddenapi::kAllowButWarn);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
-            hiddenapi::kAllowButWarn);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
 
-  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
-            hiddenapi::kAllow);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
-            hiddenapi::kAllowButWarn);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
-            hiddenapi::kDeny);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
-            hiddenapi::kDeny);
+  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+  runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
 
-  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
-            hiddenapi::kAllow);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
-            hiddenapi::kAllowButWarn);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
-            hiddenapi::kAllowButWarnAndToast);
-  ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
-            hiddenapi::kDeny);
+  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+  runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionP));
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
 }
 
 TEST_F(HiddenApiTest, CheckMembersRead) {
diff --git a/runtime/image.cc b/runtime/image.cc
index 376742a..59ac283 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '6', '\0' };  // Add metadata section.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '7', '\0' };  // Added CRC32 intrinsic
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/intern_table-inl.h b/runtime/intern_table-inl.h
index 3c7da09..6fc53e9 100644
--- a/runtime/intern_table-inl.h
+++ b/runtime/intern_table-inl.h
@@ -77,7 +77,7 @@
                                       bool visit_boot_images,
                                       bool visit_non_boot_images) {
   auto visit_tables = [&](std::vector<Table::InternalTable>& tables)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
+      NO_THREAD_SAFETY_ANALYSIS {
     for (Table::InternalTable& table : tables) {
       // Determine if we want to visit the table based on the flags..
       const bool visit =
@@ -94,6 +94,26 @@
   visit_tables(weak_interns_.tables_);
 }
 
+inline size_t InternTable::CountInterns(bool visit_boot_images,
+                                        bool visit_non_boot_images) const {
+  size_t ret = 0u;
+  auto visit_tables = [&](const std::vector<Table::InternalTable>& tables)
+      NO_THREAD_SAFETY_ANALYSIS {
+    for (const Table::InternalTable& table : tables) {
+      // Determine if we want to visit the table based on the flags..
+      const bool visit =
+          (visit_boot_images && table.IsBootImage()) ||
+          (visit_non_boot_images && !table.IsBootImage());
+      if (visit) {
+        ret += table.set_.size();
+      }
+    }
+  };
+  visit_tables(strong_interns_.tables_);
+  visit_tables(weak_interns_.tables_);
+  return ret;
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_INTERN_TABLE_INL_H_
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index baeb1a9..165d56c 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -174,6 +174,10 @@
                     bool visit_non_boot_images)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
 
+  // Count the number of intern strings in the table.
+  size_t CountInterns(bool visit_boot_images, bool visit_non_boot_images) const
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
+
   void DumpForSigQuit(std::ostream& os) const REQUIRES(!Locks::intern_table_lock_);
 
   void BroadcastForNewInterns();
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 5784b9b..d004d64 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -237,7 +237,11 @@
   kMterpImplKind          // Assembly interpreter
 };
 
+#if ART_USE_CXX_INTERPRETER
+static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind;
+#else
 static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind;
+#endif
 
 static inline JValue Execute(
     Thread* self,
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 17b3cd4..24a026a 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -558,6 +558,7 @@
     UNIMPLEMENTED_CASE(ReferenceGetReferent /* ()Ljava/lang/Object; */)
     UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */)
     UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */)
+    UNIMPLEMENTED_CASE(CRC32Update /* (II)I */)
     INTRINSIC_CASE(VarHandleFullFence)
     INTRINSIC_CASE(VarHandleAcquireFence)
     INTRINSIC_CASE(VarHandleReleaseFence)
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index 48e1728..94cb3de 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -23,6 +23,7 @@
 #include "base/memory_tool.h"
 #include "base/quasi_atomic.h"
 #include "dex/dex_file_types.h"
+#include "dex/dex_instruction_list.h"
 #include "experimental_flags.h"
 #include "interpreter_common.h"
 #include "jit/jit.h"
@@ -36,237 +37,2547 @@
 namespace art {
 namespace interpreter {
 
-#define CHECK_FORCE_RETURN()                                                        \
-  {                                                                                 \
-    if (UNLIKELY(shadow_frame.GetForcePopFrame())) {                                \
-      DCHECK(PrevFrameWillRetry(self, shadow_frame))                                \
-          << "Pop frame forced without previous frame ready to retry instruction!"; \
-      DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());                     \
-      if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) {                        \
-        SendMethodExitEvents(self,                                                  \
-                             instrumentation,                                       \
-                             shadow_frame,                                          \
-                             shadow_frame.GetThisObject(accessor.InsSize()),        \
-                             shadow_frame.GetMethod(),                              \
-                             inst->GetDexPc(insns),                                 \
-                             JValue());                                             \
-      }                                                                             \
-      ctx->result = JValue(); /* Handled in caller. */                              \
-      return;                                                                       \
-    }                                                                               \
-  }                                                                                 \
-  do {} while (false)
-
-#define HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(instr)                                    \
-  {                                                                                             \
-    DCHECK(self->IsExceptionPending());                                                         \
-    self->AllowThreadSuspension();                                                              \
-    CHECK_FORCE_RETURN();                                                                       \
-    if (!MoveToExceptionHandler(self, shadow_frame, instr)) {                                   \
-      /* Structured locking is to be enforced for abnormal termination, too. */                 \
-      DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame);                        \
-      if (interpret_one_instruction) {                                                          \
-        /* Signal mterp to return to caller */                                                  \
-        shadow_frame.SetDexPC(dex::kDexNoIndex);                                                \
-      }                                                                                         \
-      ctx->result = JValue(); /* Handled in caller. */                                          \
-      return;                                                                                   \
-    } else {                                                                                    \
-      CHECK_FORCE_RETURN();                                                                     \
-      int32_t displacement =                                                                    \
-          static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc);         \
-      inst = inst->RelativeAt(displacement);                                                    \
-      break;  /* Stop executing this opcode and continue in the exception handler. */           \
-    }                                                                                           \
-  }                                                                                             \
-  do {} while (false)
-
-#define HANDLE_PENDING_EXCEPTION() HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(instrumentation)
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL(_is_exception_pending, _next_function) \
-  {                                                                                             \
-    if (UNLIKELY(shadow_frame.GetForceRetryInstruction())) {                                    \
-      /* Don't need to do anything except clear the flag and exception. We leave the */         \
-      /* instruction the same so it will be re-executed on the next go-around.       */         \
-      DCHECK(inst->IsInvoke());                                                                 \
-      shadow_frame.SetForceRetryInstruction(false);                                             \
-      if (UNLIKELY(_is_exception_pending)) {                                                    \
-        DCHECK(self->IsExceptionPending());                                                     \
-        if (kIsDebugBuild) {                                                                    \
-          LOG(WARNING) << "Suppressing exception for instruction-retry: "                       \
-                       << self->GetException()->Dump();                                         \
-        }                                                                                       \
-        self->ClearException();                                                                 \
-      }                                                                                         \
-    } else if (UNLIKELY(_is_exception_pending)) {                                               \
-      /* Should have succeeded. */                                                              \
-      DCHECK(!shadow_frame.GetForceRetryInstruction());                                         \
-      HANDLE_PENDING_EXCEPTION();                                                               \
-    } else {                                                                                    \
-      inst = inst->_next_function();                                                            \
-    }                                                                                           \
-  }                                                                                             \
-  do {} while (false)
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(_is_exception_pending) \
-  POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL(_is_exception_pending, Next_4xx)
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(_is_exception_pending) \
-  POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL(_is_exception_pending, Next_3xx)
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _next_function)  \
-  {                                                                               \
-    /* Should only be on invoke instructions. */                                  \
-    DCHECK(!shadow_frame.GetForceRetryInstruction());                             \
-    if (UNLIKELY(_is_exception_pending)) {                                        \
-      HANDLE_PENDING_EXCEPTION();                                                 \
-    } else {                                                                      \
-      inst = inst->_next_function();                                              \
-    }                                                                             \
-  }                                                                               \
-  do {} while (false)
-
-#define HANDLE_MONITOR_CHECKS()                                                                   \
-  if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) {                       \
-    HANDLE_PENDING_EXCEPTION();                                                                   \
-  }
-
-// Code to run before each dex instruction.
-#define PREAMBLE_SAVE(save_ref)                                                                 \
-  {                                                                                             \
-    /* We need to put this before & after the instrumentation to avoid having to put in a */    \
-    /* post-script macro.                                                                 */    \
-    CHECK_FORCE_RETURN();                                                                       \
-    if (UNLIKELY(instrumentation->HasDexPcListeners())) {                                       \
-      if (UNLIKELY(!DoDexPcMoveEvent(self,                                                      \
-                                     accessor,                                                  \
-                                     shadow_frame,                                              \
-                                     dex_pc,                                                    \
-                                     instrumentation,                                           \
-                                     save_ref))) {                                              \
-        HANDLE_PENDING_EXCEPTION();                                                             \
-        break;                                                                                  \
-      }                                                                                         \
-      CHECK_FORCE_RETURN();                                                                     \
-    }                                                                                           \
-  }                                                                                             \
-  do {} while (false)
-
-#define PREAMBLE() PREAMBLE_SAVE(nullptr)
-
-#define BRANCH_INSTRUMENTATION(offset)                                                         \
-  {                                                                                            \
-    if (UNLIKELY(instrumentation->HasBranchListeners())) {                                     \
-      instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset);                 \
-    }                                                                                          \
-    JValue result;                                                                             \
-    if (jit::Jit::MaybeDoOnStackReplacement(self,                                              \
-                                            shadow_frame.GetMethod(),                          \
-                                            dex_pc,                                            \
-                                            offset,                                            \
-                                            &result)) {                                        \
-      if (interpret_one_instruction) {                                                         \
-        /* OSR has completed execution of the method.  Signal mterp to return to caller */     \
-        shadow_frame.SetDexPC(dex::kDexNoIndex);                                               \
-      }                                                                                        \
-      ctx->result = result;                                                                    \
-      return;                                                                                  \
-    }                                                                                          \
-  }                                                                                            \
-  do {} while (false)
-
-#define HOTNESS_UPDATE()                                                                       \
-  {                                                                                            \
-    if (jit != nullptr) {                                                                      \
-      jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges=*/ true);            \
-    }                                                                                          \
-  }                                                                                            \
-  do {} while (false)
-
-#define HANDLE_ASYNC_EXCEPTION()                                                               \
-  if (UNLIKELY(self->ObserveAsyncException())) {                                               \
-    HANDLE_PENDING_EXCEPTION();                                                                \
-  }                                                                                            \
-  do {} while (false)
-
-#define HANDLE_BACKWARD_BRANCH(offset)                                                         \
-  {                                                                                            \
-    if (IsBackwardBranch(offset)) {                                                            \
-      HOTNESS_UPDATE();                                                                        \
-      /* Record new dex pc early to have consistent suspend point at loop header. */           \
-      shadow_frame.SetDexPC(inst->GetDexPc(insns));                                            \
-      self->AllowThreadSuspension();                                                           \
-    }                                                                                          \
-  }                                                                                            \
-  do {} while (false)
-
-// Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if
-// the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able
-// to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by
-// jvmti-agents while handling breakpoint or single step events. We had to move this into its own
-// function because it was making ExecuteSwitchImpl have too large a stack.
-NO_INLINE static bool DoDexPcMoveEvent(Thread* self,
-                                       const CodeItemDataAccessor& accessor,
-                                       const ShadowFrame& shadow_frame,
-                                       uint32_t dex_pc,
-                                       const instrumentation::Instrumentation* instrumentation,
-                                       JValue* save_ref)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  DCHECK(instrumentation->HasDexPcListeners());
-  StackHandleScope<2> hs(self);
-  Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException()));
-  mirror::Object* null_obj = nullptr;
-  HandleWrapper<mirror::Object> h(
-      hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot()));
-  self->ClearException();
-  instrumentation->DexPcMovedEvent(self,
-                                   shadow_frame.GetThisObject(accessor.InsSize()),
-                                   shadow_frame.GetMethod(),
-                                   dex_pc);
-  if (UNLIKELY(self->IsExceptionPending())) {
-    // We got a new exception in the dex-pc-moved event. We just let this exception replace the old
-    // one.
-    // TODO It would be good to add the old exception to the suppressed exceptions of the new one if
-    // possible.
-    return false;
-  } else {
-    if (UNLIKELY(!thr.IsNull())) {
-      self->SetException(thr.Get());
+// Short-lived helper class which executes single DEX bytecode.  It is inlined by compiler.
+//
+// The function names must match the names from dex_instruction_list.h and have no arguments.
+//
+// Any relevant execution information is stored in the fields - it should be kept to minimum.
+//
+// Helper methods may return boolean value - in which case 'false' always means
+// "stop executing current opcode" (which does not necessarily exit the interpreter loop).
+//
+template<bool do_access_check, bool transaction_active>
+class InstructionHandler {
+ public:
+  ALWAYS_INLINE WARN_UNUSED bool CheckForceReturn()
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
+      DCHECK(PrevFrameWillRetry(self, shadow_frame))
+          << "Pop frame forced without previous frame ready to retry instruction!";
+      DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+      if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) {
+        SendMethodExitEvents(self,
+                             instrumentation,
+                             shadow_frame,
+                             shadow_frame.GetThisObject(Accessor().InsSize()),
+                             shadow_frame.GetMethod(),
+                             inst->GetDexPc(Insns()),
+                             JValue());
+      }
+      ctx->result = JValue(); /* Handled in caller. */
+      exit_interpreter_loop = true;
+      return false;
     }
     return true;
   }
-}
 
-static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* ins)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  return ins->HasMethodExitListeners() || ins->HasWatchedFramePopListeners();
-}
+  NO_INLINE WARN_UNUSED bool HandlePendingExceptionWithInstrumentationImpl(
+      const instrumentation::Instrumentation* instr)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(self->IsExceptionPending());
+    self->AllowThreadSuspension();
+    if (!CheckForceReturn()) {
+      return false;
+    }
+    if (!MoveToExceptionHandler(self, shadow_frame, instr)) {
+      /* Structured locking is to be enforced for abnormal termination, too. */
+      DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame);
+      if (ctx->interpret_one_instruction) {
+        /* Signal mterp to return to caller */
+        shadow_frame.SetDexPC(dex::kDexNoIndex);
+      }
+      ctx->result = JValue(); /* Handled in caller. */
+      exit_interpreter_loop = true;
+      return false;  // Return to caller.
+    }
+    if (!CheckForceReturn()) {
+      return false;
+    }
+    int32_t displacement =
+        static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc);
+    inst = inst->RelativeAt(displacement);
+    return false;  // Stop executing this opcode and continue in the exception handler.
+  }
 
-// Sends the normal method exit event. Returns true if the events succeeded and false if there is a
-// pending exception.
-NO_INLINE static bool SendMethodExitEvents(Thread* self,
-                                           const instrumentation::Instrumentation* instrumentation,
-                                           const ShadowFrame& frame,
-                                           ObjPtr<mirror::Object> thiz,
-                                           ArtMethod* method,
-                                           uint32_t dex_pc,
-                                           const JValue& result)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  bool had_event = false;
-  // We don't send method-exit if it's a pop-frame. We still send frame_popped though.
-  if (UNLIKELY(instrumentation->HasMethodExitListeners() && !frame.GetForcePopFrame())) {
-    had_event = true;
-    instrumentation->MethodExitEvent(self, thiz.Ptr(), method, dex_pc, result);
+  // Forwards the call to the NO_INLINE HandlePendingExceptionWithInstrumentationImpl.
+  ALWAYS_INLINE WARN_UNUSED bool HandlePendingExceptionWithInstrumentation(
+      const instrumentation::Instrumentation* instr)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    // We need to help the compiler a bit to make the NO_INLINE call efficient.
+    //  * All handler fields should be in registers, so we do not want to take the object
+    //    address (for 'this' argument). Make a copy of the handler just for the slow path.
+    //  * The modifiable fields should also be in registers, so we don't want to store their
+    //    address even in the handler copy. Make a copy of them just for the call as well.
+    const Instruction* inst_copy = inst;
+    bool exit_loop_copy = exit_interpreter_loop;
+    InstructionHandler<do_access_check, transaction_active> handler_copy(
+        ctx, instrumentation, self, shadow_frame, dex_pc, inst_copy, inst_data, exit_loop_copy);
+    bool result = handler_copy.HandlePendingExceptionWithInstrumentationImpl(instr);
+    inst = inst_copy;
+    exit_interpreter_loop = exit_loop_copy;
+    return result;
   }
-  if (UNLIKELY(frame.NeedsNotifyPop() && instrumentation->HasWatchedFramePopListeners())) {
-    had_event = true;
-    instrumentation->WatchedFramePopped(self, frame);
+
+  ALWAYS_INLINE WARN_UNUSED bool HandlePendingException()
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return HandlePendingExceptionWithInstrumentation(instrumentation);
   }
-  if (UNLIKELY(had_event)) {
-    return !self->IsExceptionPending();
-  } else {
+
+  ALWAYS_INLINE WARN_UNUSED bool PossiblyHandlePendingExceptionOnInvokeImpl(
+      bool is_exception_pending,
+      const Instruction* next_inst)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (UNLIKELY(shadow_frame.GetForceRetryInstruction())) {
+      /* Don't need to do anything except clear the flag and exception. We leave the */
+      /* instruction the same so it will be re-executed on the next go-around.       */
+      DCHECK(inst->IsInvoke());
+      shadow_frame.SetForceRetryInstruction(false);
+      if (UNLIKELY(is_exception_pending)) {
+        DCHECK(self->IsExceptionPending());
+        if (kIsDebugBuild) {
+          LOG(WARNING) << "Suppressing exception for instruction-retry: "
+                       << self->GetException()->Dump();
+        }
+        self->ClearException();
+      }
+    } else if (UNLIKELY(is_exception_pending)) {
+      /* Should have succeeded. */
+      DCHECK(!shadow_frame.GetForceRetryInstruction());
+      if (!HandlePendingException()) {
+        return false;
+      }
+    } else {
+      inst = next_inst;
+    }
     return true;
   }
-}
+
+  ALWAYS_INLINE WARN_UNUSED bool PossiblyHandlePendingException(
+      bool is_exception_pending,
+      const Instruction* next_inst)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    /* Should only be on invoke instructions. */
+    DCHECK(!shadow_frame.GetForceRetryInstruction());
+    if (UNLIKELY(is_exception_pending)) {
+      if (!HandlePendingException()) {
+        return false;
+      }
+    } else {
+      inst = next_inst;
+    }
+    return true;
+  }
+
+  ALWAYS_INLINE WARN_UNUSED bool HandleMonitorChecks()
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) {
+      if (!HandlePendingException()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // Code to run before each dex instruction.
+  ALWAYS_INLINE WARN_UNUSED bool Preamble()
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    /* We need to put this before & after the instrumentation to avoid having to put in a */
+    /* post-script macro.                                                                 */
+    if (!CheckForceReturn()) {
+      return false;
+    }
+    if (UNLIKELY(instrumentation->HasDexPcListeners())) {
+      uint8_t opcode = inst->Opcode(inst_data);
+      bool is_move_result_object = (opcode == Instruction::MOVE_RESULT_OBJECT);
+      JValue* save_ref = is_move_result_object ? &ctx->result_register : nullptr;
+      if (UNLIKELY(!DoDexPcMoveEvent(self,
+                                     Accessor(),
+                                     shadow_frame,
+                                     dex_pc,
+                                     instrumentation,
+                                     save_ref))) {
+        if (!HandlePendingException()) {
+          return false;
+        }
+      }
+      if (!CheckForceReturn()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  ALWAYS_INLINE WARN_UNUSED bool BranchInstrumentation(int32_t offset)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (UNLIKELY(instrumentation->HasBranchListeners())) {
+      instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset);
+    }
+    JValue result;
+    if (jit::Jit::MaybeDoOnStackReplacement(self,
+                                            shadow_frame.GetMethod(),
+                                            dex_pc,
+                                            offset,
+                                            &result)) {
+      if (ctx->interpret_one_instruction) {
+        /* OSR has completed execution of the method.  Signal mterp to return to caller */
+        shadow_frame.SetDexPC(dex::kDexNoIndex);
+      }
+      ctx->result = result;
+      exit_interpreter_loop = true;
+      return false;
+    }
+    return true;
+  }
+
+  ALWAYS_INLINE void HotnessUpdate()
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    jit::Jit* jit = Runtime::Current()->GetJit();
+    if (jit != nullptr) {
+      jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges=*/ true);
+    }
+  }
+
+  ALWAYS_INLINE WARN_UNUSED bool HandleAsyncException()
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (UNLIKELY(self->ObserveAsyncException())) {
+      if (!HandlePendingException()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  ALWAYS_INLINE void HandleBackwardBranch(int32_t offset)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (IsBackwardBranch(offset)) {
+      HotnessUpdate();
+      /* Record new dex pc early to have consistent suspend point at loop header. */
+      shadow_frame.SetDexPC(inst->GetDexPc(Insns()));
+      self->AllowThreadSuspension();
+    }
+  }
+
+  // Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if
+  // the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able
+  // to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by
+  // jvmti-agents while handling breakpoint or single step events. We had to move this into its own
+  // function because it was making ExecuteSwitchImpl have too large a stack.
+  NO_INLINE static bool DoDexPcMoveEvent(Thread* self,
+                                         const CodeItemDataAccessor& accessor,
+                                         const ShadowFrame& shadow_frame,
+                                         uint32_t dex_pc,
+                                         const instrumentation::Instrumentation* instrumentation,
+                                         JValue* save_ref)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(instrumentation->HasDexPcListeners());
+    StackHandleScope<2> hs(self);
+    Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException()));
+    mirror::Object* null_obj = nullptr;
+    HandleWrapper<mirror::Object> h(
+        hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot()));
+    self->ClearException();
+    instrumentation->DexPcMovedEvent(self,
+                                     shadow_frame.GetThisObject(accessor.InsSize()),
+                                     shadow_frame.GetMethod(),
+                                     dex_pc);
+    if (UNLIKELY(self->IsExceptionPending())) {
+      // We got a new exception in the dex-pc-moved event.
+      // We just let this exception replace the old one.
+      // TODO It would be good to add the old exception to the
+      // suppressed exceptions of the new one if possible.
+      return false;
+    } else {
+      if (UNLIKELY(!thr.IsNull())) {
+        self->SetException(thr.Get());
+      }
+      return true;
+    }
+  }
+
+  static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* ins)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return ins->HasMethodExitListeners() || ins->HasWatchedFramePopListeners();
+  }
+
+  // Sends the normal method exit event.
+  // Returns true if the events succeeded and false if there is a pending exception.
+  NO_INLINE static bool SendMethodExitEvents(
+      Thread* self,
+      const instrumentation::Instrumentation* instrumentation,
+      const ShadowFrame& frame,
+      ObjPtr<mirror::Object> thiz,
+      ArtMethod* method,
+      uint32_t dex_pc,
+      const JValue& result)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool had_event = false;
+    // We don't send method-exit if it's a pop-frame. We still send frame_popped though.
+    if (UNLIKELY(instrumentation->HasMethodExitListeners() && !frame.GetForcePopFrame())) {
+      had_event = true;
+      instrumentation->MethodExitEvent(self, thiz.Ptr(), method, dex_pc, result);
+    }
+    if (UNLIKELY(frame.NeedsNotifyPop() && instrumentation->HasWatchedFramePopListeners())) {
+      had_event = true;
+      instrumentation->WatchedFramePopped(self, frame);
+    }
+    if (UNLIKELY(had_event)) {
+      return !self->IsExceptionPending();
+    } else {
+      return true;
+    }
+  }
+
+#define BRANCH_INSTRUMENTATION(offset)                                                            \
+  if (!BranchInstrumentation(offset)) {                                                           \
+    return;                                                                                       \
+  }
+
+#define HANDLE_PENDING_EXCEPTION()                                                                \
+  if (!HandlePendingException()) {                                                                \
+    return;                                                                                       \
+  }
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION(is_exception_pending, next_function)                    \
+  if (!PossiblyHandlePendingException(is_exception_pending, inst->next_function())) {             \
+    return;                                                                                       \
+  }
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(is_exception_pending)             \
+  if (!PossiblyHandlePendingExceptionOnInvokeImpl(is_exception_pending, inst->Next_4xx())) {      \
+    return;                                                                                       \
+  }
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(is_exception_pending)                         \
+  if (!PossiblyHandlePendingExceptionOnInvokeImpl(is_exception_pending, inst->Next_3xx())) {      \
+    return;                                                                                       \
+  }
+
+  ALWAYS_INLINE void NOP() REQUIRES_SHARED(Locks::mutator_lock_) {
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MOVE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MOVE_FROM16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_22x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void MOVE_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_32x(),
+                         shadow_frame.GetVReg(inst->VRegB_32x()));
+    inst = inst->Next_3xx();
+  }
+
+  ALWAYS_INLINE void MOVE_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MOVE_WIDE_FROM16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
+                             shadow_frame.GetVRegLong(inst->VRegB_22x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void MOVE_WIDE_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_32x(),
+                             shadow_frame.GetVRegLong(inst->VRegB_32x()));
+    inst = inst->Next_3xx();
+  }
+
+  ALWAYS_INLINE void MOVE_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data),
+                                  shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MOVE_OBJECT_FROM16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data),
+                                  shadow_frame.GetVRegReference(inst->VRegB_22x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void MOVE_OBJECT_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegReference(inst->VRegA_32x(),
+                                  shadow_frame.GetVRegReference(inst->VRegB_32x()));
+    inst = inst->Next_3xx();
+  }
+
+  ALWAYS_INLINE void MOVE_RESULT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_11x(inst_data), ResultRegister()->GetI());
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MOVE_RESULT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), ResultRegister()->GetJ());
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MOVE_RESULT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), ResultRegister()->GetL());
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MOVE_EXCEPTION() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Throwable> exception = self->GetException();
+    DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
+    shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
+    self->ClearException();
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void RETURN_VOID_NO_BARRIER() REQUIRES_SHARED(Locks::mutator_lock_) {
+    JValue result;
+    self->AllowThreadSuspension();
+    if (!HandleMonitorChecks()) {
+      return;
+    }
+    if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                 !SendMethodExitEvents(self,
+                                       instrumentation,
+                                       shadow_frame,
+                                       shadow_frame.GetThisObject(Accessor().InsSize()),
+                                       shadow_frame.GetMethod(),
+                                       inst->GetDexPc(Insns()),
+                                       result))) {
+      if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+        return;
+      }
+    }
+    if (ctx->interpret_one_instruction) {
+      /* Signal mterp to return to caller */
+      shadow_frame.SetDexPC(dex::kDexNoIndex);
+    }
+    ctx->result = result;
+    exit_interpreter_loop = true;
+  }
+
+  ALWAYS_INLINE void RETURN_VOID() REQUIRES_SHARED(Locks::mutator_lock_) {
+    QuasiAtomic::ThreadFenceForConstructor();
+    JValue result;
+    self->AllowThreadSuspension();
+    if (!HandleMonitorChecks()) {
+      return;
+    }
+    if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                 !SendMethodExitEvents(self,
+                                       instrumentation,
+                                       shadow_frame,
+                                       shadow_frame.GetThisObject(Accessor().InsSize()),
+                                       shadow_frame.GetMethod(),
+                                       inst->GetDexPc(Insns()),
+                                       result))) {
+      if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+        return;
+      }
+    }
+    if (ctx->interpret_one_instruction) {
+      /* Signal mterp to return to caller */
+      shadow_frame.SetDexPC(dex::kDexNoIndex);
+    }
+    ctx->result = result;
+    exit_interpreter_loop = true;
+  }
+
+  ALWAYS_INLINE void RETURN() REQUIRES_SHARED(Locks::mutator_lock_) {
+    JValue result;
+    result.SetJ(0);
+    result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
+    self->AllowThreadSuspension();
+    if (!HandleMonitorChecks()) {
+      return;
+    }
+    if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                 !SendMethodExitEvents(self,
+                                       instrumentation,
+                                       shadow_frame,
+                                       shadow_frame.GetThisObject(Accessor().InsSize()),
+                                       shadow_frame.GetMethod(),
+                                       inst->GetDexPc(Insns()),
+                                       result))) {
+      if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+        return;
+      }
+    }
+    if (ctx->interpret_one_instruction) {
+      /* Signal mterp to return to caller */
+      shadow_frame.SetDexPC(dex::kDexNoIndex);
+    }
+    ctx->result = result;
+    exit_interpreter_loop = true;
+  }
+
+  ALWAYS_INLINE void RETURN_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    JValue result;
+    result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
+    self->AllowThreadSuspension();
+    if (!HandleMonitorChecks()) {
+      return;
+    }
+    if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                 !SendMethodExitEvents(self,
+                                       instrumentation,
+                                       shadow_frame,
+                                       shadow_frame.GetThisObject(Accessor().InsSize()),
+                                       shadow_frame.GetMethod(),
+                                       inst->GetDexPc(Insns()),
+                                       result))) {
+      if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+        return;
+      }
+    }
+    if (ctx->interpret_one_instruction) {
+      /* Signal mterp to return to caller */
+      shadow_frame.SetDexPC(dex::kDexNoIndex);
+    }
+    ctx->result = result;
+    exit_interpreter_loop = true;
+  }
+
+  ALWAYS_INLINE void RETURN_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    JValue result;
+    self->AllowThreadSuspension();
+    if (!HandleMonitorChecks()) {
+      return;
+    }
+    const size_t ref_idx = inst->VRegA_11x(inst_data);
+    ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
+    if (do_assignability_check && obj_result != nullptr) {
+      ObjPtr<mirror::Class> return_type = shadow_frame.GetMethod()->ResolveReturnType();
+      // Re-load since it might have moved.
+      obj_result = shadow_frame.GetVRegReference(ref_idx);
+      if (return_type == nullptr) {
+        // Return the pending exception.
+        HANDLE_PENDING_EXCEPTION();
+      }
+      if (!obj_result->VerifierInstanceOf(return_type)) {
+        // This should never happen.
+        std::string temp1, temp2;
+        self->ThrowNewExceptionF("Ljava/lang/InternalError;",
+                                 "Returning '%s' that is not instance of return type '%s'",
+                                 obj_result->GetClass()->GetDescriptor(&temp1),
+                                 return_type->GetDescriptor(&temp2));
+        HANDLE_PENDING_EXCEPTION();
+      }
+    }
+    result.SetL(obj_result);
+    if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                 !SendMethodExitEvents(self,
+                                       instrumentation,
+                                       shadow_frame,
+                                       shadow_frame.GetThisObject(Accessor().InsSize()),
+                                       shadow_frame.GetMethod(),
+                                       inst->GetDexPc(Insns()),
+                                       result))) {
+      if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+        return;
+      }
+    }
+    // Re-load since it might have moved during the MethodExitEvent.
+    result.SetL(shadow_frame.GetVRegReference(ref_idx));
+    if (ctx->interpret_one_instruction) {
+      /* Signal mterp to return to caller */
+      shadow_frame.SetDexPC(dex::kDexNoIndex);
+    }
+    ctx->result = result;
+    exit_interpreter_loop = true;
+  }
+
+  ALWAYS_INLINE void CONST_4() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t dst = inst->VRegA_11n(inst_data);
+    int4_t val = inst->VRegB_11n(inst_data);
+    shadow_frame.SetVReg(dst, val);
+    if (val == 0) {
+      shadow_frame.SetVRegReference(dst, nullptr);
+    }
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void CONST_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint8_t dst = inst->VRegA_21s(inst_data);
+    int16_t val = inst->VRegB_21s();
+    shadow_frame.SetVReg(dst, val);
+    if (val == 0) {
+      shadow_frame.SetVRegReference(dst, nullptr);
+    }
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void CONST() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint8_t dst = inst->VRegA_31i(inst_data);
+    int32_t val = inst->VRegB_31i();
+    shadow_frame.SetVReg(dst, val);
+    if (val == 0) {
+      shadow_frame.SetVRegReference(dst, nullptr);
+    }
+    inst = inst->Next_3xx();
+  }
+
+  ALWAYS_INLINE void CONST_HIGH16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint8_t dst = inst->VRegA_21h(inst_data);
+    int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
+    shadow_frame.SetVReg(dst, val);
+    if (val == 0) {
+      shadow_frame.SetVRegReference(dst, nullptr);
+    }
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void CONST_WIDE_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s());
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void CONST_WIDE_32() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i());
+    inst = inst->Next_3xx();
+  }
+
+  ALWAYS_INLINE void CONST_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l());
+    inst = inst->Next_51l();
+  }
+
+  ALWAYS_INLINE void CONST_WIDE_HIGH16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data),
+                             static_cast<uint64_t>(inst->VRegB_21h()) << 48);
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void CONST_STRING() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::String> s = ResolveString(self,
+                                             shadow_frame,
+                                             dex::StringIndex(inst->VRegB_21c()));
+    if (UNLIKELY(s == nullptr)) {
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void CONST_STRING_JUMBO() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::String> s = ResolveString(self,
+                                             shadow_frame,
+                                             dex::StringIndex(inst->VRegB_31c()));
+    if (UNLIKELY(s == nullptr)) {
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
+      inst = inst->Next_3xx();
+    }
+  }
+
+  ALWAYS_INLINE void CONST_CLASS() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
+                                                     shadow_frame.GetMethod(),
+                                                     self,
+                                                     false,
+                                                     do_access_check);
+    if (UNLIKELY(c == nullptr)) {
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void CONST_METHOD_HANDLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ClassLinker* cl = Runtime::Current()->GetClassLinker();
+    ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self,
+                                                              inst->VRegB_21c(),
+                                                              shadow_frame.GetMethod());
+    if (UNLIKELY(mh == nullptr)) {
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void CONST_METHOD_TYPE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ClassLinker* cl = Runtime::Current()->GetClassLinker();
+    ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self,
+                                                          dex::ProtoIndex(inst->VRegB_21c()),
+                                                          shadow_frame.GetMethod());
+    if (UNLIKELY(mt == nullptr)) {
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void MONITOR_ENTER() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!HandleAsyncException()) {
+      return;
+    }
+    ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+    if (UNLIKELY(obj == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      DoMonitorEnter<do_assignability_check>(self, &shadow_frame, obj);
+      POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+    }
+  }
+
+  ALWAYS_INLINE void MONITOR_EXIT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!HandleAsyncException()) {
+      return;
+    }
+    ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+    if (UNLIKELY(obj == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      DoMonitorExit<do_assignability_check>(self, &shadow_frame, obj);
+      POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+    }
+  }
+
+  ALWAYS_INLINE void CHECK_CAST() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
+                                                     shadow_frame.GetMethod(),
+                                                     self,
+                                                     false,
+                                                     do_access_check);
+    if (UNLIKELY(c == nullptr)) {
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
+      if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) {
+        ThrowClassCastException(c, obj->GetClass());
+        HANDLE_PENDING_EXCEPTION();
+      } else {
+        inst = inst->Next_2xx();
+      }
+    }
+  }
+
+  ALWAYS_INLINE void INSTANCE_OF() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegC_22c()),
+                                                     shadow_frame.GetMethod(),
+                                                     self,
+                                                     false,
+                                                     do_access_check);
+    if (UNLIKELY(c == nullptr)) {
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+      shadow_frame.SetVReg(inst->VRegA_22c(inst_data),
+                           (obj != nullptr && obj->InstanceOf(c)) ? 1 : 0);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void ARRAY_LENGTH() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
+    if (UNLIKELY(array == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
+      inst = inst->Next_1xx();
+    }
+  }
+
+  ALWAYS_INLINE void NEW_INSTANCE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> obj = nullptr;
+    ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
+                                                     shadow_frame.GetMethod(),
+                                                     self,
+                                                     false,
+                                                     do_access_check);
+    if (LIKELY(c != nullptr)) {
+      if (UNLIKELY(c->IsStringClass())) {
+        gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+        obj = mirror::String::AllocEmptyString<true>(self, allocator_type);
+      } else {
+        obj = AllocObjectFromCode<true>(
+            c.Ptr(),
+            self,
+            Runtime::Current()->GetHeap()->GetCurrentAllocator());
+      }
+    }
+    if (UNLIKELY(obj == nullptr)) {
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      obj->GetClass()->AssertInitializedOrInitializingInThread(self);
+      // Don't allow finalizable objects to be allocated during a transaction since these can't
+      // be finalized without a started runtime.
+      if (transaction_active && obj->GetClass()->IsFinalizable()) {
+        AbortTransactionF(self, "Allocating finalizable object in transaction: %s",
+                          obj->PrettyTypeOf().c_str());
+        HANDLE_PENDING_EXCEPTION();
+      }
+      shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void NEW_ARRAY() REQUIRES_SHARED(Locks::mutator_lock_) {
+    int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
+    ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check, true>(
+        dex::TypeIndex(inst->VRegC_22c()),
+        length,
+        shadow_frame.GetMethod(),
+        self,
+        Runtime::Current()->GetHeap()->GetCurrentAllocator());
+    if (UNLIKELY(obj == nullptr)) {
+      HANDLE_PENDING_EXCEPTION();
+    } else {
+      shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void FILLED_NEW_ARRAY() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success =
+        DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self,
+                                                                     ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+  }
+
+  ALWAYS_INLINE void FILLED_NEW_ARRAY_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success =
+        DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
+                                                                    self, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+  }
+
+  ALWAYS_INLINE void FILL_ARRAY_DATA() REQUIRES_SHARED(Locks::mutator_lock_) {
+    const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+    const Instruction::ArrayDataPayload* payload =
+        reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
+    ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
+    bool success = FillArrayData(obj, payload);
+    if (!success) {
+      HANDLE_PENDING_EXCEPTION();
+    }
+    if (transaction_active) {
+      RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count);
+    }
+    inst = inst->Next_3xx();
+  }
+
+  ALWAYS_INLINE void THROW() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!HandleAsyncException()) {
+      return;
+    }
+    ObjPtr<mirror::Object> exception =
+        shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+    if (UNLIKELY(exception == nullptr)) {
+      ThrowNullPointerException("throw with null exception");
+    } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
+      // This should never happen.
+      std::string temp;
+      self->ThrowNewExceptionF("Ljava/lang/InternalError;",
+                               "Throwing '%s' that is not instance of Throwable",
+                               exception->GetClass()->GetDescriptor(&temp));
+    } else {
+      self->SetException(exception->AsThrowable());
+    }
+    HANDLE_PENDING_EXCEPTION();
+  }
+
+  ALWAYS_INLINE void GOTO() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!HandleAsyncException()) {
+      return;
+    }
+    int8_t offset = inst->VRegA_10t(inst_data);
+    BRANCH_INSTRUMENTATION(offset);
+    inst = inst->RelativeAt(offset);
+    HandleBackwardBranch(offset);
+  }
+
+  ALWAYS_INLINE void GOTO_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!HandleAsyncException()) {
+      return;
+    }
+    int16_t offset = inst->VRegA_20t();
+    BRANCH_INSTRUMENTATION(offset);
+    inst = inst->RelativeAt(offset);
+    HandleBackwardBranch(offset);
+  }
+
+  ALWAYS_INLINE void GOTO_32() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!HandleAsyncException()) {
+      return;
+    }
+    int32_t offset = inst->VRegA_30t();
+    BRANCH_INSTRUMENTATION(offset);
+    inst = inst->RelativeAt(offset);
+    HandleBackwardBranch(offset);
+  }
+
+  ALWAYS_INLINE void PACKED_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) {
+    int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
+    BRANCH_INSTRUMENTATION(offset);
+    inst = inst->RelativeAt(offset);
+    HandleBackwardBranch(offset);
+  }
+
+  ALWAYS_INLINE void SPARSE_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) {
+    int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
+    BRANCH_INSTRUMENTATION(offset);
+    inst = inst->RelativeAt(offset);
+    HandleBackwardBranch(offset);
+  }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wfloat-equal"
+
+
+  ALWAYS_INLINE void CMPL_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+    float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+    int32_t result;
+    if (val1 > val2) {
+      result = 1;
+    } else if (val1 == val2) {
+      result = 0;
+    } else {
+      result = -1;
+    }
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void CMPG_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+    float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+    int32_t result;
+    if (val1 < val2) {
+      result = -1;
+    } else if (val1 == val2) {
+      result = 0;
+    } else {
+      result = 1;
+    }
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void CMPL_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+    double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+    int32_t result;
+    if (val1 > val2) {
+      result = 1;
+    } else if (val1 == val2) {
+      result = 0;
+    } else {
+      result = -1;
+    }
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+    inst = inst->Next_2xx();
+  }
+
+
+  ALWAYS_INLINE void CMPG_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+    double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+    int32_t result;
+    if (val1 < val2) {
+      result = -1;
+    } else if (val1 == val2) {
+      result = 0;
+    } else {
+      result = 1;
+    }
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+    inst = inst->Next_2xx();
+  }
+
+#pragma clang diagnostic pop
+
+
+  ALWAYS_INLINE void CMP_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
+    int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
+    int32_t result;
+    if (val1 > val2) {
+      result = 1;
+    } else if (val1 == val2) {
+      result = 0;
+    } else {
+      result = -1;
+    }
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void IF_EQ() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) ==
+        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+      int16_t offset = inst->VRegC_22t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_NE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) !=
+        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+      int16_t offset = inst->VRegC_22t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_LT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <
+        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+      int16_t offset = inst->VRegC_22t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_GE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >=
+        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+      int16_t offset = inst->VRegC_22t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_GT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >
+    shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+      int16_t offset = inst->VRegC_22t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_LE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <=
+        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+      int16_t offset = inst->VRegC_22t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_EQZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
+      int16_t offset = inst->VRegB_21t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_NEZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
+      int16_t offset = inst->VRegB_21t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_LTZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
+      int16_t offset = inst->VRegB_21t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_GEZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
+      int16_t offset = inst->VRegB_21t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_GTZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
+      int16_t offset = inst->VRegB_21t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void IF_LEZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
+      int16_t offset = inst->VRegB_21t();
+      BRANCH_INSTRUMENTATION(offset);
+      inst = inst->RelativeAt(offset);
+      HandleBackwardBranch(offset);
+    } else {
+      BRANCH_INSTRUMENTATION(2);
+      inst = inst->Next_2xx();
+    }
+  }
+
+  ALWAYS_INLINE void AGET_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
+    if (array->CheckIsValidIndex(index)) {
+      shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void AGET_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::ByteArray> array = a->AsByteArray();
+    if (array->CheckIsValidIndex(index)) {
+      shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void AGET_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::CharArray> array = a->AsCharArray();
+    if (array->CheckIsValidIndex(index)) {
+      shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void AGET_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::ShortArray> array = a->AsShortArray();
+    if (array->CheckIsValidIndex(index)) {
+      shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void AGET() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
+    ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
+    if (array->CheckIsValidIndex(index)) {
+      shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void AGET_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
+    ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
+    if (array->CheckIsValidIndex(index)) {
+      shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void AGET_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
+    if (array->CheckIsValidIndex(index)) {
+      shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void APUT_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
+    if (array->CheckIsValidIndex(index)) {
+      array->SetWithoutChecks<transaction_active>(index, val);
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void APUT_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::ByteArray> array = a->AsByteArray();
+    if (array->CheckIsValidIndex(index)) {
+      array->SetWithoutChecks<transaction_active>(index, val);
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void APUT_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::CharArray> array = a->AsCharArray();
+    if (array->CheckIsValidIndex(index)) {
+      array->SetWithoutChecks<transaction_active>(index, val);
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void APUT_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::ShortArray> array = a->AsShortArray();
+    if (array->CheckIsValidIndex(index)) {
+      array->SetWithoutChecks<transaction_active>(index, val);
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void APUT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
+    ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
+    if (array->CheckIsValidIndex(index)) {
+      array->SetWithoutChecks<transaction_active>(index, val);
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void APUT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
+    ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
+    if (array->CheckIsValidIndex(index)) {
+      array->SetWithoutChecks<transaction_active>(index, val);
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void APUT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+    if (UNLIKELY(a == nullptr)) {
+      ThrowNullPointerExceptionFromInterpreter();
+      HANDLE_PENDING_EXCEPTION();
+    }
+    int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+    ObjPtr<mirror::Object> val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
+    ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
+    if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) {
+      array->SetWithoutChecks<transaction_active>(index, val);
+      inst = inst->Next_2xx();
+    } else {
+      HANDLE_PENDING_EXCEPTION();
+    }
+  }
+
+  ALWAYS_INLINE void IGET_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(
+        self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(
+        self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(
+        self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(
+        self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(
+        self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(
+        self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(
+        self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_WIDE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_OBJECT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_BOOLEAN_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_BYTE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_CHAR_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IGET_SHORT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SGET_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SGET_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SGET_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SGET_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SGET() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SGET_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SGET_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(
+        shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_BOOLEAN_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>(
+        shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_BYTE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>(
+        shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_CHAR_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>(
+        shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_SHORT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>(
+        shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_WIDE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(
+        shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void IPUT_OBJECT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(
+        shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SPUT_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SPUT_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SPUT_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SPUT_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SPUT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SPUT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SPUT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check,
+        transaction_active>(self, shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void INVOKE_VIRTUAL() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_VIRTUAL_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_SUPER() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kSuper, false, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_SUPER_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kSuper, true, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_DIRECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kDirect, false, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_DIRECT_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kDirect, true, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_INTERFACE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kInterface, false, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_INTERFACE_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kInterface, true, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_STATIC() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kStatic, false, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_STATIC_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kStatic, true, do_access_check, /*is_mterp=*/ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_VIRTUAL_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false,
+        /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_VIRTUAL_RANGE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false,
+        /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_POLYMORPHIC() REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+    bool success = DoInvokePolymorphic</* is_range= */ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_POLYMORPHIC_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+    bool success = DoInvokePolymorphic</* is_range= */ true>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_CUSTOM() REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+    bool success = DoInvokeCustom</* is_range= */ false>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void INVOKE_CUSTOM_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+    bool success = DoInvokeCustom</* is_range= */ true>(
+        self, shadow_frame, inst, inst_data, ResultRegister());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+  }
+
+  ALWAYS_INLINE void NEG_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(
+        inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void NOT_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(
+        inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void NEG_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(
+        inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void NOT_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(
+        inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void NEG_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegFloat(
+        inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void NEG_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegDouble(
+        inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void INT_TO_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+                             shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void INT_TO_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+                              shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void INT_TO_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+                               shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void LONG_TO_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+                         shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void LONG_TO_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+                              shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void LONG_TO_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+                               shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void FLOAT_TO_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+    int32_t result = art_float_to_integral<int32_t, float>(val);
+    shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void FLOAT_TO_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+    int64_t result = art_float_to_integral<int64_t, float>(val);
+    shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void FLOAT_TO_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+                               shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void DOUBLE_TO_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+    int32_t result = art_float_to_integral<int32_t, double>(val);
+    shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void DOUBLE_TO_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+    int64_t result = art_float_to_integral<int64_t, double>(val);
+    shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void DOUBLE_TO_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+                              shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void INT_TO_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int8_t>(
+        shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void INT_TO_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<uint16_t>(
+        shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void INT_TO_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int16_t>(
+        shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void ADD_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+                         SafeAdd(shadow_frame.GetVReg(inst->VRegB_23x()),
+                                 shadow_frame.GetVReg(inst->VRegC_23x())));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void SUB_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+                         SafeSub(shadow_frame.GetVReg(inst->VRegB_23x()),
+                                 shadow_frame.GetVReg(inst->VRegC_23x())));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void MUL_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+                         SafeMul(shadow_frame.GetVReg(inst->VRegB_23x()),
+                                 shadow_frame.GetVReg(inst->VRegC_23x())));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void DIV_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
+                               shadow_frame.GetVReg(inst->VRegB_23x()),
+                               shadow_frame.GetVReg(inst->VRegC_23x()));
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void REM_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+                                  shadow_frame.GetVReg(inst->VRegB_23x()),
+                                  shadow_frame.GetVReg(inst->VRegC_23x()));
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void SHL_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_23x()) <<
+                         (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void SHR_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_23x()) >>
+                         (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void USHR_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+                         static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
+                         (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void AND_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_23x()) &
+                         shadow_frame.GetVReg(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void OR_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_23x()) |
+                         shadow_frame.GetVReg(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void XOR_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_23x()) ^
+                         shadow_frame.GetVReg(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void ADD_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+                             SafeAdd(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                                     shadow_frame.GetVRegLong(inst->VRegC_23x())));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void SUB_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+                             SafeSub(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                                     shadow_frame.GetVRegLong(inst->VRegC_23x())));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void MUL_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+                             SafeMul(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                                     shadow_frame.GetVRegLong(inst->VRegC_23x())));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void DIV_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
+                 shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                 shadow_frame.GetVRegLong(inst->VRegC_23x()));
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
+  }
+
+  ALWAYS_INLINE void REM_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+                    shadow_frame.GetVRegLong(inst->VRegB_23x()),
+                    shadow_frame.GetVRegLong(inst->VRegC_23x()));
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
+  }
+
+  ALWAYS_INLINE void AND_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+                             shadow_frame.GetVRegLong(inst->VRegB_23x()) &
+                             shadow_frame.GetVRegLong(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void OR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+                             shadow_frame.GetVRegLong(inst->VRegB_23x()) |
+                             shadow_frame.GetVRegLong(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void XOR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+                             shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
+                             shadow_frame.GetVRegLong(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void SHL_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+                             shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
+                             (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void SHR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+                             shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
+                             (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void USHR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+                             static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
+                             (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void ADD_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+                              shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
+                              shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void SUB_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+                              shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
+                              shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void MUL_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+                              shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
+                              shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void DIV_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+                              shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
+                              shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void REM_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+                              fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
+                                    shadow_frame.GetVRegFloat(inst->VRegC_23x())));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void ADD_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+                               shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
+                               shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void SUB_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+                               shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
+                               shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void MUL_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+                               shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
+                               shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void DIV_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+                               shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
+                               shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void REM_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+                               fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
+                                    shadow_frame.GetVRegDouble(inst->VRegC_23x())));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void ADD_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVReg(vregA, SafeAdd(shadow_frame.GetVReg(vregA),
+                                        shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void SUB_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVReg(vregA,
+                         SafeSub(shadow_frame.GetVReg(vregA),
+                                 shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MUL_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVReg(vregA,
+                         SafeMul(shadow_frame.GetVReg(vregA),
+                                 shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void DIV_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+                               shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
+  }
+
+  ALWAYS_INLINE void REM_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+                                  shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
+  }
+
+  ALWAYS_INLINE void SHL_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVReg(vregA,
+                         shadow_frame.GetVReg(vregA) <<
+                         (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void SHR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVReg(vregA,
+                         shadow_frame.GetVReg(vregA) >>
+                         (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void USHR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVReg(vregA,
+                         static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
+                         (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void AND_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVReg(vregA,
+                         shadow_frame.GetVReg(vregA) &
+                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void OR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVReg(vregA,
+                         shadow_frame.GetVReg(vregA) |
+                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void XOR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVReg(vregA,
+                         shadow_frame.GetVReg(vregA) ^
+                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void ADD_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegLong(vregA,
+                             SafeAdd(shadow_frame.GetVRegLong(vregA),
+                                     shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void SUB_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegLong(vregA,
+                             SafeSub(shadow_frame.GetVRegLong(vregA),
+                                     shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MUL_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegLong(vregA,
+                             SafeMul(shadow_frame.GetVRegLong(vregA),
+                                     shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void DIV_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+                shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+  }
+
+  ALWAYS_INLINE void REM_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+                    shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+  }
+
+  ALWAYS_INLINE void AND_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegLong(vregA,
+                             shadow_frame.GetVRegLong(vregA) &
+                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void OR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegLong(vregA,
+                             shadow_frame.GetVRegLong(vregA) |
+                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void XOR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegLong(vregA,
+                             shadow_frame.GetVRegLong(vregA) ^
+                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void SHL_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegLong(vregA,
+                             shadow_frame.GetVRegLong(vregA) <<
+                             (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void SHR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegLong(vregA,
+                             shadow_frame.GetVRegLong(vregA) >>
+                             (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void USHR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegLong(vregA,
+                             static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
+                             (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void ADD_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegFloat(vregA,
+                              shadow_frame.GetVRegFloat(vregA) +
+                              shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void SUB_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegFloat(vregA,
+                              shadow_frame.GetVRegFloat(vregA) -
+                              shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MUL_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegFloat(vregA,
+                              shadow_frame.GetVRegFloat(vregA) *
+                              shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void DIV_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegFloat(vregA,
+                              shadow_frame.GetVRegFloat(vregA) /
+                              shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void REM_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegFloat(vregA,
+                              fmodf(shadow_frame.GetVRegFloat(vregA),
+                                    shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void ADD_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegDouble(vregA,
+                               shadow_frame.GetVRegDouble(vregA) +
+                               shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void SUB_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegDouble(vregA,
+                               shadow_frame.GetVRegDouble(vregA) -
+                               shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void MUL_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegDouble(vregA,
+                               shadow_frame.GetVRegDouble(vregA) *
+                               shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void DIV_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegDouble(vregA,
+                               shadow_frame.GetVRegDouble(vregA) /
+                               shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void REM_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint4_t vregA = inst->VRegA_12x(inst_data);
+    shadow_frame.SetVRegDouble(vregA,
+                               fmod(shadow_frame.GetVRegDouble(vregA),
+                                    shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))));
+    inst = inst->Next_1xx();
+  }
+
+  ALWAYS_INLINE void ADD_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+                         SafeAdd(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+                                 inst->VRegC_22s()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void RSUB_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+                         SafeSub(inst->VRegC_22s(),
+                                 shadow_frame.GetVReg(inst->VRegB_22s(inst_data))));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void MUL_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+                         SafeMul(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+                                 inst->VRegC_22s()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void DIV_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data),
+                               shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+                               inst->VRegC_22s());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void REM_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data),
+                                  shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+                                  inst->VRegC_22s());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void AND_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) &
+                         inst->VRegC_22s());
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void OR_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) |
+                         inst->VRegC_22s());
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void XOR_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^
+                         inst->VRegC_22s());
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void ADD_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+                         SafeAdd(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void RSUB_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+                         SafeSub(inst->VRegC_22b(), shadow_frame.GetVReg(inst->VRegB_22b())));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void MUL_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+                         SafeMul(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void DIV_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data),
+                               shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void REM_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data),
+                                  shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+  }
+
+  ALWAYS_INLINE void AND_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_22b()) &
+                         inst->VRegC_22b());
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void OR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_22b()) |
+                         inst->VRegC_22b());
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void XOR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_22b()) ^
+                         inst->VRegC_22b());
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void SHL_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_22b()) <<
+                         (inst->VRegC_22b() & 0x1f));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void SHR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+                         shadow_frame.GetVReg(inst->VRegB_22b()) >>
+                         (inst->VRegC_22b() & 0x1f));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void USHR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+                         static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
+                         (inst->VRegC_22b() & 0x1f));
+    inst = inst->Next_2xx();
+  }
+
+  ALWAYS_INLINE void UNUSED_3E() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_3F() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_40() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_41() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_42() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_43() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_79() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_7A() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_F3() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_F4() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_F5() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_F6() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_F7() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_F8() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE void UNUSED_F9() REQUIRES_SHARED(Locks::mutator_lock_) {
+    UnexpectedOpcode(inst, shadow_frame);
+  }
+
+  ALWAYS_INLINE InstructionHandler(SwitchImplContext* ctx,
+                                   const instrumentation::Instrumentation* instrumentation,
+                                   Thread* self,
+                                   ShadowFrame& shadow_frame,
+                                   uint16_t dex_pc,
+                                   const Instruction*& inst,
+                                   uint16_t inst_data,
+                                   bool& exit_interpreter_loop)
+    : ctx(ctx),
+      instrumentation(instrumentation),
+      self(self),
+      shadow_frame(shadow_frame),
+      dex_pc(dex_pc),
+      inst(inst),
+      inst_data(inst_data),
+      exit_interpreter_loop(exit_interpreter_loop) {
+  }
+
+ private:
+  static constexpr bool do_assignability_check = do_access_check;
+
+  const CodeItemDataAccessor& Accessor() { return ctx->accessor; }
+  const uint16_t* Insns() { return ctx->accessor.Insns(); }
+  JValue* ResultRegister() { return &ctx->result_register; }
+
+  SwitchImplContext* const ctx;
+  const instrumentation::Instrumentation* const instrumentation;
+  Thread* const self;
+  ShadowFrame& shadow_frame;
+  uint32_t const dex_pc;
+  const Instruction*& inst;
+  uint16_t const inst_data;
+  bool& exit_interpreter_loop;
+};
+
+#undef BRANCH_INSTRUMENTATION
+#undef POSSIBLY_HANDLE_PENDING_EXCEPTION
+#undef POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE
+#undef POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC
+#undef HANDLE_PENDING_EXCEPTION
 
 // TODO On ASAN builds this function gets a huge stack frame. Since normally we run in the mterp
 // this shouldn't cause any problems for stack overflow detection. Remove this once b/117341496 is
@@ -276,9 +2587,6 @@
   Thread* self = ctx->self;
   const CodeItemDataAccessor& accessor = ctx->accessor;
   ShadowFrame& shadow_frame = ctx->shadow_frame;
-  JValue result_register = ctx->result_register;
-  bool interpret_one_instruction = ctx->interpret_one_instruction;
-  constexpr bool do_assignability_check = do_access_check;
   if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
     LOG(FATAL) << "Invalid shadow frame for interpreter use";
     ctx->result = JValue();
@@ -291,2294 +2599,53 @@
   const uint16_t* const insns = accessor.Insns();
   const Instruction* inst = Instruction::At(insns + dex_pc);
   uint16_t inst_data;
-  jit::Jit* jit = Runtime::Current()->GetJit();
 
   DCHECK(!shadow_frame.GetForceRetryInstruction())
       << "Entered interpreter from invoke without retry instruction being handled!";
 
-  do {
+  bool const interpret_one_instruction = ctx->interpret_one_instruction;
+  while (true) {
     dex_pc = inst->GetDexPc(insns);
     shadow_frame.SetDexPC(dex_pc);
     TraceExecution(shadow_frame, inst, dex_pc);
     inst_data = inst->Fetch16(0);
-    switch (inst->Opcode(inst_data)) {
-      case Instruction::NOP:
-        PREAMBLE();
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::MOVE:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::MOVE_FROM16:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::MOVE_16:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_32x(),
-                             shadow_frame.GetVReg(inst->VRegB_32x()));
-        inst = inst->Next_3xx();
-        break;
-      case Instruction::MOVE_WIDE:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::MOVE_WIDE_FROM16:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_22x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::MOVE_WIDE_16:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_32x(),
-                                 shadow_frame.GetVRegLong(inst->VRegB_32x()));
-        inst = inst->Next_3xx();
-        break;
-      case Instruction::MOVE_OBJECT:
-        PREAMBLE();
-        shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data),
-                                      shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::MOVE_OBJECT_FROM16:
-        PREAMBLE();
-        shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data),
-                                      shadow_frame.GetVRegReference(inst->VRegB_22x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::MOVE_OBJECT_16:
-        PREAMBLE();
-        shadow_frame.SetVRegReference(inst->VRegA_32x(),
-                                      shadow_frame.GetVRegReference(inst->VRegB_32x()));
-        inst = inst->Next_3xx();
-        break;
-      case Instruction::MOVE_RESULT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_11x(inst_data), result_register.GetI());
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::MOVE_RESULT_WIDE:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ());
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::MOVE_RESULT_OBJECT:
-        PREAMBLE_SAVE(&result_register);
-        shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::MOVE_EXCEPTION: {
-        PREAMBLE();
-        ObjPtr<mirror::Throwable> exception = self->GetException();
-        DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
-        shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
-        self->ClearException();
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::RETURN_VOID_NO_BARRIER: {
-        PREAMBLE();
-        JValue result;
-        self->AllowThreadSuspension();
-        HANDLE_MONITOR_CHECKS();
-        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
-                     !SendMethodExitEvents(self,
-                                           instrumentation,
-                                           shadow_frame,
-                                           shadow_frame.GetThisObject(accessor.InsSize()),
-                                           shadow_frame.GetMethod(),
-                                           inst->GetDexPc(insns),
-                                           result))) {
-          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
+    {
+      bool exit_loop = false;
+      InstructionHandler<do_access_check, transaction_active> handler(
+          ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop);
+      if (!handler.Preamble()) {
+        if (UNLIKELY(exit_loop)) {
+          return;
         }
-        if (interpret_one_instruction) {
-          /* Signal mterp to return to caller */
-          shadow_frame.SetDexPC(dex::kDexNoIndex);
-        }
-        ctx->result = result;
-        return;
-      }
-      case Instruction::RETURN_VOID: {
-        PREAMBLE();
-        QuasiAtomic::ThreadFenceForConstructor();
-        JValue result;
-        self->AllowThreadSuspension();
-        HANDLE_MONITOR_CHECKS();
-        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
-                     !SendMethodExitEvents(self,
-                                           instrumentation,
-                                           shadow_frame,
-                                           shadow_frame.GetThisObject(accessor.InsSize()),
-                                           shadow_frame.GetMethod(),
-                                           inst->GetDexPc(insns),
-                                           result))) {
-          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
-        }
-        if (interpret_one_instruction) {
-          /* Signal mterp to return to caller */
-          shadow_frame.SetDexPC(dex::kDexNoIndex);
-        }
-        ctx->result = result;
-        return;
-      }
-      case Instruction::RETURN: {
-        PREAMBLE();
-        JValue result;
-        result.SetJ(0);
-        result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
-        self->AllowThreadSuspension();
-        HANDLE_MONITOR_CHECKS();
-        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
-                     !SendMethodExitEvents(self,
-                                           instrumentation,
-                                           shadow_frame,
-                                           shadow_frame.GetThisObject(accessor.InsSize()),
-                                           shadow_frame.GetMethod(),
-                                           inst->GetDexPc(insns),
-                                           result))) {
-          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
-        }
-        if (interpret_one_instruction) {
-          /* Signal mterp to return to caller */
-          shadow_frame.SetDexPC(dex::kDexNoIndex);
-        }
-        ctx->result = result;
-        return;
-      }
-      case Instruction::RETURN_WIDE: {
-        PREAMBLE();
-        JValue result;
-        result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
-        self->AllowThreadSuspension();
-        HANDLE_MONITOR_CHECKS();
-        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
-                     !SendMethodExitEvents(self,
-                                           instrumentation,
-                                           shadow_frame,
-                                           shadow_frame.GetThisObject(accessor.InsSize()),
-                                           shadow_frame.GetMethod(),
-                                           inst->GetDexPc(insns),
-                                           result))) {
-          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
-        }
-        if (interpret_one_instruction) {
-          /* Signal mterp to return to caller */
-          shadow_frame.SetDexPC(dex::kDexNoIndex);
-        }
-        ctx->result = result;
-        return;
-      }
-      case Instruction::RETURN_OBJECT: {
-        PREAMBLE();
-        JValue result;
-        self->AllowThreadSuspension();
-        HANDLE_MONITOR_CHECKS();
-        const size_t ref_idx = inst->VRegA_11x(inst_data);
-        ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
-        if (do_assignability_check && obj_result != nullptr) {
-          ObjPtr<mirror::Class> return_type = shadow_frame.GetMethod()->ResolveReturnType();
-          // Re-load since it might have moved.
-          obj_result = shadow_frame.GetVRegReference(ref_idx);
-          if (return_type == nullptr) {
-            // Return the pending exception.
-            HANDLE_PENDING_EXCEPTION();
-          }
-          if (!obj_result->VerifierInstanceOf(return_type)) {
-            // This should never happen.
-            std::string temp1, temp2;
-            self->ThrowNewExceptionF("Ljava/lang/InternalError;",
-                                     "Returning '%s' that is not instance of return type '%s'",
-                                     obj_result->GetClass()->GetDescriptor(&temp1),
-                                     return_type->GetDescriptor(&temp2));
-            HANDLE_PENDING_EXCEPTION();
-          }
-        }
-        result.SetL(obj_result);
-        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
-                     !SendMethodExitEvents(self,
-                                           instrumentation,
-                                           shadow_frame,
-                                           shadow_frame.GetThisObject(accessor.InsSize()),
-                                           shadow_frame.GetMethod(),
-                                           inst->GetDexPc(insns),
-                                           result))) {
-          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
-        }
-        // Re-load since it might have moved during the MethodExitEvent.
-        result.SetL(shadow_frame.GetVRegReference(ref_idx));
-        if (interpret_one_instruction) {
-          /* Signal mterp to return to caller */
-          shadow_frame.SetDexPC(dex::kDexNoIndex);
-        }
-        ctx->result = result;
-        return;
-      }
-      case Instruction::CONST_4: {
-        PREAMBLE();
-        uint4_t dst = inst->VRegA_11n(inst_data);
-        int4_t val = inst->VRegB_11n(inst_data);
-        shadow_frame.SetVReg(dst, val);
-        if (val == 0) {
-          shadow_frame.SetVRegReference(dst, nullptr);
-        }
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::CONST_16: {
-        PREAMBLE();
-        uint8_t dst = inst->VRegA_21s(inst_data);
-        int16_t val = inst->VRegB_21s();
-        shadow_frame.SetVReg(dst, val);
-        if (val == 0) {
-          shadow_frame.SetVRegReference(dst, nullptr);
-        }
-        inst = inst->Next_2xx();
-        break;
-      }
-      case Instruction::CONST: {
-        PREAMBLE();
-        uint8_t dst = inst->VRegA_31i(inst_data);
-        int32_t val = inst->VRegB_31i();
-        shadow_frame.SetVReg(dst, val);
-        if (val == 0) {
-          shadow_frame.SetVRegReference(dst, nullptr);
-        }
-        inst = inst->Next_3xx();
-        break;
-      }
-      case Instruction::CONST_HIGH16: {
-        PREAMBLE();
-        uint8_t dst = inst->VRegA_21h(inst_data);
-        int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
-        shadow_frame.SetVReg(dst, val);
-        if (val == 0) {
-          shadow_frame.SetVRegReference(dst, nullptr);
-        }
-        inst = inst->Next_2xx();
-        break;
-      }
-      case Instruction::CONST_WIDE_16:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s());
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::CONST_WIDE_32:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i());
-        inst = inst->Next_3xx();
-        break;
-      case Instruction::CONST_WIDE:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l());
-        inst = inst->Next_51l();
-        break;
-      case Instruction::CONST_WIDE_HIGH16:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data),
-                                 static_cast<uint64_t>(inst->VRegB_21h()) << 48);
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::CONST_STRING: {
-        PREAMBLE();
-        ObjPtr<mirror::String> s = ResolveString(self,
-                                                 shadow_frame,
-                                                 dex::StringIndex(inst->VRegB_21c()));
-        if (UNLIKELY(s == nullptr)) {
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::CONST_STRING_JUMBO: {
-        PREAMBLE();
-        ObjPtr<mirror::String> s = ResolveString(self,
-                                                 shadow_frame,
-                                                 dex::StringIndex(inst->VRegB_31c()));
-        if (UNLIKELY(s == nullptr)) {
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
-          inst = inst->Next_3xx();
-        }
-        break;
-      }
-      case Instruction::CONST_CLASS: {
-        PREAMBLE();
-        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
-                                                         shadow_frame.GetMethod(),
-                                                         self,
-                                                         false,
-                                                         do_access_check);
-        if (UNLIKELY(c == nullptr)) {
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::CONST_METHOD_HANDLE: {
-        PREAMBLE();
-        ClassLinker* cl = Runtime::Current()->GetClassLinker();
-        ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self,
-                                                                  inst->VRegB_21c(),
-                                                                  shadow_frame.GetMethod());
-        if (UNLIKELY(mh == nullptr)) {
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::CONST_METHOD_TYPE: {
-        PREAMBLE();
-        ClassLinker* cl = Runtime::Current()->GetClassLinker();
-        ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self,
-                                                              dex::ProtoIndex(inst->VRegB_21c()),
-                                                              shadow_frame.GetMethod());
-        if (UNLIKELY(mt == nullptr)) {
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::MONITOR_ENTER: {
-        PREAMBLE();
-        HANDLE_ASYNC_EXCEPTION();
-        ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
-        if (UNLIKELY(obj == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          DoMonitorEnter<do_assignability_check>(self, &shadow_frame, obj);
-          POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
-        }
-        break;
-      }
-      case Instruction::MONITOR_EXIT: {
-        PREAMBLE();
-        HANDLE_ASYNC_EXCEPTION();
-        ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
-        if (UNLIKELY(obj == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          DoMonitorExit<do_assignability_check>(self, &shadow_frame, obj);
-          POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
-        }
-        break;
-      }
-      case Instruction::CHECK_CAST: {
-        PREAMBLE();
-        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
-                                                         shadow_frame.GetMethod(),
-                                                         self,
-                                                         false,
-                                                         do_access_check);
-        if (UNLIKELY(c == nullptr)) {
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
-          if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) {
-            ThrowClassCastException(c, obj->GetClass());
-            HANDLE_PENDING_EXCEPTION();
-          } else {
-            inst = inst->Next_2xx();
-          }
-        }
-        break;
-      }
-      case Instruction::INSTANCE_OF: {
-        PREAMBLE();
-        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegC_22c()),
-                                                         shadow_frame.GetMethod(),
-                                                         self,
-                                                         false,
-                                                         do_access_check);
-        if (UNLIKELY(c == nullptr)) {
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
-          shadow_frame.SetVReg(inst->VRegA_22c(inst_data),
-                               (obj != nullptr && obj->InstanceOf(c)) ? 1 : 0);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::ARRAY_LENGTH:  {
-        PREAMBLE();
-        ObjPtr<mirror::Object> array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
-        if (UNLIKELY(array == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
-          inst = inst->Next_1xx();
-        }
-        break;
-      }
-      case Instruction::NEW_INSTANCE: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> obj = nullptr;
-        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
-                                                         shadow_frame.GetMethod(),
-                                                         self,
-                                                         false,
-                                                         do_access_check);
-        if (LIKELY(c != nullptr)) {
-          if (UNLIKELY(c->IsStringClass())) {
-            gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
-            obj = mirror::String::AllocEmptyString<true>(self, allocator_type);
-          } else {
-            obj = AllocObjectFromCode<true>(
-                c.Ptr(),
-                self,
-                Runtime::Current()->GetHeap()->GetCurrentAllocator());
-          }
-        }
-        if (UNLIKELY(obj == nullptr)) {
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          obj->GetClass()->AssertInitializedOrInitializingInThread(self);
-          // Don't allow finalizable objects to be allocated during a transaction since these can't
-          // be finalized without a started runtime.
-          if (transaction_active && obj->GetClass()->IsFinalizable()) {
-            AbortTransactionF(self, "Allocating finalizable object in transaction: %s",
-                              obj->PrettyTypeOf().c_str());
-            HANDLE_PENDING_EXCEPTION();
-            break;
-          }
-          shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::NEW_ARRAY: {
-        PREAMBLE();
-        int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
-        ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check, true>(
-            dex::TypeIndex(inst->VRegC_22c()),
-            length,
-            shadow_frame.GetMethod(),
-            self,
-            Runtime::Current()->GetHeap()->GetCurrentAllocator());
-        if (UNLIKELY(obj == nullptr)) {
-          HANDLE_PENDING_EXCEPTION();
-        } else {
-          shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::FILLED_NEW_ARRAY: {
-        PREAMBLE();
-        bool success =
-            DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self,
-                                                                         &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
-        break;
-      }
-      case Instruction::FILLED_NEW_ARRAY_RANGE: {
-        PREAMBLE();
-        bool success =
-            DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
-                                                                        self, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
-        break;
-      }
-      case Instruction::FILL_ARRAY_DATA: {
-        PREAMBLE();
-        const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
-        const Instruction::ArrayDataPayload* payload =
-            reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
-        ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
-        bool success = FillArrayData(obj, payload);
-        if (!success) {
-          HANDLE_PENDING_EXCEPTION();
+        if (UNLIKELY(interpret_one_instruction)) {
           break;
         }
-        if (transaction_active) {
-          RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count);
-        }
-        inst = inst->Next_3xx();
-        break;
-      }
-      case Instruction::THROW: {
-        PREAMBLE();
-        HANDLE_ASYNC_EXCEPTION();
-        ObjPtr<mirror::Object> exception =
-            shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
-        if (UNLIKELY(exception == nullptr)) {
-          ThrowNullPointerException("throw with null exception");
-        } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
-          // This should never happen.
-          std::string temp;
-          self->ThrowNewExceptionF("Ljava/lang/InternalError;",
-                                   "Throwing '%s' that is not instance of Throwable",
-                                   exception->GetClass()->GetDescriptor(&temp));
-        } else {
-          self->SetException(exception->AsThrowable());
-        }
-        HANDLE_PENDING_EXCEPTION();
-        break;
-      }
-      case Instruction::GOTO: {
-        PREAMBLE();
-        HANDLE_ASYNC_EXCEPTION();
-        int8_t offset = inst->VRegA_10t(inst_data);
-        BRANCH_INSTRUMENTATION(offset);
-        inst = inst->RelativeAt(offset);
-        HANDLE_BACKWARD_BRANCH(offset);
-        break;
-      }
-      case Instruction::GOTO_16: {
-        PREAMBLE();
-        HANDLE_ASYNC_EXCEPTION();
-        int16_t offset = inst->VRegA_20t();
-        BRANCH_INSTRUMENTATION(offset);
-        inst = inst->RelativeAt(offset);
-        HANDLE_BACKWARD_BRANCH(offset);
-        break;
-      }
-      case Instruction::GOTO_32: {
-        PREAMBLE();
-        HANDLE_ASYNC_EXCEPTION();
-        int32_t offset = inst->VRegA_30t();
-        BRANCH_INSTRUMENTATION(offset);
-        inst = inst->RelativeAt(offset);
-        HANDLE_BACKWARD_BRANCH(offset);
-        break;
-      }
-      case Instruction::PACKED_SWITCH: {
-        PREAMBLE();
-        int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
-        BRANCH_INSTRUMENTATION(offset);
-        inst = inst->RelativeAt(offset);
-        HANDLE_BACKWARD_BRANCH(offset);
-        break;
-      }
-      case Instruction::SPARSE_SWITCH: {
-        PREAMBLE();
-        int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
-        BRANCH_INSTRUMENTATION(offset);
-        inst = inst->RelativeAt(offset);
-        HANDLE_BACKWARD_BRANCH(offset);
-        break;
-      }
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wfloat-equal"
-
-      case Instruction::CMPL_FLOAT: {
-        PREAMBLE();
-        float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
-        float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
-        int32_t result;
-        if (val1 > val2) {
-          result = 1;
-        } else if (val1 == val2) {
-          result = 0;
-        } else {
-          result = -1;
-        }
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-        inst = inst->Next_2xx();
-        break;
-      }
-      case Instruction::CMPG_FLOAT: {
-        PREAMBLE();
-        float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
-        float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
-        int32_t result;
-        if (val1 < val2) {
-          result = -1;
-        } else if (val1 == val2) {
-          result = 0;
-        } else {
-          result = 1;
-        }
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-        inst = inst->Next_2xx();
-        break;
-      }
-      case Instruction::CMPL_DOUBLE: {
-        PREAMBLE();
-        double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
-        double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
-        int32_t result;
-        if (val1 > val2) {
-          result = 1;
-        } else if (val1 == val2) {
-          result = 0;
-        } else {
-          result = -1;
-        }
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-        inst = inst->Next_2xx();
-        break;
-      }
-
-      case Instruction::CMPG_DOUBLE: {
-        PREAMBLE();
-        double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
-        double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
-        int32_t result;
-        if (val1 < val2) {
-          result = -1;
-        } else if (val1 == val2) {
-          result = 0;
-        } else {
-          result = 1;
-        }
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-        inst = inst->Next_2xx();
-        break;
-      }
-
-#pragma clang diagnostic pop
-
-      case Instruction::CMP_LONG: {
-        PREAMBLE();
-        int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
-        int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
-        int32_t result;
-        if (val1 > val2) {
-          result = 1;
-        } else if (val1 == val2) {
-          result = 0;
-        } else {
-          result = -1;
-        }
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-        inst = inst->Next_2xx();
-        break;
-      }
-      case Instruction::IF_EQ: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) ==
-            shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-          int16_t offset = inst->VRegC_22t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_NE: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) !=
-            shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-          int16_t offset = inst->VRegC_22t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_LT: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <
-            shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-          int16_t offset = inst->VRegC_22t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_GE: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >=
-            shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-          int16_t offset = inst->VRegC_22t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_GT: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >
-        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-          int16_t offset = inst->VRegC_22t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_LE: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <=
-            shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-          int16_t offset = inst->VRegC_22t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_EQZ: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
-          int16_t offset = inst->VRegB_21t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_NEZ: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
-          int16_t offset = inst->VRegB_21t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_LTZ: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
-          int16_t offset = inst->VRegB_21t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_GEZ: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
-          int16_t offset = inst->VRegB_21t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_GTZ: {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
-          int16_t offset = inst->VRegB_21t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::IF_LEZ:  {
-        PREAMBLE();
-        if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
-          int16_t offset = inst->VRegB_21t();
-          BRANCH_INSTRUMENTATION(offset);
-          inst = inst->RelativeAt(offset);
-          HANDLE_BACKWARD_BRANCH(offset);
-        } else {
-          BRANCH_INSTRUMENTATION(2);
-          inst = inst->Next_2xx();
-        }
-        break;
-      }
-      case Instruction::AGET_BOOLEAN: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
-        if (array->CheckIsValidIndex(index)) {
-          shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::AGET_BYTE: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::ByteArray> array = a->AsByteArray();
-        if (array->CheckIsValidIndex(index)) {
-          shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::AGET_CHAR: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::CharArray> array = a->AsCharArray();
-        if (array->CheckIsValidIndex(index)) {
-          shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::AGET_SHORT: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::ShortArray> array = a->AsShortArray();
-        if (array->CheckIsValidIndex(index)) {
-          shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::AGET: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
-        ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
-        if (array->CheckIsValidIndex(index)) {
-          shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::AGET_WIDE:  {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
-        ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
-        if (array->CheckIsValidIndex(index)) {
-          shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::AGET_OBJECT: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
-        if (array->CheckIsValidIndex(index)) {
-          shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::APUT_BOOLEAN: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
-        if (array->CheckIsValidIndex(index)) {
-          array->SetWithoutChecks<transaction_active>(index, val);
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::APUT_BYTE: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::ByteArray> array = a->AsByteArray();
-        if (array->CheckIsValidIndex(index)) {
-          array->SetWithoutChecks<transaction_active>(index, val);
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::APUT_CHAR: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::CharArray> array = a->AsCharArray();
-        if (array->CheckIsValidIndex(index)) {
-          array->SetWithoutChecks<transaction_active>(index, val);
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::APUT_SHORT: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::ShortArray> array = a->AsShortArray();
-        if (array->CheckIsValidIndex(index)) {
-          array->SetWithoutChecks<transaction_active>(index, val);
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::APUT: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
-        ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
-        if (array->CheckIsValidIndex(index)) {
-          array->SetWithoutChecks<transaction_active>(index, val);
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::APUT_WIDE: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
-        ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
-        if (array->CheckIsValidIndex(index)) {
-          array->SetWithoutChecks<transaction_active>(index, val);
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::APUT_OBJECT: {
-        PREAMBLE();
-        ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-        if (UNLIKELY(a == nullptr)) {
-          ThrowNullPointerExceptionFromInterpreter();
-          HANDLE_PENDING_EXCEPTION();
-          break;
-        }
-        int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-        ObjPtr<mirror::Object> val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
-        ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
-        if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) {
-          array->SetWithoutChecks<transaction_active>(index, val);
-          inst = inst->Next_2xx();
-        } else {
-          HANDLE_PENDING_EXCEPTION();
-        }
-        break;
-      }
-      case Instruction::IGET_BOOLEAN: {
-        PREAMBLE();
-        bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(
-            self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_BYTE: {
-        PREAMBLE();
-        bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(
-            self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_CHAR: {
-        PREAMBLE();
-        bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(
-            self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_SHORT: {
-        PREAMBLE();
-        bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(
-            self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET: {
-        PREAMBLE();
-        bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(
-            self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_WIDE: {
-        PREAMBLE();
-        bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(
-            self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_OBJECT: {
-        PREAMBLE();
-        bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(
-            self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_QUICK: {
-        PREAMBLE();
-        bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_WIDE_QUICK: {
-        PREAMBLE();
-        bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_OBJECT_QUICK: {
-        PREAMBLE();
-        bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_BOOLEAN_QUICK: {
-        PREAMBLE();
-        bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_BYTE_QUICK: {
-        PREAMBLE();
-        bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_CHAR_QUICK: {
-        PREAMBLE();
-        bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IGET_SHORT_QUICK: {
-        PREAMBLE();
-        bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SGET_BOOLEAN: {
-        PREAMBLE();
-        bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SGET_BYTE: {
-        PREAMBLE();
-        bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SGET_CHAR: {
-        PREAMBLE();
-        bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SGET_SHORT: {
-        PREAMBLE();
-        bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SGET: {
-        PREAMBLE();
-        bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SGET_WIDE: {
-        PREAMBLE();
-        bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SGET_OBJECT: {
-        PREAMBLE();
-        bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_BOOLEAN: {
-        PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_BYTE: {
-        PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_CHAR: {
-        PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_SHORT: {
-        PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT: {
-        PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_WIDE: {
-        PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_OBJECT: {
-        PREAMBLE();
-        bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_QUICK: {
-        PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(
-            shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_BOOLEAN_QUICK: {
-        PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>(
-            shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_BYTE_QUICK: {
-        PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>(
-            shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_CHAR_QUICK: {
-        PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>(
-            shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_SHORT_QUICK: {
-        PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>(
-            shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_WIDE_QUICK: {
-        PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(
-            shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::IPUT_OBJECT_QUICK: {
-        PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(
-            shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SPUT_BOOLEAN: {
-        PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SPUT_BYTE: {
-        PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SPUT_CHAR: {
-        PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SPUT_SHORT: {
-        PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SPUT: {
-        PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SPUT_WIDE: {
-        PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SPUT_OBJECT: {
-        PREAMBLE();
-        bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check,
-            transaction_active>(self, shadow_frame, inst, inst_data);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::INVOKE_VIRTUAL: {
-        PREAMBLE();
-        bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_VIRTUAL_RANGE: {
-        PREAMBLE();
-        bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_SUPER: {
-        PREAMBLE();
-        bool success = DoInvoke<kSuper, false, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_SUPER_RANGE: {
-        PREAMBLE();
-        bool success = DoInvoke<kSuper, true, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_DIRECT: {
-        PREAMBLE();
-        bool success = DoInvoke<kDirect, false, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_DIRECT_RANGE: {
-        PREAMBLE();
-        bool success = DoInvoke<kDirect, true, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_INTERFACE: {
-        PREAMBLE();
-        bool success = DoInvoke<kInterface, false, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_INTERFACE_RANGE: {
-        PREAMBLE();
-        bool success = DoInvoke<kInterface, true, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_STATIC: {
-        PREAMBLE();
-        bool success = DoInvoke<kStatic, false, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_STATIC_RANGE: {
-        PREAMBLE();
-        bool success = DoInvoke<kStatic, true, do_access_check, /*is_mterp=*/ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_VIRTUAL_QUICK: {
-        PREAMBLE();
-        bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false,
-            /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
-        PREAMBLE();
-        bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false,
-            /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_POLYMORPHIC: {
-        PREAMBLE();
-        DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-        bool success = DoInvokePolymorphic</* is_range= */ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
-        break;
-      }
-      case Instruction::INVOKE_POLYMORPHIC_RANGE: {
-        PREAMBLE();
-        DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-        bool success = DoInvokePolymorphic</* is_range= */ true>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
-        break;
-      }
-      case Instruction::INVOKE_CUSTOM: {
-        PREAMBLE();
-        DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-        bool success = DoInvokeCustom</* is_range= */ false>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::INVOKE_CUSTOM_RANGE: {
-        PREAMBLE();
-        DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-        bool success = DoInvokeCustom</* is_range= */ true>(
-            self, shadow_frame, inst, inst_data, &result_register);
-        POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-        break;
-      }
-      case Instruction::NEG_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(
-            inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::NOT_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(
-            inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::NEG_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(
-            inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::NOT_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(
-            inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::NEG_FLOAT:
-        PREAMBLE();
-        shadow_frame.SetVRegFloat(
-            inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::NEG_DOUBLE:
-        PREAMBLE();
-        shadow_frame.SetVRegDouble(
-            inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::INT_TO_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
-                                 shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::INT_TO_FLOAT:
-        PREAMBLE();
-        shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
-                                  shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::INT_TO_DOUBLE:
-        PREAMBLE();
-        shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
-                                   shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::LONG_TO_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::LONG_TO_FLOAT:
-        PREAMBLE();
-        shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
-                                  shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::LONG_TO_DOUBLE:
-        PREAMBLE();
-        shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
-                                   shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::FLOAT_TO_INT: {
-        PREAMBLE();
-        float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
-        int32_t result = art_float_to_integral<int32_t, float>(val);
-        shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::FLOAT_TO_LONG: {
-        PREAMBLE();
-        float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
-        int64_t result = art_float_to_integral<int64_t, float>(val);
-        shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::FLOAT_TO_DOUBLE:
-        PREAMBLE();
-        shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
-                                   shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::DOUBLE_TO_INT: {
-        PREAMBLE();
-        double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
-        int32_t result = art_float_to_integral<int32_t, double>(val);
-        shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::DOUBLE_TO_LONG: {
-        PREAMBLE();
-        double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
-        int64_t result = art_float_to_integral<int64_t, double>(val);
-        shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::DOUBLE_TO_FLOAT:
-        PREAMBLE();
-        shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
-                                  shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::INT_TO_BYTE:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int8_t>(
-            shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::INT_TO_CHAR:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<uint16_t>(
-            shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::INT_TO_SHORT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int16_t>(
-            shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      case Instruction::ADD_INT: {
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             SafeAdd(shadow_frame.GetVReg(inst->VRegB_23x()),
-                                     shadow_frame.GetVReg(inst->VRegC_23x())));
-        inst = inst->Next_2xx();
-        break;
-      }
-      case Instruction::SUB_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             SafeSub(shadow_frame.GetVReg(inst->VRegB_23x()),
-                                     shadow_frame.GetVReg(inst->VRegC_23x())));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::MUL_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             SafeMul(shadow_frame.GetVReg(inst->VRegB_23x()),
-                                     shadow_frame.GetVReg(inst->VRegC_23x())));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::DIV_INT: {
-        PREAMBLE();
-        bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
-                                   shadow_frame.GetVReg(inst->VRegB_23x()),
-                                   shadow_frame.GetVReg(inst->VRegC_23x()));
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::REM_INT: {
-        PREAMBLE();
-        bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data),
-                                      shadow_frame.GetVReg(inst->VRegB_23x()),
-                                      shadow_frame.GetVReg(inst->VRegC_23x()));
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::SHL_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_23x()) <<
-                             (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::SHR_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_23x()) >>
-                             (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::USHR_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
-                             (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::AND_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_23x()) &
-                             shadow_frame.GetVReg(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::OR_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_23x()) |
-                             shadow_frame.GetVReg(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::XOR_INT:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_23x()) ^
-                             shadow_frame.GetVReg(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::ADD_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 SafeAdd(shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                                         shadow_frame.GetVRegLong(inst->VRegC_23x())));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::SUB_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 SafeSub(shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                                         shadow_frame.GetVRegLong(inst->VRegC_23x())));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::MUL_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 SafeMul(shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                                         shadow_frame.GetVRegLong(inst->VRegC_23x())));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::DIV_LONG:
-        PREAMBLE();
-        DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
-                     shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                     shadow_frame.GetVRegLong(inst->VRegC_23x()));
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
-        break;
-      case Instruction::REM_LONG:
-        PREAMBLE();
-        DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data),
-                        shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                        shadow_frame.GetVRegLong(inst->VRegC_23x()));
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
-        break;
-      case Instruction::AND_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_23x()) &
-                                 shadow_frame.GetVRegLong(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::OR_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_23x()) |
-                                 shadow_frame.GetVRegLong(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::XOR_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
-                                 shadow_frame.GetVRegLong(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::SHL_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
-                                 (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::SHR_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
-                                 (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::USHR_LONG:
-        PREAMBLE();
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                                 static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
-                                 (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::ADD_FLOAT:
-        PREAMBLE();
-        shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                                  shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
-                                  shadow_frame.GetVRegFloat(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::SUB_FLOAT:
-        PREAMBLE();
-        shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                                  shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
-                                  shadow_frame.GetVRegFloat(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::MUL_FLOAT:
-        PREAMBLE();
-        shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                                  shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
-                                  shadow_frame.GetVRegFloat(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::DIV_FLOAT:
-        PREAMBLE();
-        shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                                  shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
-                                  shadow_frame.GetVRegFloat(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::REM_FLOAT:
-        PREAMBLE();
-        shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                                  fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
-                                        shadow_frame.GetVRegFloat(inst->VRegC_23x())));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::ADD_DOUBLE:
-        PREAMBLE();
-        shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                                   shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
-                                   shadow_frame.GetVRegDouble(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::SUB_DOUBLE:
-        PREAMBLE();
-        shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                                   shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
-                                   shadow_frame.GetVRegDouble(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::MUL_DOUBLE:
-        PREAMBLE();
-        shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                                   shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
-                                   shadow_frame.GetVRegDouble(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::DIV_DOUBLE:
-        PREAMBLE();
-        shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                                   shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
-                                   shadow_frame.GetVRegDouble(inst->VRegC_23x()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::REM_DOUBLE:
-        PREAMBLE();
-        shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                                   fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
-                                        shadow_frame.GetVRegDouble(inst->VRegC_23x())));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::ADD_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA, SafeAdd(shadow_frame.GetVReg(vregA),
-                                            shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::SUB_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA,
-                             SafeSub(shadow_frame.GetVReg(vregA),
-                                     shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::MUL_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA,
-                             SafeMul(shadow_frame.GetVReg(vregA),
-                                     shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::DIV_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
-                                   shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
-        break;
-      }
-      case Instruction::REM_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
-                                      shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
-        break;
-      }
-      case Instruction::SHL_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA,
-                             shadow_frame.GetVReg(vregA) <<
-                             (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::SHR_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA,
-                             shadow_frame.GetVReg(vregA) >>
-                             (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::USHR_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA,
-                             static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
-                             (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::AND_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA,
-                             shadow_frame.GetVReg(vregA) &
-                             shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::OR_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA,
-                             shadow_frame.GetVReg(vregA) |
-                             shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::XOR_INT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVReg(vregA,
-                             shadow_frame.GetVReg(vregA) ^
-                             shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::ADD_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegLong(vregA,
-                                 SafeAdd(shadow_frame.GetVRegLong(vregA),
-                                         shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::SUB_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegLong(vregA,
-                                 SafeSub(shadow_frame.GetVRegLong(vregA),
-                                         shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::MUL_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegLong(vregA,
-                                 SafeMul(shadow_frame.GetVRegLong(vregA),
-                                         shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::DIV_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
-                    shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
-        break;
-      }
-      case Instruction::REM_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
-                        shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
-        break;
-      }
-      case Instruction::AND_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegLong(vregA,
-                                 shadow_frame.GetVRegLong(vregA) &
-                                 shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::OR_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegLong(vregA,
-                                 shadow_frame.GetVRegLong(vregA) |
-                                 shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::XOR_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegLong(vregA,
-                                 shadow_frame.GetVRegLong(vregA) ^
-                                 shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::SHL_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegLong(vregA,
-                                 shadow_frame.GetVRegLong(vregA) <<
-                                 (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::SHR_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegLong(vregA,
-                                 shadow_frame.GetVRegLong(vregA) >>
-                                 (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::USHR_LONG_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegLong(vregA,
-                                 static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
-                                 (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::ADD_FLOAT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegFloat(vregA,
-                                  shadow_frame.GetVRegFloat(vregA) +
-                                  shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::SUB_FLOAT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegFloat(vregA,
-                                  shadow_frame.GetVRegFloat(vregA) -
-                                  shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::MUL_FLOAT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegFloat(vregA,
-                                  shadow_frame.GetVRegFloat(vregA) *
-                                  shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::DIV_FLOAT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegFloat(vregA,
-                                  shadow_frame.GetVRegFloat(vregA) /
-                                  shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::REM_FLOAT_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegFloat(vregA,
-                                  fmodf(shadow_frame.GetVRegFloat(vregA),
-                                        shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::ADD_DOUBLE_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegDouble(vregA,
-                                   shadow_frame.GetVRegDouble(vregA) +
-                                   shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::SUB_DOUBLE_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegDouble(vregA,
-                                   shadow_frame.GetVRegDouble(vregA) -
-                                   shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::MUL_DOUBLE_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegDouble(vregA,
-                                   shadow_frame.GetVRegDouble(vregA) *
-                                   shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::DIV_DOUBLE_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegDouble(vregA,
-                                   shadow_frame.GetVRegDouble(vregA) /
-                                   shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::REM_DOUBLE_2ADDR: {
-        PREAMBLE();
-        uint4_t vregA = inst->VRegA_12x(inst_data);
-        shadow_frame.SetVRegDouble(vregA,
-                                   fmod(shadow_frame.GetVRegDouble(vregA),
-                                        shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))));
-        inst = inst->Next_1xx();
-        break;
-      }
-      case Instruction::ADD_INT_LIT16:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                             SafeAdd(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
-                                     inst->VRegC_22s()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::RSUB_INT_LIT16:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                             SafeSub(inst->VRegC_22s(),
-                                     shadow_frame.GetVReg(inst->VRegB_22s(inst_data))));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::MUL_INT_LIT16:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                             SafeMul(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
-                                     inst->VRegC_22s()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::DIV_INT_LIT16: {
-        PREAMBLE();
-        bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data),
-                                   shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
-                                   inst->VRegC_22s());
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::REM_INT_LIT16: {
-        PREAMBLE();
-        bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data),
-                                      shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
-                                      inst->VRegC_22s());
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::AND_INT_LIT16:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) &
-                             inst->VRegC_22s());
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::OR_INT_LIT16:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) |
-                             inst->VRegC_22s());
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::XOR_INT_LIT16:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^
-                             inst->VRegC_22s());
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::ADD_INT_LIT8:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             SafeAdd(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::RSUB_INT_LIT8:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             SafeSub(inst->VRegC_22b(), shadow_frame.GetVReg(inst->VRegB_22b())));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::MUL_INT_LIT8:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             SafeMul(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::DIV_INT_LIT8: {
-        PREAMBLE();
-        bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data),
-                                   shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
-      }
-      case Instruction::REM_INT_LIT8: {
-        PREAMBLE();
-        bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data),
-                                      shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
-        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-        break;
+        continue;
       }
-      case Instruction::AND_INT_LIT8:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22b()) &
-                             inst->VRegC_22b());
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::OR_INT_LIT8:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22b()) |
-                             inst->VRegC_22b());
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::XOR_INT_LIT8:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22b()) ^
-                             inst->VRegC_22b());
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::SHL_INT_LIT8:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22b()) <<
-                             (inst->VRegC_22b() & 0x1f));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::SHR_INT_LIT8:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             shadow_frame.GetVReg(inst->VRegB_22b()) >>
-                             (inst->VRegC_22b() & 0x1f));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::USHR_INT_LIT8:
-        PREAMBLE();
-        shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                             static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
-                             (inst->VRegC_22b() & 0x1f));
-        inst = inst->Next_2xx();
-        break;
-      case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
-      case Instruction::UNUSED_79 ... Instruction::UNUSED_7A:
-      case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
-        UnexpectedOpcode(inst, shadow_frame);
     }
-  } while (!interpret_one_instruction);
+    switch (inst->Opcode(inst_data)) {
+#define OPCODE_CASE(OPCODE, OPCODE_NAME, pname, f, i, a, e, v)                                    \
+      case OPCODE: {                                                                              \
+        bool exit_loop = false;                                                                   \
+        InstructionHandler<do_access_check, transaction_active> handler(                          \
+            ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop);        \
+        handler.OPCODE_NAME();                                                                    \
+        /* TODO: Advance 'inst' here, instead of explicitly in each handler */                    \
+        if (UNLIKELY(exit_loop)) {                                                                \
+          return;                                                                                 \
+        }                                                                                         \
+        break;                                                                                    \
+      }
+DEX_INSTRUCTION_LIST(OPCODE_CASE)
+#undef OPCODE_CASE
+    }
+    if (UNLIKELY(interpret_one_instruction)) {
+      break;
+    }
+  }
   // Record where we stopped.
   shadow_frame.SetDexPC(inst->GetDexPc(insns));
-  ctx->result = result_register;
+  ctx->result = ctx->result_register;
   return;
 }  // NOLINT(readability/fn_size)
 
diff --git a/runtime/interpreter/mterp/arm/other.S b/runtime/interpreter/mterp/arm/other.S
index 340038c..fcdde1e 100644
--- a/runtime/interpreter/mterp/arm/other.S
+++ b/runtime/interpreter/mterp/arm/other.S
@@ -159,6 +159,9 @@
     cmp      r0, #0
     bne      MterpException
     FETCH_ADVANCE_INST 1
+    ldr      r0, [rSELF, #THREAD_USE_MTERP_OFFSET]
+    cmp      r0, #0
+    beq      MterpFallback
     GET_INST_OPCODE ip                   @ extract opcode from rINST
     GOTO_OPCODE ip                       @ jump to next instruction
 
@@ -179,6 +182,9 @@
     cmp     r0, #0                      @ failed?
     bne     MterpException
     FETCH_ADVANCE_INST 1                @ before throw: advance rPC, load rINST
+    ldr      r0, [rSELF, #THREAD_USE_MTERP_OFFSET]
+    cmp      r0, #0
+    beq      MterpFallback
     GET_INST_OPCODE ip                  @ extract opcode from rINST
     GOTO_OPCODE ip                      @ jump to next instruction
 
diff --git a/runtime/interpreter/mterp/arm64/other.S b/runtime/interpreter/mterp/arm64/other.S
index 024a5c8..f1d0ef3 100644
--- a/runtime/interpreter/mterp/arm64/other.S
+++ b/runtime/interpreter/mterp/arm64/other.S
@@ -146,6 +146,8 @@
     bl       artLockObjectFromCode
     cbnz     w0, MterpException
     FETCH_ADVANCE_INST 1
+    ldr      w0, [xSELF, #THREAD_USE_MTERP_OFFSET]
+    cbz      w0, MterpFallback
     GET_INST_OPCODE ip                   // extract opcode from rINST
     GOTO_OPCODE ip                       // jump to next instruction
 
@@ -165,6 +167,8 @@
     bl       artUnlockObjectFromCode    // w0<- success for unlock(self, obj)
     cbnz     w0, MterpException
     FETCH_ADVANCE_INST 1                // before throw: advance rPC, load rINST
+    ldr      w0, [xSELF, #THREAD_USE_MTERP_OFFSET]
+    cbz      w0, MterpFallback
     GET_INST_OPCODE ip                  // extract opcode from rINST
     GOTO_OPCODE ip                      // jump to next instruction
 
diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S
index cfb9c7c..06cd904 100644
--- a/runtime/interpreter/mterp/x86/invoke.S
+++ b/runtime/interpreter/mterp/x86/invoke.S
@@ -18,8 +18,7 @@
     jz      MterpException
     ADVANCE_PC 3
     movl    rSELF, %eax
-    movb    THREAD_USE_MTERP_OFFSET(%eax), %al
-    testb   %al, %al
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
     jz      MterpFallback
     RESTORE_IBASE
     FETCH_INST
@@ -45,8 +44,7 @@
     jz      MterpException
     ADVANCE_PC 4
     movl    rSELF, %eax
-    movb    THREAD_USE_MTERP_OFFSET(%eax), %al
-    testb   %al, %al
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
     jz      MterpFallback
     RESTORE_IBASE
     FETCH_INST
diff --git a/runtime/interpreter/mterp/x86/main.S b/runtime/interpreter/mterp/x86/main.S
index b233f2c..0621fb4 100644
--- a/runtime/interpreter/mterp/x86/main.S
+++ b/runtime/interpreter/mterp/x86/main.S
@@ -91,6 +91,8 @@
 #include "asm_support.h"
 #include "interpreter/cfi_asm_support.h"
 
+#define LITERAL(value) $$(value)
+
 /*
  * Handle mac compiler specific
  */
@@ -561,8 +563,7 @@
     movl    rPC, OFF_FP_DEX_PC_PTR(rFP)
     /* Do we need to switch interpreters? */
     movl    rSELF, %eax
-    movb    THREAD_USE_MTERP_OFFSET(%eax), %al
-    testb   %al, %al
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
     jz      MterpFallback
     /* resume execution at catch block */
     REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/other.S b/runtime/interpreter/mterp/x86/other.S
index 5de3381..270ccb6 100644
--- a/runtime/interpreter/mterp/x86/other.S
+++ b/runtime/interpreter/mterp/x86/other.S
@@ -132,7 +132,12 @@
     RESTORE_IBASE
     testb   %al, %al
     jnz     MterpException
-    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+    ADVANCE_PC 1
+    movl    rSELF, %eax
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
+    jz      MterpFallback
+    FETCH_INST
+    GOTO_NEXT
 
 %def op_monitor_exit():
 /*
@@ -152,7 +157,12 @@
     RESTORE_IBASE
     testb   %al, %al
     jnz     MterpException
-    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+    ADVANCE_PC 1
+    movl    rSELF, %eax
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
+    jz      MterpFallback
+    FETCH_INST
+    GOTO_NEXT
 
 %def op_move(is_object="0"):
     /* for move, move-object, long-to-int */
diff --git a/runtime/interpreter/mterp/x86_64/invoke.S b/runtime/interpreter/mterp/x86_64/invoke.S
index f727915..15b48c9 100644
--- a/runtime/interpreter/mterp/x86_64/invoke.S
+++ b/runtime/interpreter/mterp/x86_64/invoke.S
@@ -16,8 +16,7 @@
     jz      MterpException
     ADVANCE_PC 3
     movq    rSELF, %rax
-    movb    THREAD_USE_MTERP_OFFSET(%rax), %al
-    testb   %al, %al
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
     jz      MterpFallback
     FETCH_INST
     GOTO_NEXT
@@ -40,8 +39,7 @@
     jz      MterpException
     ADVANCE_PC 4
     movq    rSELF, %rax
-    movb    THREAD_USE_MTERP_OFFSET(%rax), %al
-    testb   %al, %al
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
     jz      MterpFallback
     FETCH_INST
     GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86_64/main.S b/runtime/interpreter/mterp/x86_64/main.S
index 75eb00c..4609067 100644
--- a/runtime/interpreter/mterp/x86_64/main.S
+++ b/runtime/interpreter/mterp/x86_64/main.S
@@ -87,6 +87,8 @@
 #include "asm_support.h"
 #include "interpreter/cfi_asm_support.h"
 
+#define LITERAL(value) $$(value)
+
 /*
  * Handle mac compiler specific
  */
@@ -527,8 +529,7 @@
     movq    rPC, OFF_FP_DEX_PC_PTR(rFP)
     /* Do we need to switch interpreters? */
     movq    rSELF, %rax
-    movb    THREAD_USE_MTERP_OFFSET(%rax), %al
-    testb   %al, %al
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
     jz      MterpFallback
     /* resume execution at catch block */
     REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86_64/other.S b/runtime/interpreter/mterp/x86_64/other.S
index 849155c..412389f 100644
--- a/runtime/interpreter/mterp/x86_64/other.S
+++ b/runtime/interpreter/mterp/x86_64/other.S
@@ -108,7 +108,12 @@
     call    SYMBOL(artLockObjectFromCode)   # (object, self)
     testq   %rax, %rax
     jnz     MterpException
-    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+    ADVANCE_PC 1
+    movq    rSELF, %rax
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
+    jz      MterpFallback
+    FETCH_INST
+    GOTO_NEXT
 
 %def op_monitor_exit():
 /*
@@ -125,7 +130,12 @@
     call    SYMBOL(artUnlockObjectFromCode) # (object, self)
     testq   %rax, %rax
     jnz     MterpException
-    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+    ADVANCE_PC 1
+    movq    rSELF, %rax
+    cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
+    jz      MterpFallback
+    FETCH_INST
+    GOTO_NEXT
 
 %def op_move(is_object="0"):
     /* for move, move-object, long-to-int */
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 9bc2179..e292a76 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -182,15 +182,16 @@
 }
 
 template<typename T>
-static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame)
+static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   // All uses in this file are from reflection
-  constexpr hiddenapi::AccessMethod access_method = hiddenapi::kReflection;
-  return hiddenapi::GetMemberAction(
+  constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kReflection;
+  return hiddenapi::ShouldDenyAccessToMember(
       member,
-      frame->GetMethod()->GetDeclaringClass()->GetClassLoader(),
-      frame->GetMethod()->GetDeclaringClass()->GetDexCache(),
-      access_method) == hiddenapi::kDeny;
+      [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+        return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
+      },
+      access_method);
 }
 
 void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
@@ -297,7 +298,7 @@
   auto* cl = Runtime::Current()->GetClassLinker();
   if (cl->EnsureInitialized(self, h_klass, true, true)) {
     ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize());
-    if (cons != nullptr && ShouldBlockAccessToMember(cons, shadow_frame)) {
+    if (cons != nullptr && ShouldDenyAccessToMember(cons, shadow_frame)) {
       cons = nullptr;
     }
     if (cons != nullptr) {
@@ -342,7 +343,7 @@
       }
     }
   }
-  if (found != nullptr && ShouldBlockAccessToMember(found, shadow_frame)) {
+  if (found != nullptr && ShouldDenyAccessToMember(found, shadow_frame)) {
     found = nullptr;
   }
   if (found == nullptr) {
@@ -407,7 +408,7 @@
           self, klass, name, args);
     }
   }
-  if (method != nullptr && ShouldBlockAccessToMember(method->GetArtMethod(), shadow_frame)) {
+  if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) {
     method = nullptr;
   }
   result->SetL(method);
@@ -445,7 +446,7 @@
     }
   }
   if (constructor != nullptr &&
-      ShouldBlockAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
+      ShouldDenyAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
     constructor = nullptr;
   }
   result->SetL(constructor);
diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h
index 2f91f5d..093dd7f 100644
--- a/runtime/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -219,6 +219,7 @@
   V(VarHandleLoadLoadFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "loadLoadFence", "()V") \
   V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \
   V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \
+  V(CRC32Update, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "update", "(II)I") \
   SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V)
 
 #endif  // ART_RUNTIME_INTRINSICS_LIST_H_
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index e876a1b..d67d9dc 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -60,7 +60,6 @@
 void (*Jit::jit_unload_)(void*) = nullptr;
 bool (*Jit::jit_compile_method_)(void*, ArtMethod*, Thread*, bool) = nullptr;
 void (*Jit::jit_types_loaded_)(void*, mirror::Class**, size_t count) = nullptr;
-bool Jit::generate_debug_info_ = false;
 
 struct StressModeHelper {
   DECLARE_RUNTIME_DEBUG_FLAG(kSlowMode);
@@ -176,11 +175,24 @@
       lock_("JIT memory use lock") {}
 
 Jit* Jit::Create(JitCodeCache* code_cache, JitOptions* options) {
-  CHECK(jit_compiler_handle_ != nullptr) << "Jit::LoadLibrary() needs to be called first";
-  std::unique_ptr<Jit> jit(new Jit(code_cache, options));
-  if (jit_compiler_handle_ == nullptr) {
+  if (jit_load_ == nullptr) {
+    LOG(WARNING) << "Not creating JIT: library not loaded";
     return nullptr;
   }
+  bool will_generate_debug_symbols = false;
+  jit_compiler_handle_ = (jit_load_)(&will_generate_debug_symbols);
+  if (jit_compiler_handle_ == nullptr) {
+    LOG(WARNING) << "Not creating JIT: failed to allocate a compiler";
+    return nullptr;
+  }
+  std::unique_ptr<Jit> jit(new Jit(code_cache, options));
+  jit->generate_debug_info_ = will_generate_debug_symbols;
+
+  // With 'perf', we want a 1-1 mapping between an address and a method.
+  // We aren't able to keep method pointers live during the instrumentation method entry trampoline
+  // so we will just disable jit-gc if we are doing that.
+  code_cache->SetGarbageCollectCode(!jit->generate_debug_info_ &&
+      !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled());
 
   VLOG(jit) << "JIT created with initial_capacity="
       << PrettySize(options->GetCodeCacheInitialCapacity())
@@ -195,7 +207,7 @@
   return jit.release();
 }
 
-bool Jit::BindCompilerMethods(std::string* error_msg) {
+bool Jit::LoadCompilerLibrary(std::string* error_msg) {
   jit_library_handle_ = dlopen(
       kIsDebugBuild ? "libartd-compiler.so" : "libart-compiler.so", RTLD_NOW);
   if (jit_library_handle_ == nullptr) {
@@ -234,23 +246,6 @@
   return true;
 }
 
-bool Jit::LoadCompiler(std::string* error_msg) {
-  if (jit_library_handle_ == nullptr && !BindCompilerMethods(error_msg)) {
-    return false;
-  }
-  bool will_generate_debug_symbols = false;
-  VLOG(jit) << "Calling JitLoad interpreter_only="
-      << Runtime::Current()->GetInstrumentation()->InterpretOnly();
-  jit_compiler_handle_ = (jit_load_)(&will_generate_debug_symbols);
-  if (jit_compiler_handle_ == nullptr) {
-    dlclose(jit_library_handle_);
-    *error_msg = "JIT couldn't load compiler";
-    return false;
-  }
-  generate_debug_info_ = will_generate_debug_symbols;
-  return true;
-}
-
 bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) {
   DCHECK(Runtime::Current()->UseJitCompilation());
   DCHECK(!method->IsRuntimeMethod());
@@ -300,11 +295,6 @@
   return success;
 }
 
-bool Jit::ShouldGenerateDebugInfo() {
-  CHECK(CompilerIsLoaded());
-  return generate_debug_info_;
-}
-
 void Jit::CreateThreadPool() {
   // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
   // is not null when we instrument.
@@ -385,7 +375,7 @@
     return;
   }
   jit::Jit* jit = Runtime::Current()->GetJit();
-  if (generate_debug_info_) {
+  if (jit->generate_debug_info_) {
     DCHECK(jit->jit_types_loaded_ != nullptr);
     jit->jit_types_loaded_(jit->jit_compiler_handle_, &type, 1);
   }
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index b0ea19b..46b0762 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -282,13 +282,8 @@
                                         JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Load and initialize compiler.
-  static bool LoadCompiler(std::string* error_msg);
-
-  static bool CompilerIsLoaded() { return jit_compiler_handle_ != nullptr; }
-
-  // Return whether debug info should be generated. Requires LoadCompiler() to have been called.
-  static bool ShouldGenerateDebugInfo();
+  // Load the compiler library.
+  static bool LoadCompilerLibrary(std::string* error_msg);
 
   ThreadPool* GetThreadPool() const {
     return thread_pool_.get();
@@ -313,8 +308,8 @@
   static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool);
   static void (*jit_types_loaded_)(void*, mirror::Class**, size_t count);
 
-  // We make this static to simplify the interaction with libart-compiler.so.
-  static bool generate_debug_info_;
+  // Whether we should generate debug info when compiling.
+  bool generate_debug_info_;
 
   // JIT resources owned by runtime.
   jit::JitCodeCache* const code_cache_;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index a15a9be..359f97e 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -436,12 +436,6 @@
     SetFootprintLimit(current_capacity_);
   }
 
-  // With 'perf', we want a 1-1 mapping between an address and a method.
-  // We aren't able to keep method pointers live during the instrumentation method entry trampoline
-  // so we will just disable jit-gc if we are doing that.
-  garbage_collect_code_ = !Jit::ShouldGenerateDebugInfo() &&
-      !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled();
-
   VLOG(jit) << "Created jit code cache: initial data size="
             << PrettySize(initial_data_capacity)
             << ", initial code size="
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 5e01b79..af86cc0 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -84,20 +84,20 @@
 // things not rendering correctly. E.g. b/16858794
 static constexpr bool kWarnJniAbort = false;
 
-static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
-  return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames= */ 1));
-}
-
 template<typename T>
-ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  hiddenapi::Action action = hiddenapi::GetMemberAction(
-      member, self, IsCallerTrusted, hiddenapi::kJNI);
-  if (action != hiddenapi::kAllow) {
-    hiddenapi::NotifyHiddenApiListener(member);
-  }
-
-  return action == hiddenapi::kDeny;
+  return hiddenapi::ShouldDenyAccessToMember(
+      member,
+      [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+        // Construct AccessContext from the first calling class on stack.
+        // If the calling class cannot be determined, e.g. unattached threads,
+        // we conservatively assume the caller is trusted.
+        ObjPtr<mirror::Class> caller = GetCallingClass(self, /* num_frames */ 1);
+        return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true)
+                               : hiddenapi::AccessContext(caller);
+      },
+      hiddenapi::AccessMethod::kJNI);
 }
 
 // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
@@ -259,7 +259,7 @@
   } else {
     method = c->FindClassMethod(name, sig, pointer_size);
   }
-  if (method != nullptr && ShouldBlockAccessToMember(method, soa.Self())) {
+  if (method != nullptr && ShouldDenyAccessToMember(method, soa.Self())) {
     method = nullptr;
   }
   if (method == nullptr || method->IsStatic() != is_static) {
@@ -338,7 +338,7 @@
   } else {
     field = c->FindInstanceField(name, field_type->GetDescriptor(&temp));
   }
-  if (field != nullptr && ShouldBlockAccessToMember(field, soa.Self())) {
+  if (field != nullptr && ShouldDenyAccessToMember(field, soa.Self())) {
     field = nullptr;
   }
   if (field == nullptr) {
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 4427332..a6a5ba2 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -224,16 +224,14 @@
   }
 }
 
-template<typename T, VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<typename T, VerifyObjectFlags kVerifyFlags>
 inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
   // C style casts here since we sometimes have T be a pointer, or sometimes an integer
   // (for stack traces).
   if (ptr_size == PointerSize::k64) {
-    return (T)static_cast<uintptr_t>(
-        AsLongArray<kVerifyFlags, kReadBarrierOption>()->GetWithoutChecks(idx));
+    return (T)static_cast<uintptr_t>(AsLongArray<kVerifyFlags>()->GetWithoutChecks(idx));
   }
-  return (T)static_cast<uintptr_t>(static_cast<uint32_t>(
-      AsIntArray<kVerifyFlags, kReadBarrierOption>()->GetWithoutChecks(idx)));
+  return (T)static_cast<uintptr_t>(AsIntArray<kVerifyFlags>()->GetWithoutChecks(idx));
 }
 
 template<bool kTransactionActive, bool kUnchecked>
@@ -255,12 +253,12 @@
                                                     ptr_size);
 }
 
-template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption, typename Visitor>
+template <VerifyObjectFlags kVerifyFlags, typename Visitor>
 inline void PointerArray::Fixup(mirror::PointerArray* dest,
                                 PointerSize pointer_size,
                                 const Visitor& visitor) {
   for (size_t i = 0, count = GetLength(); i < count; ++i) {
-    void* ptr = GetElementPtrSize<void*, kVerifyFlags, kReadBarrierOption>(i, pointer_size);
+    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);
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 7211f30..8816c61 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -193,9 +193,7 @@
 // Either an IntArray or a LongArray.
 class PointerArray : public Array {
  public:
-  template<typename T,
-           VerifyObjectFlags kVerifyFlags = kVerifyNone,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<typename T, VerifyObjectFlags kVerifyFlags = kVerifyNone>
   T GetElementPtrSize(uint32_t idx, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -216,9 +214,7 @@
 
   // Fixup the pointers in the dest arrays by passing our pointers through the visitor. Only copies
   // to dest if visitor(source_ptr) != source_ptr.
-  template <VerifyObjectFlags kVerifyFlags = kVerifyNone,
-            ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
-            typename Visitor>
+  template <VerifyObjectFlags kVerifyFlags = kVerifyNone, typename Visitor>
   void Fixup(mirror::PointerArray* dest, PointerSize pointer_size, const Visitor& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 9a4130d..185ae3b 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -294,16 +294,20 @@
 }
 
 inline bool Class::HasVTable() {
-  return GetVTable() != nullptr || ShouldHaveEmbeddedVTable();
+  // No read barrier is needed for comparing with null.
+  return GetVTable<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr ||
+         ShouldHaveEmbeddedVTable();
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline int32_t Class::GetVTableLength() {
   if (ShouldHaveEmbeddedVTable<kVerifyFlags>()) {
     return GetEmbeddedVTableLength();
   }
-  return GetVTable<kVerifyFlags, kReadBarrierOption>() != nullptr ?
-      GetVTable<kVerifyFlags, kReadBarrierOption>()->GetLength() : 0;
+  // We do not need a read barrier here as the length is constant,
+  // both from-space and to-space vtables shall yield the same result.
+  ObjPtr<PointerArray> vtable = GetVTable<kVerifyFlags, kWithoutReadBarrier>();
+  return vtable != nullptr ? vtable->GetLength() : 0;
 }
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
@@ -311,10 +315,9 @@
   if (ShouldHaveEmbeddedVTable<kVerifyFlags>()) {
     return GetEmbeddedVTableEntry(i, pointer_size);
   }
-  auto* vtable = GetVTable<kVerifyFlags, kReadBarrierOption>();
+  ObjPtr<PointerArray> vtable = GetVTable<kVerifyFlags, kReadBarrierOption>();
   DCHECK(vtable != nullptr);
-  return vtable->template GetElementPtrSize<ArtMethod*, kVerifyFlags, kReadBarrierOption>(
-      i, pointer_size);
+  return vtable->GetElementPtrSize<ArtMethod*, kVerifyFlags>(i, pointer_size);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
@@ -624,9 +627,11 @@
   return ret.Ptr();
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline int32_t Class::GetIfTableCount() {
-  return GetIfTable<kVerifyFlags, kReadBarrierOption>()->Count();
+  // We do not need a read barrier here as the length is constant,
+  // both from-space and to-space iftables shall yield the same result.
+  return GetIfTable<kVerifyFlags, kWithoutReadBarrier>()->Count();
 }
 
 inline void Class::SetIfTable(ObjPtr<IfTable> new_iftable) {
@@ -970,7 +975,17 @@
   // We do not need a read barrier here as the primitive type is constant,
   // both from-space and to-space component type classes shall yield the same result.
   ObjPtr<Class> const component_type = GetComponentType<kVerifyFlags, kWithoutReadBarrier>();
-  return component_type != nullptr && !component_type->IsPrimitive<kVerifyFlags>();
+  constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
+  return component_type != nullptr && !component_type->IsPrimitive<kNewFlags>();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+bool Class::IsPrimitiveArray() {
+  // We do not need a read barrier here as the primitive type is constant,
+  // both from-space and to-space component type classes shall yield the same result.
+  ObjPtr<Class> const component_type = GetComponentType<kVerifyFlags, kWithoutReadBarrier>();
+  constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
+  return component_type != nullptr && component_type->IsPrimitive<kNewFlags>();
 }
 
 inline bool Class::IsAssignableFrom(ObjPtr<Class> src) {
@@ -1087,6 +1102,16 @@
   return component->IsPrimitive() || component->CannotBeAssignedFromOtherTypes();
 }
 
+template <bool kCheckTransaction>
+inline void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
+  if (kCheckTransaction && Runtime::Current()->IsActiveTransaction()) {
+    SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
+  } else {
+    DCHECK(!Runtime::Current()->IsActiveTransaction());
+    SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
+  }
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index e33e407..83d76a9 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -427,14 +427,6 @@
   return GetClassRoot<mirror::Throwable>()->IsAssignableFrom(this);
 }
 
-void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
-  if (Runtime::Current()->IsActiveTransaction()) {
-    SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
-  } else {
-    SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
-  }
-}
-
 template <typename SignatureType>
 static inline ArtMethod* FindInterfaceMethodWithSignature(ObjPtr<Class> klass,
                                                           const StringPiece& name,
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 74fca54..c38cc86 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -422,13 +422,6 @@
     return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimVoid;
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  bool IsPrimitiveArray() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return IsArrayClass<kVerifyFlags>() &&
-        GetComponentType<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>()->
-        IsPrimitive();
-  }
-
   // Depth of class from java.lang.Object
   uint32_t Depth() REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -466,7 +459,8 @@
   }
 
   bool IsObjectClass() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return !IsPrimitive() && GetSuperClass() == nullptr;
+    // No read barrier is needed for comparing with null.
+    return !IsPrimitive() && GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr;
   }
 
   bool IsInstantiableNonArray() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -485,18 +479,7 @@
   ALWAYS_INLINE bool IsObjectArrayClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  bool IsIntArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) {
-    constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
-    auto* component_type = GetComponentType<kVerifyFlags>();
-    return component_type != nullptr && component_type->template IsPrimitiveInt<kNewFlags>();
-  }
-
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  bool IsLongArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) {
-    constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
-    auto* component_type = GetComponentType<kVerifyFlags>();
-    return component_type != nullptr && component_type->template IsPrimitiveLong<kNewFlags>();
-  }
+  bool IsPrimitiveArray() REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Creates a raw object instance but does not invoke the default constructor.
   template<bool kIsInstrumented, bool kCheckAddFinalizer = true>
@@ -633,7 +616,8 @@
   void SetSuperClass(ObjPtr<Class> new_super_class) REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool HasSuperClass() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetSuperClass() != nullptr;
+    // No read barrier is needed for comparing with null.
+    return GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr;
   }
 
   static constexpr MemberOffset SuperClassOffset() {
@@ -644,6 +628,7 @@
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   ClassLoader* GetClassLoader() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template <bool kCheckTransaction = true>
   void SetClassLoader(ObjPtr<ClassLoader> new_cl) REQUIRES_SHARED(Locks::mutator_lock_);
 
   static constexpr MemberOffset DexCacheOffset() {
@@ -812,8 +797,7 @@
 
   static MemberOffset EmbeddedVTableEntryOffset(uint32_t i, PointerSize pointer_size);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   int32_t GetVTableLength() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -948,8 +932,7 @@
     return (GetAccessFlags() & kAccRecursivelyInitialized) != 0;
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE int32_t GetIfTableCount() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -1216,7 +1199,8 @@
 
   // Returns true if the class loader is null, ie the class loader is the boot strap class loader.
   bool IsBootStrapClassLoaded() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetClassLoader() == nullptr;
+    // No read barrier is needed for comparing with null.
+    return GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr;
   }
 
   static size_t ImTableEntrySize(PointerSize pointer_size) {
diff --git a/runtime/mirror/class_ext-inl.h b/runtime/mirror/class_ext-inl.h
index feaac85..8d68dc9 100644
--- a/runtime/mirror/class_ext-inl.h
+++ b/runtime/mirror/class_ext-inl.h
@@ -32,9 +32,7 @@
   }
   int32_t len = arr->GetLength();
   for (int32_t i = 0; i < len; i++) {
-    ArtMethod* method = arr->GetElementPtrSize<ArtMethod*,
-                                               kDefaultVerifyFlags,
-                                               kReadBarrierOption>(i, pointer_size);
+    ArtMethod* method = arr->GetElementPtrSize<ArtMethod*, kDefaultVerifyFlags>(i, pointer_size);
     if (method != nullptr) {
       method->VisitRoots<kReadBarrierOption>(visitor, pointer_size);
     }
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 8ae79a8..2c2ad9b 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -156,7 +156,7 @@
 inline bool Object::IsObjectArray() {
   // We do not need a read barrier here as the primitive type is constant,
   // both from-space and to-space component type classes shall yield the same result.
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
+  constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
   return IsArrayInstance<kVerifyFlags>() &&
       !GetClass<kNewFlags, kWithoutReadBarrier>()->
           template GetComponentType<kNewFlags, kWithoutReadBarrier>()->IsPrimitive();
@@ -192,113 +192,102 @@
   return down_cast<Array*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags, Primitive::Type kType>
+ALWAYS_INLINE bool Object::IsSpecificPrimitiveArray() {
+  // We do not need a read barrier here as the primitive type is constant,
+  // both from-space and to-space component type classes shall yield the same result.
+  ObjPtr<Class> klass = GetClass<kVerifyFlags, kWithoutReadBarrier>();
+  constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
+  ObjPtr<Class> const component_type = klass->GetComponentType<kNewFlags, kWithoutReadBarrier>();
+  return component_type != nullptr &&
+         component_type->GetPrimitiveType<kNewFlags>() == kType;
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsBooleanArray() {
+  return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimBoolean>();
+}
+
 template<VerifyObjectFlags kVerifyFlags>
 inline BooleanArray* Object::AsBooleanArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
-  DCHECK(GetClass<kNewFlags>()->GetComponentType()->IsPrimitiveBoolean());
+  DCHECK(IsBooleanArray<kVerifyFlags>());
   return down_cast<BooleanArray*>(this);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsByteArray() {
+  return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimByte>();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
 inline ByteArray* Object::AsByteArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
-  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveByte());
+  DCHECK(IsByteArray<kVerifyFlags>());
   return down_cast<ByteArray*>(this);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
-inline ByteArray* Object::AsByteSizedArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
-  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveByte() ||
-         GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveBoolean());
-  return down_cast<ByteArray*>(this);
+inline bool Object::IsCharArray() {
+  return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimChar>();
 }
 
 template<VerifyObjectFlags kVerifyFlags>
 inline CharArray* Object::AsCharArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
-  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveChar());
+  DCHECK(IsCharArray<kVerifyFlags>());
   return down_cast<CharArray*>(this);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsShortArray() {
+  return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimShort>();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
 inline ShortArray* Object::AsShortArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
-  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveShort());
+  DCHECK(IsShortArray<kVerifyFlags>());
   return down_cast<ShortArray*>(this);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
-inline ShortArray* Object::AsShortSizedArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
-  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveShort() ||
-         GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveChar());
-  return down_cast<ShortArray*>(this);
-}
-
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
 inline bool Object::IsIntArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  ObjPtr<Class> klass = GetClass<kVerifyFlags, kReadBarrierOption>();
-  ObjPtr<Class> component_type = klass->GetComponentType<kVerifyFlags, kReadBarrierOption>();
-  return component_type != nullptr && component_type->template IsPrimitiveInt<kNewFlags>();
+  return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimInt>();
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline IntArray* Object::AsIntArray() {
-  DCHECK((IsIntArray<kVerifyFlags, kReadBarrierOption>()));
+  DCHECK((IsIntArray<kVerifyFlags>()));
   return down_cast<IntArray*>(this);
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsLongArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  ObjPtr<Class> klass = GetClass<kVerifyFlags, kReadBarrierOption>();
-  ObjPtr<Class> component_type = klass->GetComponentType<kVerifyFlags, kReadBarrierOption>();
-  return component_type != nullptr && component_type->template IsPrimitiveLong<kNewFlags>();
+  return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimLong>();
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline LongArray* Object::AsLongArray() {
-  DCHECK((IsLongArray<kVerifyFlags, kReadBarrierOption>()));
+  DCHECK((IsLongArray<kVerifyFlags>()));
   return down_cast<LongArray*>(this);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsFloatArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  auto* component_type = GetClass<kVerifyFlags>()->GetComponentType();
-  return component_type != nullptr && component_type->template IsPrimitiveFloat<kNewFlags>();
+  return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimFloat>();
 }
 
 template<VerifyObjectFlags kVerifyFlags>
 inline FloatArray* Object::AsFloatArray() {
   DCHECK(IsFloatArray<kVerifyFlags>());
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
-  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveFloat());
   return down_cast<FloatArray*>(this);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsDoubleArray() {
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  auto* component_type = GetClass<kVerifyFlags>()->GetComponentType();
-  return component_type != nullptr && component_type->template IsPrimitiveDouble<kNewFlags>();
+  return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimDouble>();
 }
 
 template<VerifyObjectFlags kVerifyFlags>
 inline DoubleArray* Object::AsDoubleArray() {
   DCHECK(IsDoubleArray<kVerifyFlags>());
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
-  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveDouble());
   return down_cast<DoubleArray*>(this);
 }
 
@@ -351,7 +340,7 @@
   // values is OK because of that.
   static constexpr ReadBarrierOption kRBO = kWithoutReadBarrier;
   size_t result;
-  constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
+  constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
   if (IsArrayInstance<kVerifyFlags>()) {
     result = AsArray<kNewFlags>()->template SizeOf<kNewFlags, kRBO>();
   } else if (IsClass<kNewFlags>()) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 11e8cca..bca7511 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -21,6 +21,7 @@
 #include "base/casts.h"
 #include "base/enums.h"
 #include "base/globals.h"
+#include "dex/primitive.h"
 #include "obj_ptr.h"
 #include "object_reference.h"
 #include "offsets.h"
@@ -199,31 +200,33 @@
   Array* AsArray() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsBooleanArray() REQUIRES_SHARED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   BooleanArray* AsBooleanArray() REQUIRES_SHARED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsByteArray() REQUIRES_SHARED(Locks::mutator_lock_);
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ByteArray* AsByteArray() REQUIRES_SHARED(Locks::mutator_lock_);
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  ByteArray* AsByteSizedArray() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsCharArray() REQUIRES_SHARED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   CharArray* AsCharArray() REQUIRES_SHARED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsShortArray() REQUIRES_SHARED(Locks::mutator_lock_);
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ShortArray* AsShortArray() REQUIRES_SHARED(Locks::mutator_lock_);
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  ShortArray* AsShortSizedArray() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   IntArray* AsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   LongArray* AsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -757,6 +760,9 @@
                             size_t num_bytes)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags, Primitive::Type kType>
+  bool IsSpecificPrimitiveArray() REQUIRES_SHARED(Locks::mutator_lock_);
+
   static Atomic<uint32_t> hash_code_seed;
 
   // The Class representing the type of the object.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 0f0a378..df2a8e2 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -97,6 +97,7 @@
       lock_count_(0),
       obj_(GcRoot<mirror::Object>(obj)),
       wait_set_(nullptr),
+      wake_set_(nullptr),
       hash_code_(hash_code),
       locking_method_(nullptr),
       locking_dex_pc_(0),
@@ -120,6 +121,7 @@
       lock_count_(0),
       obj_(GcRoot<mirror::Object>(obj)),
       wait_set_(nullptr),
+      wake_set_(nullptr),
       hash_code_(hash_code),
       locking_method_(nullptr),
       locking_dex_pc_(0),
@@ -226,7 +228,8 @@
 }
 
 void Monitor::AppendToWaitSet(Thread* thread) {
-  DCHECK(owner_ == Thread::Current());
+  // Not checking that the owner is equal to this thread, since we've released
+  // the monitor by the time this method is called.
   DCHECK(thread != nullptr);
   DCHECK(thread->GetWaitNext() == nullptr) << thread->GetWaitNext();
   if (wait_set_ == nullptr) {
@@ -245,24 +248,29 @@
 void Monitor::RemoveFromWaitSet(Thread *thread) {
   DCHECK(owner_ == Thread::Current());
   DCHECK(thread != nullptr);
-  if (wait_set_ == nullptr) {
-    return;
-  }
-  if (wait_set_ == thread) {
-    wait_set_ = thread->GetWaitNext();
-    thread->SetWaitNext(nullptr);
-    return;
-  }
-
-  Thread* t = wait_set_;
-  while (t->GetWaitNext() != nullptr) {
-    if (t->GetWaitNext() == thread) {
-      t->SetWaitNext(thread->GetWaitNext());
-      thread->SetWaitNext(nullptr);
-      return;
+  auto remove = [&](Thread*& set){
+    if (set != nullptr) {
+      if (set == thread) {
+        set = thread->GetWaitNext();
+        thread->SetWaitNext(nullptr);
+        return true;
+      }
+      Thread* t = set;
+      while (t->GetWaitNext() != nullptr) {
+        if (t->GetWaitNext() == thread) {
+          t->SetWaitNext(thread->GetWaitNext());
+          thread->SetWaitNext(nullptr);
+          return true;
+        }
+        t = t->GetWaitNext();
+      }
     }
-    t = t->GetWaitNext();
+    return false;
+  };
+  if (remove(wait_set_)) {
+    return;
   }
+  remove(wake_set_);
 }
 
 void Monitor::SetObject(mirror::Object* object) {
@@ -699,33 +707,81 @@
 bool Monitor::Unlock(Thread* self) {
   DCHECK(self != nullptr);
   uint32_t owner_thread_id = 0u;
-  {
-    MutexLock mu(self, monitor_lock_);
-    Thread* owner = owner_;
-    if (owner != nullptr) {
-      owner_thread_id = owner->GetThreadId();
-    }
-    if (owner == self) {
-      // We own the monitor, so nobody else can be in here.
-      AtraceMonitorUnlock();
-      if (lock_count_ == 0) {
-        owner_ = nullptr;
-        locking_method_ = nullptr;
-        locking_dex_pc_ = 0;
-        // Wake a contender.
-        monitor_contenders_.Signal(self);
-      } else {
-        --lock_count_;
-      }
+  DCHECK(!monitor_lock_.IsExclusiveHeld(self));
+  monitor_lock_.Lock(self);
+  Thread* owner = owner_;
+  if (owner != nullptr) {
+    owner_thread_id = owner->GetThreadId();
+  }
+  if (owner == self) {
+    // We own the monitor, so nobody else can be in here.
+    AtraceMonitorUnlock();
+    if (lock_count_ == 0) {
+      owner_ = nullptr;
+      locking_method_ = nullptr;
+      locking_dex_pc_ = 0;
+      SignalContendersAndReleaseMonitorLock(self);
+      return true;
+    } else {
+      --lock_count_;
+      monitor_lock_.Unlock(self);
       return true;
     }
   }
   // We don't own this, so we're not allowed to unlock it.
   // The JNI spec says that we should throw IllegalMonitorStateException in this case.
   FailedUnlock(GetObject(), self->GetThreadId(), owner_thread_id, this);
+  monitor_lock_.Unlock(self);
   return false;
 }
 
+void Monitor::SignalContendersAndReleaseMonitorLock(Thread* self) {
+  // We want to signal one thread to wake up, to acquire the monitor that
+  // we are releasing. This could either be a Thread waiting on its own
+  // ConditionVariable, or a thread waiting on monitor_contenders_.
+  while (wake_set_ != nullptr) {
+    // No risk of waking ourselves here; since monitor_lock_ is not released until we're ready to
+    // return, notify can't move the current thread from wait_set_ to wake_set_ until this
+    // method is done checking wake_set_.
+    Thread* thread = wake_set_;
+    wake_set_ = thread->GetWaitNext();
+    thread->SetWaitNext(nullptr);
+
+    // Check to see if the thread is still waiting.
+    {
+      // In the case of wait(), we'll be acquiring another thread's GetWaitMutex with
+      // self's GetWaitMutex held. This does not risk deadlock, because we only acquire this lock
+      // for threads in the wake_set_. A thread can only enter wake_set_ from Notify or NotifyAll,
+      // and those hold monitor_lock_. Thus, the threads whose wait mutexes we acquire here must
+      // have already been released from wait(), since we have not released monitor_lock_ until
+      // after we've chosen our thread to wake, so there is no risk of the following lock ordering
+      // leading to deadlock:
+      // Thread 1 waits
+      // Thread 2 waits
+      // Thread 3 moves threads 1 and 2 from wait_set_ to wake_set_
+      // Thread 1 enters this block, and attempts to acquire Thread 2's GetWaitMutex to wake it
+      // Thread 2 enters this block, and attempts to acquire Thread 1's GetWaitMutex to wake it
+      //
+      // Since monitor_lock_ is not released until the thread-to-be-woken-up's GetWaitMutex is
+      // acquired, two threads cannot attempt to acquire each other's GetWaitMutex while holding
+      // their own and cause deadlock.
+      MutexLock wait_mu(self, *thread->GetWaitMutex());
+      if (thread->GetWaitMonitor() != nullptr) {
+        // Release the lock, so that a potentially awakened thread will not
+        // immediately contend on it. The lock ordering here is:
+        // monitor_lock_, self->GetWaitMutex, thread->GetWaitMutex
+        monitor_lock_.Unlock(self);
+        thread->GetWaitConditionVariable()->Signal(self);
+        return;
+      }
+    }
+  }
+  // If we didn't wake any threads that were originally waiting on us,
+  // wake a contender.
+  monitor_contenders_.Signal(self);
+  monitor_lock_.Unlock(self);
+}
+
 void Monitor::Wait(Thread* self, int64_t ms, int32_t ns,
                    bool interruptShouldThrow, ThreadState why) {
   DCHECK(self != nullptr);
@@ -755,17 +811,9 @@
   }
 
   /*
-   * Add ourselves to the set of threads waiting on this monitor, and
-   * release our hold.  We need to let it go even if we're a few levels
+   * Release our hold - we need to let it go even if we're a few levels
    * deep in a recursive lock, and we need to restore that later.
-   *
-   * We append to the wait set ahead of clearing the count and owner
-   * fields so the subroutine can check that the calling thread owns
-   * the monitor.  Aside from that, the order of member updates is
-   * not order sensitive as we hold the pthread mutex.
    */
-  AppendToWaitSet(self);
-  ++num_waiters_;
   int prev_lock_count = lock_count_;
   lock_count_ = 0;
   owner_ = nullptr;
@@ -790,6 +838,17 @@
     // Pseudo-atomically wait on self's wait_cond_ and release the monitor lock.
     MutexLock mu(self, *self->GetWaitMutex());
 
+    /*
+     * Add ourselves to the set of threads waiting on this monitor.
+     * It's important that we are only added to the wait set after
+     * acquiring our GetWaitMutex, so that calls to Notify() that occur after we
+     * have released monitor_lock_ will not move us from wait_set_ to wake_set_
+     * until we've signalled contenders on this monitor.
+     */
+    AppendToWaitSet(self);
+    ++num_waiters_;
+
+
     // Set wait_monitor_ to the monitor object we will be waiting on. When wait_monitor_ is
     // non-null a notifying or interrupting thread must signal the thread's wait_cond_ to wake it
     // up.
@@ -797,8 +856,7 @@
     self->SetWaitMonitor(this);
 
     // Release the monitor lock.
-    monitor_contenders_.Signal(self);
-    monitor_lock_.Unlock(self);
+    SignalContendersAndReleaseMonitorLock(self);
 
     // Handle the case where the thread was interrupted before we called wait().
     if (self->IsInterrupted()) {
@@ -874,18 +932,12 @@
     ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()");
     return;
   }
-  // Signal the first waiting thread in the wait set.
-  while (wait_set_ != nullptr) {
-    Thread* thread = wait_set_;
-    wait_set_ = thread->GetWaitNext();
-    thread->SetWaitNext(nullptr);
-
-    // Check to see if the thread is still waiting.
-    MutexLock wait_mu(self, *thread->GetWaitMutex());
-    if (thread->GetWaitMonitor() != nullptr) {
-      thread->GetWaitConditionVariable()->Signal(self);
-      return;
-    }
+  // Move one thread from waiters to wake set
+  Thread* to_move = wait_set_;
+  if (to_move != nullptr) {
+    wait_set_ = to_move->GetWaitNext();
+    to_move->SetWaitNext(wake_set_);
+    wake_set_ = to_move;
   }
 }
 
@@ -897,12 +949,20 @@
     ThrowIllegalMonitorStateExceptionF("object not locked by thread before notifyAll()");
     return;
   }
-  // Signal all threads in the wait set.
-  while (wait_set_ != nullptr) {
-    Thread* thread = wait_set_;
-    wait_set_ = thread->GetWaitNext();
-    thread->SetWaitNext(nullptr);
-    thread->Notify();
+
+  // Move all threads from waiters to wake set
+  Thread* to_move = wait_set_;
+  if (to_move != nullptr) {
+    wait_set_ = nullptr;
+    Thread* move_to = wake_set_;
+    if (move_to == nullptr) {
+      wake_set_ = to_move;
+      return;
+    }
+    while (move_to->GetWaitNext() != nullptr) {
+      move_to = move_to->GetWaitNext();
+    }
+    move_to->SetWaitNext(to_move);
   }
 }
 
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 6b7604e..c1f84e9 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -181,6 +181,8 @@
   // this routine.
   void RemoveFromWaitSet(Thread* thread) REQUIRES(monitor_lock_);
 
+  void SignalContendersAndReleaseMonitorLock(Thread* self) RELEASE(monitor_lock_);
+
   // Changes the shape of a monitor from thin to fat, preserving the internal lock state. The
   // calling thread must own the lock or the owner must be suspended. There's a race with other
   // threads inflating the lock, installing hash codes and spurious failures. The caller should
@@ -306,6 +308,9 @@
   // Threads currently waiting on this monitor.
   Thread* wait_set_ GUARDED_BY(monitor_lock_);
 
+  // Threads that were waiting on this monitor, but are now contending on it.
+  Thread* wake_set_ GUARDED_BY(monitor_lock_);
+
   // Stored object hash code, generated lazily by GetHashCode.
   AtomicInteger hash_code_;
 
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 32b88e6..49b71cd 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -371,7 +371,7 @@
   const char* class_name = dex_file->StringByTypeIdx(type_idx);
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
   ObjPtr<mirror::Class> klass = (class_name[1] == '\0')
-      ? linker->FindPrimitiveClass(class_name[0])
+      ? linker->LookupPrimitiveClass(class_name[0])
       : linker->LookupClass(self, class_name, nullptr);
   if (klass == nullptr) {
     return;
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 4d3ad62..e4bc1b7 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -304,8 +304,7 @@
   // Our system thread ID, etc, has changed so reset Thread state.
   thread->InitAfterFork();
   runtime_flags = EnableDebugFeatures(runtime_flags);
-  hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks;
-  bool dedupe_hidden_api_warnings = true;
+  hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kDisabled;
 
   if ((runtime_flags & DISABLE_VERIFIER) != 0) {
     Runtime::Current()->DisableVerifier();
@@ -372,14 +371,14 @@
     }
   }
 
-  bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks;
+  bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled;
   DCHECK(!(is_system_server && do_hidden_api_checks))
       << "SystemServer should be forked with EnforcementPolicy::kDisable";
   DCHECK(!(is_zygote && do_hidden_api_checks))
       << "Child zygote processes should be forked with EnforcementPolicy::kDisable";
   Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy);
-  Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings);
-  if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks &&
+  Runtime::Current()->SetDedupeHiddenApiWarnings(true);
+  if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled &&
       Runtime::Current()->GetHiddenApiEventLogSampleRate() != 0) {
     // Hidden API checks are enabled, and we are sampling access for the event log. Initialize the
     // random seed, to ensure the sampling is actually random. We do this post-fork, as doing it
@@ -387,9 +386,6 @@
     std::srand(static_cast<uint32_t>(NanoTime()));
   }
 
-  // Clear the hidden API warning flag, in case it was set.
-  Runtime::Current()->SetPendingHiddenApiWarning(false);
-
   if (is_zygote) {
     // If creating a child-zygote, do not call into the runtime's post-fork logic.
     // Doing so would spin up threads for Binder and JDWP. Instead, the Java side
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index c7b8ad4..612a4b3 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -54,11 +54,12 @@
 
 namespace art {
 
-// Returns true if the first caller outside of the Class class or java.lang.invoke package
-// is in a platform DEX file.
-static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
-  // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke.
-  // This is very expensive. Save this till the last.
+// Walks the stack, finds the caller of this reflective call and returns
+// a hiddenapi AccessContext formed from its declaring class.
+static hiddenapi::AccessContext GetReflectionCaller(Thread* self)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Walk the stack and find the first frame not from java.lang.Class and not
+  // from java.lang.invoke. This is very expensive. Save this till the last.
   struct FirstExternalCallerVisitor : public StackVisitor {
     explicit FirstExternalCallerVisitor(Thread* thread)
         : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
@@ -102,50 +103,42 @@
 
   FirstExternalCallerVisitor visitor(self);
   visitor.WalkStack();
-  return visitor.caller != nullptr &&
-         hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass());
-}
 
-// Returns true if the first non-ClassClass caller up the stack is not allowed to
-// access hidden APIs. This can be *very* expensive. Never call this in a loop.
-ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
-  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self);
+  // Construct AccessContext from the calling class found on the stack.
+  // If the calling class cannot be determined, e.g. unattached threads,
+  // we conservatively assume the caller is trusted.
+  ObjPtr<mirror::Class> caller = (visitor.caller == nullptr)
+      ? nullptr : visitor.caller->GetDeclaringClass();
+  return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true)
+                         : hiddenapi::AccessContext(caller);
 }
 
 // Returns true if the first non-ClassClass caller up the stack should not be
 // allowed access to `member`.
 template<typename T>
-ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  hiddenapi::Action action = hiddenapi::GetMemberAction(
-      member, self, IsCallerTrusted, hiddenapi::kReflection);
-  if (action != hiddenapi::kAllow) {
-    hiddenapi::NotifyHiddenApiListener(member);
-  }
-
-  return action == hiddenapi::kDeny;
+  return hiddenapi::ShouldDenyAccessToMember(
+      member,
+      [&]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); },
+      hiddenapi::AccessMethod::kReflection);
 }
 
 // Returns true if a class member should be discoverable with reflection given
 // the criteria. Some reflection calls only return public members
 // (public_only == true), some members should be hidden from non-boot class path
-// callers (enforce_hidden_api == true).
+// callers (hiddenapi_context).
 template<typename T>
 ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
-                                         bool enforce_hidden_api,
+                                         hiddenapi::AccessContext access_context,
                                          T* member)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
     return false;
   }
 
-  return hiddenapi::GetMemberAction(member,
-                                    nullptr,
-                                    [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; },
-                                    hiddenapi::kNone)
-      != hiddenapi::kDeny;
+  return !hiddenapi::ShouldDenyAccessToMember(
+      member, access_context, hiddenapi::AccessMethod::kNone);
 }
 
 ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
@@ -266,15 +259,15 @@
   IterationRange<StrideIterator<ArtField>> ifields = klass->GetIFields();
   IterationRange<StrideIterator<ArtField>> sfields = klass->GetSFields();
   size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
-  bool enforce_hidden_api = ShouldEnforceHiddenApi(self);
+  hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(self);
   // Lets go subtract all the non discoverable fields.
   for (ArtField& field : ifields) {
-    if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+    if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
       --array_size;
     }
   }
   for (ArtField& field : sfields) {
-    if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+    if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
       --array_size;
     }
   }
@@ -285,7 +278,7 @@
     return nullptr;
   }
   for (ArtField& field : ifields) {
-    if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+    if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
       auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
                                                                                    &field,
                                                                                    force_resolve);
@@ -300,7 +293,7 @@
     }
   }
   for (ArtField& field : sfields) {
-    if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+    if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
       auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
                                                                                    &field,
                                                                                    force_resolve);
@@ -459,8 +452,7 @@
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Field> field = hs.NewHandle(GetPublicFieldRecursive(
       soa.Self(), DecodeClass(soa, javaThis), name_string));
-  if (field.Get() == nullptr ||
-      ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) {
+  if (field.Get() == nullptr || ShouldDenyAccessToMember(field->GetArtField(), soa.Self())) {
     return nullptr;
   }
   return soa.AddLocalReference<jobject>(field.Get());
@@ -477,7 +469,7 @@
   Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
   Handle<mirror::Field> result =
       hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get()));
-  if (result == nullptr || ShouldBlockAccessToMember(result->GetArtField(), soa.Self())) {
+  if (result == nullptr || ShouldDenyAccessToMember(result->GetArtField(), soa.Self())) {
     std::string name_str = h_string->ToModifiedUtf8();
     if (name_str == "value" && h_klass->IsStringClass()) {
       // We log the error for this specific case, as the user might just swallow the exception.
@@ -509,19 +501,19 @@
       soa.Self(),
       DecodeClass(soa, javaThis),
       soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
-  if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+  if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
     return nullptr;
   }
   return soa.AddLocalReference<jobject>(result.Get());
 }
 
 static ALWAYS_INLINE inline bool MethodMatchesConstructor(
-    ArtMethod* m, bool public_only, bool enforce_hidden_api)
+    ArtMethod* m, bool public_only, hiddenapi::AccessContext hiddenapi_context)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(m != nullptr);
   return m->IsConstructor() &&
          !m->IsStatic() &&
-         IsDiscoverable(public_only, enforce_hidden_api, m);
+         IsDiscoverable(public_only, hiddenapi_context, m);
 }
 
 static jobjectArray Class_getDeclaredConstructorsInternal(
@@ -529,12 +521,12 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<2> hs(soa.Self());
   bool public_only = (publicOnly != JNI_FALSE);
-  bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
+  hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self());
   Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
   size_t constructor_count = 0;
   // Two pass approach for speed.
   for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
-    constructor_count += MethodMatchesConstructor(&m, public_only, enforce_hidden_api) ? 1u : 0u;
+    constructor_count += MethodMatchesConstructor(&m, public_only, hiddenapi_context) ? 1u : 0u;
   }
   auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc(
       soa.Self(), GetClassRoot<mirror::ObjectArray<mirror::Constructor>>(), constructor_count));
@@ -544,7 +536,7 @@
   }
   constructor_count = 0;
   for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
-    if (MethodMatchesConstructor(&m, public_only, enforce_hidden_api)) {
+    if (MethodMatchesConstructor(&m, public_only, hiddenapi_context)) {
       DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
       DCHECK(!Runtime::Current()->IsActiveTransaction());
       ObjPtr<mirror::Constructor> constructor =
@@ -571,7 +563,7 @@
           DecodeClass(soa, javaThis),
           soa.Decode<mirror::String>(name),
           soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
-  if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+  if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
     return nullptr;
   }
   return soa.AddLocalReference<jobject>(result.Get());
@@ -582,7 +574,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<2> hs(soa.Self());
 
-  bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
+  hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self());
   bool public_only = (publicOnly != JNI_FALSE);
 
   Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis));
@@ -591,7 +583,7 @@
     uint32_t modifiers = m.GetAccessFlags();
     // Add non-constructor declared methods.
     if ((modifiers & kAccConstructor) == 0 &&
-        IsDiscoverable(public_only, enforce_hidden_api, &m)) {
+        IsDiscoverable(public_only, hiddenapi_context, &m)) {
       ++num_methods;
     }
   }
@@ -605,7 +597,7 @@
   for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
     uint32_t modifiers = m.GetAccessFlags();
     if ((modifiers & kAccConstructor) == 0 &&
-        IsDiscoverable(public_only, enforce_hidden_api, &m)) {
+        IsDiscoverable(public_only, hiddenapi_context, &m)) {
       DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
       DCHECK(!Runtime::Current()->IsActiveTransaction());
       ObjPtr<mirror::Method> method =
@@ -819,7 +811,7 @@
       soa.Self(),
       ScopedNullHandle<mirror::ObjectArray<mirror::Class>>(),
       kRuntimePointerSize);
-  if (UNLIKELY(constructor == nullptr) || ShouldBlockAccessToMember(constructor, soa.Self())) {
+  if (UNLIKELY(constructor == nullptr) || ShouldDenyAccessToMember(constructor, soa.Self())) {
     soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
                                    "%s has no zero argument constructor",
                                    klass->PrettyClass().c_str());
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 2c4184c..e4bc8ce 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -101,32 +101,36 @@
       case Primitive::kPrimBoolean:
       case Primitive::kPrimByte:
         DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 1U);
-        dstArray->AsByteSizedArray()->Memmove(dstPos, srcArray->AsByteSizedArray(), srcPos, count);
+        // Note: Treating BooleanArray as ByteArray.
+        ObjPtr<mirror::ByteArray>::DownCast(dstArray)->Memmove(
+            dstPos, ObjPtr<mirror::ByteArray>::DownCast(srcArray), srcPos, count);
         return;
       case Primitive::kPrimChar:
       case Primitive::kPrimShort:
         DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 2U);
-        dstArray->AsShortSizedArray()->Memmove(dstPos, srcArray->AsShortSizedArray(), srcPos, count);
+        // Note: Treating CharArray as ShortArray.
+        ObjPtr<mirror::ShortArray>::DownCast(dstArray)->Memmove(
+            dstPos, ObjPtr<mirror::ShortArray>::DownCast(srcArray), srcPos, count);
         return;
       case Primitive::kPrimInt:
-        DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 4U);
-        dstArray->AsIntArray()->Memmove(dstPos, srcArray->AsIntArray(), srcPos, count);
-        return;
       case Primitive::kPrimFloat:
         DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 4U);
-        dstArray->AsFloatArray()->Memmove(dstPos, srcArray->AsFloatArray(), srcPos, count);
+        // Note: Treating FloatArray as IntArray.
+        ObjPtr<mirror::IntArray>::DownCast(dstArray)->Memmove(
+            dstPos, ObjPtr<mirror::IntArray>::DownCast(srcArray), srcPos, count);
         return;
       case Primitive::kPrimLong:
-        DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 8U);
-        dstArray->AsLongArray()->Memmove(dstPos, srcArray->AsLongArray(), srcPos, count);
-        return;
       case Primitive::kPrimDouble:
         DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 8U);
-        dstArray->AsDoubleArray()->Memmove(dstPos, srcArray->AsDoubleArray(), srcPos, count);
+        // Note: Treating DoubleArray as LongArray.
+        ObjPtr<mirror::LongArray>::DownCast(dstArray)->Memmove(
+            dstPos, ObjPtr<mirror::LongArray>::DownCast(srcArray), srcPos, count);
         return;
       case Primitive::kPrimNot: {
-        mirror::ObjectArray<mirror::Object>* dstObjArray = dstArray->AsObjectArray<mirror::Object>();
-        mirror::ObjectArray<mirror::Object>* srcObjArray = srcArray->AsObjectArray<mirror::Object>();
+        mirror::ObjectArray<mirror::Object>* dstObjArray =
+            dstArray->AsObjectArray<mirror::Object>();
+        mirror::ObjectArray<mirror::Object>* srcObjArray =
+            srcArray->AsObjectArray<mirror::Object>();
         dstObjArray->AssignableMemmove(dstPos, srcObjArray, srcPos, count);
         return;
       }
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index b9ac88d..f21ded9 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -334,14 +334,15 @@
     return;
   }
   ObjPtr<mirror::Class> field_type;
-  const char* field_type_desciptor = f->GetArtField()->GetTypeDescriptor();
-  Primitive::Type field_prim_type = Primitive::GetType(field_type_desciptor[0]);
+  const char* field_type_descriptor = f->GetArtField()->GetTypeDescriptor();
+  Primitive::Type field_prim_type = Primitive::GetType(field_type_descriptor[0]);
   if (field_prim_type == Primitive::kPrimNot) {
     field_type = f->GetType();
-    DCHECK(field_type != nullptr);
   } else {
-    field_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(field_type_desciptor[0]);
+    field_type =
+        Runtime::Current()->GetClassLinker()->LookupPrimitiveClass(field_type_descriptor[0]);
   }
+  DCHECK(field_type != nullptr) << field_type_descriptor;
   // We now don't expect suspension unless an exception is thrown.
   // Unbox the value, if necessary.
   ObjPtr<mirror::Object> boxed_value = soa.Decode<mirror::Object>(javaValue);
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index e021b77..5014f34 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -31,8 +31,10 @@
 #include "mirror/array.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
+#include "art_field-inl.h"
 #include "native_util.h"
 #include "scoped_fast_native_object_access-inl.h"
+#include "well_known_classes.h"
 
 namespace art {
 
@@ -364,13 +366,17 @@
   ObjPtr<mirror::Object> dst = soa.Decode<mirror::Object>(dstObj);
   ObjPtr<mirror::Class> component_type = dst->GetClass()->GetComponentType();
   if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
-    copyToArray(srcAddr, MakeObjPtr(dst->AsByteSizedArray()), dst_offset, sz);
+    // Note: Treating BooleanArray as ByteArray.
+    copyToArray(srcAddr, ObjPtr<mirror::ByteArray>::DownCast(dst), dst_offset, sz);
   } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
-    copyToArray(srcAddr, MakeObjPtr(dst->AsShortSizedArray()), dst_offset, sz);
+    // Note: Treating CharArray as ShortArray.
+    copyToArray(srcAddr, ObjPtr<mirror::ShortArray>::DownCast(dst), dst_offset, sz);
   } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
-    copyToArray(srcAddr, MakeObjPtr(dst->AsIntArray()), dst_offset, sz);
+    // Note: Treating FloatArray as IntArray.
+    copyToArray(srcAddr, ObjPtr<mirror::IntArray>::DownCast(dst), dst_offset, sz);
   } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
-    copyToArray(srcAddr, MakeObjPtr(dst->AsLongArray()), dst_offset, sz);
+    // Note: Treating DoubleArray as LongArray.
+    copyToArray(srcAddr, ObjPtr<mirror::LongArray>::DownCast(dst), dst_offset, sz);
   } else {
     ThrowIllegalAccessException("not a primitive array");
   }
@@ -395,13 +401,17 @@
   ObjPtr<mirror::Object> src = soa.Decode<mirror::Object>(srcObj);
   ObjPtr<mirror::Class> component_type = src->GetClass()->GetComponentType();
   if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
-    copyFromArray(dstAddr, MakeObjPtr(src->AsByteSizedArray()), src_offset, sz);
+    // Note: Treating BooleanArray as ByteArray.
+    copyFromArray(dstAddr, ObjPtr<mirror::ByteArray>::DownCast(src), src_offset, sz);
   } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
-    copyFromArray(dstAddr, MakeObjPtr(src->AsShortSizedArray()), src_offset, sz);
+    // Note: Treating CharArray as ShortArray.
+    copyFromArray(dstAddr, ObjPtr<mirror::ShortArray>::DownCast(src), src_offset, sz);
   } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
-    copyFromArray(dstAddr, MakeObjPtr(src->AsIntArray()), src_offset, sz);
+    // Note: Treating FloatArray as IntArray.
+    copyFromArray(dstAddr, ObjPtr<mirror::IntArray>::DownCast(src), src_offset, sz);
   } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
-    copyFromArray(dstAddr, MakeObjPtr(src->AsLongArray()), src_offset, sz);
+    // Note: Treating DoubleArray as LongArray.
+    copyFromArray(dstAddr, ObjPtr<mirror::LongArray>::DownCast(src), src_offset, sz);
   } else {
     ThrowIllegalAccessException("not a primitive array");
   }
@@ -504,6 +514,33 @@
   std::atomic_thread_fence(std::memory_order_seq_cst);
 }
 
+static void Unsafe_park(JNIEnv* env, jobject, jboolean isAbsolute, jlong time) {
+  ScopedObjectAccess soa(env);
+  Thread::Current()->Park(isAbsolute, time);
+}
+
+static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
+  art::ScopedFastNativeObjectAccess soa(env);
+  if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) {
+    ThrowIllegalArgumentException("Argument to unpark() was not a Thread");
+    return;
+  }
+  art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+  art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+  if (thread != nullptr) {
+    thread->Unpark();
+  } else {
+    // If thread is null, that means that either the thread is not started yet,
+    // or the thread has already terminated. Setting the field to true will be
+    // respected when the thread does start, and is harmless if the thread has
+    // already terminated.
+    ArtField* unparked =
+        jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+    // JNI must use non transactional mode.
+    unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
+  }
+}
+
 static JNINativeMethod gMethods[] = {
   FAST_NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"),
   FAST_NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"),
@@ -546,6 +583,8 @@
   FAST_NATIVE_METHOD(Unsafe, putShort, "(Ljava/lang/Object;JS)V"),
   FAST_NATIVE_METHOD(Unsafe, putFloat, "(Ljava/lang/Object;JF)V"),
   FAST_NATIVE_METHOD(Unsafe, putDouble, "(Ljava/lang/Object;JD)V"),
+  FAST_NATIVE_METHOD(Unsafe, unpark, "(Ljava/lang/Object;)V"),
+  NATIVE_METHOD(Unsafe, park, "(ZJ)V"),
 
   // Each of the getFoo variants are overloaded with a call that operates
   // directively on a native pointer.
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 10f5898..88adad0 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -373,7 +373,7 @@
     }
     os << std::endl;
     if (try_addr2line && use_addr2line) {
-      Addr2line(it->map.name, it->pc - it->map.start, os, prefix, &addr2line_state);
+      Addr2line(it->map.name, it->rel_pc, os, prefix, &addr2line_state);
     }
   }
 
diff --git a/runtime/oat.h b/runtime/oat.h
index 2a6d738..3d6415e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -34,9 +34,7 @@
   // Last oat version changed reason: Remove interpreter alt tables.
   static constexpr uint8_t kOatVersion[] = { '1', '6', '3', '\0' };
 
-  static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
-  static constexpr const char* kDex2OatHostKey = "dex2oat-host";
   static constexpr const char* kDebuggableKey = "debuggable";
   static constexpr const char* kNativeDebuggableKey = "native-debuggable";
   static constexpr const char* kCompilerFilter = "compiler-filter";
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 646de75..8011836 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -886,32 +886,31 @@
 
   JValue boxed_value;
   ObjPtr<mirror::Class> klass = o->GetClass();
-  ObjPtr<mirror::Class> src_class = nullptr;
-  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  Primitive::Type primitive_type;
   ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0);
   if (klass->DescriptorEquals("Ljava/lang/Boolean;")) {
-    src_class = class_linker->FindPrimitiveClass('Z');
+    primitive_type = Primitive::kPrimBoolean;
     boxed_value.SetZ(primitive_field->GetBoolean(o));
   } else if (klass->DescriptorEquals("Ljava/lang/Byte;")) {
-    src_class = class_linker->FindPrimitiveClass('B');
+    primitive_type = Primitive::kPrimByte;
     boxed_value.SetB(primitive_field->GetByte(o));
   } else if (klass->DescriptorEquals("Ljava/lang/Character;")) {
-    src_class = class_linker->FindPrimitiveClass('C');
+    primitive_type = Primitive::kPrimChar;
     boxed_value.SetC(primitive_field->GetChar(o));
   } else if (klass->DescriptorEquals("Ljava/lang/Float;")) {
-    src_class = class_linker->FindPrimitiveClass('F');
+    primitive_type = Primitive::kPrimFloat;
     boxed_value.SetF(primitive_field->GetFloat(o));
   } else if (klass->DescriptorEquals("Ljava/lang/Double;")) {
-    src_class = class_linker->FindPrimitiveClass('D');
+    primitive_type = Primitive::kPrimDouble;
     boxed_value.SetD(primitive_field->GetDouble(o));
   } else if (klass->DescriptorEquals("Ljava/lang/Integer;")) {
-    src_class = class_linker->FindPrimitiveClass('I');
+    primitive_type = Primitive::kPrimInt;
     boxed_value.SetI(primitive_field->GetInt(o));
   } else if (klass->DescriptorEquals("Ljava/lang/Long;")) {
-    src_class = class_linker->FindPrimitiveClass('J');
+    primitive_type = Primitive::kPrimLong;
     boxed_value.SetJ(primitive_field->GetLong(o));
   } else if (klass->DescriptorEquals("Ljava/lang/Short;")) {
-    src_class = class_linker->FindPrimitiveClass('S');
+    primitive_type = Primitive::kPrimShort;
     boxed_value.SetS(primitive_field->GetShort(o));
   } else {
     std::string temp;
@@ -923,7 +922,8 @@
   }
 
   return ConvertPrimitiveValue(unbox_for_result,
-                               src_class->GetPrimitiveType(), dst_class->GetPrimitiveType(),
+                               primitive_type,
+                               dst_class->GetPrimitiveType(),
                                boxed_value, unboxed_value);
 }
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 34b84f5..ccbc2d9 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -269,10 +269,8 @@
       oat_file_manager_(nullptr),
       is_low_memory_mode_(false),
       safe_mode_(false),
-      hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks),
-      pending_hidden_api_warning_(false),
+      hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
       dedupe_hidden_api_warnings_(true),
-      always_set_hidden_api_warning_flag_(false),
       hidden_api_access_event_log_rate_(0),
       dump_native_stack_on_sig_quit_(true),
       pruned_dalvik_cache_(false),
@@ -786,15 +784,11 @@
   // TODO(calin): We use the JIT class as a proxy for JIT compilation and for
   // recoding profiles. Maybe we should consider changing the name to be more clear it's
   // not only about compiling. b/28295073.
-  if (!safe_mode_ && (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo())) {
+  if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) {
     // Try to load compiler pre zygote to reduce PSS. b/27744947
     std::string error_msg;
-    if (!jit::Jit::LoadCompiler(&error_msg)) {
+    if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
       LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
-    } else if (!IsZygote()) {
-      // If we are the zygote then we need to wait until after forking to create the code cache
-      // due to SELinux restrictions on r/w/x memory regions.
-      CreateJitCodeCache(/*rwx_memory_allowed=*/true);
     }
   }
 
@@ -900,6 +894,11 @@
   }
 
   if (jit_ == nullptr) {
+    // The system server's code cache was initialized specially. For other zygote forks or
+    // processes create it now.
+    if (!is_system_server) {
+      CreateJitCodeCache(/*rwx_memory_allowed=*/true);
+    }
     // Note that when running ART standalone (not zygote, nor zygote fork),
     // the jit may have already been created.
     CreateJit();
@@ -1101,6 +1100,10 @@
   sentinel_ = GcRoot<mirror::Object>(sentinel);
 }
 
+GcRoot<mirror::Object> Runtime::GetSentinel() {
+  return sentinel_;
+}
+
 static inline void CreatePreAllocatedException(Thread* self,
                                                Runtime* runtime,
                                                GcRoot<mirror::Throwable>* exception,
@@ -1230,8 +1233,8 @@
   // As is, we're encoding some logic here about which specific policy to use, which would be better
   // controlled by the framework.
   hidden_api_policy_ = do_hidden_api_checks
-      ? hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList
-      : hiddenapi::EnforcementPolicy::kNoChecks;
+      ? hiddenapi::EnforcementPolicy::kEnabled
+      : hiddenapi::EnforcementPolicy::kDisabled;
 
   no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
   force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
@@ -2485,7 +2488,7 @@
     DCHECK(!jit_options_->UseJitCompilation());
   }
 
-  if (safe_mode_ || (!jit_options_->UseJitCompilation() && !jit_options_->GetSaveProfilingInfo())) {
+  if (!jit_options_->UseJitCompilation() && !jit_options_->GetSaveProfilingInfo()) {
     return;
   }
 
@@ -2506,7 +2509,16 @@
 }
 
 void Runtime::CreateJit() {
+  DCHECK(jit_ == nullptr);
   if (jit_code_cache_.get() == nullptr) {
+    if (!IsSafeMode()) {
+      LOG(WARNING) << "Missing code cache, cannot create JIT.";
+    }
+    return;
+  }
+  if (IsSafeMode()) {
+    LOG(INFO) << "Not creating JIT because of SafeMode.";
+    jit_code_cache_.reset();
     return;
   }
 
@@ -2515,7 +2527,7 @@
   if (jit == nullptr) {
     LOG(WARNING) << "Failed to allocate JIT";
     // Release JIT code cache resources (several MB of memory).
-    jit_code_cache_.reset(nullptr);
+    jit_code_cache_.reset();
   }
 }
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 4fb0d2e..f6a5634 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -535,10 +535,6 @@
     return hidden_api_policy_;
   }
 
-  void SetPendingHiddenApiWarning(bool value) {
-    pending_hidden_api_warning_ = value;
-  }
-
   void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
     hidden_api_exemptions_ = exemptions;
   }
@@ -547,10 +543,6 @@
     return hidden_api_exemptions_;
   }
 
-  bool HasPendingHiddenApiWarning() const {
-    return pending_hidden_api_warning_;
-  }
-
   void SetDedupeHiddenApiWarnings(bool value) {
     dedupe_hidden_api_warnings_ = value;
   }
@@ -559,14 +551,6 @@
     return dedupe_hidden_api_warnings_;
   }
 
-  void AlwaysSetHiddenApiWarningFlag() {
-    always_set_hidden_api_warning_flag_ = true;
-  }
-
-  bool ShouldAlwaysSetHiddenApiWarningFlag() const {
-    return always_set_hidden_api_warning_flag_;
-  }
-
   void SetHiddenApiEventLogSampleRate(uint32_t rate) {
     hidden_api_access_event_log_rate_ = rate;
   }
@@ -650,6 +634,13 @@
   void DeoptimizeBootImage();
 
   bool IsNativeDebuggable() const {
+    CHECK(!is_zygote_ || IsAotCompiler());
+    return is_native_debuggable_;
+  }
+
+  // Note: prefer not to use this method, but the checked version above. The separation exists
+  //       as the runtime state may change for a zygote child.
+  bool IsNativeDebuggableZygoteOK() const {
     return is_native_debuggable_;
   }
 
@@ -691,6 +682,9 @@
 
   // Called from class linker.
   void SetSentinel(mirror::Object* sentinel) REQUIRES_SHARED(Locks::mutator_lock_);
+  // For testing purpose only.
+  // TODO: Remove this when this is no longer needed (b/116087961).
+  GcRoot<mirror::Object> GetSentinel() REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Create a normal LinearAlloc or low 4gb version if we are 64 bit AOT compiler.
   LinearAlloc* CreateLinearAlloc();
@@ -703,6 +697,11 @@
   double GetHashTableMinLoadFactor() const;
   double GetHashTableMaxLoadFactor() const;
 
+  bool IsSafeMode() const {
+    CHECK(!is_zygote_);
+    return safe_mode_;
+  }
+
   void SetSafeMode(bool mode) {
     safe_mode_ = mode;
   }
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 758917c..bf74816 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -151,6 +151,26 @@
   Remove(cb, &monitor_callbacks_);
 }
 
+void RuntimeCallbacks::ThreadParkStart(bool is_absolute, int64_t timeout) {
+  for (ParkCallback * cb : park_callbacks_) {
+    cb->ThreadParkStart(is_absolute, timeout);
+  }
+}
+
+void RuntimeCallbacks::ThreadParkFinished(bool timeout) {
+  for (ParkCallback * cb : park_callbacks_) {
+    cb->ThreadParkFinished(timeout);
+  }
+}
+
+void RuntimeCallbacks::AddParkCallback(ParkCallback* cb) {
+  park_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveParkCallback(ParkCallback* cb) {
+  Remove(cb, &park_callbacks_);
+}
+
 void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
   Remove(cb, &thread_callbacks_);
 }
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 9f0410d..4cce15e 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -115,6 +115,19 @@
   virtual ~MonitorCallback() {}
 };
 
+class ParkCallback {
+ public:
+  // Called on entry to the Unsafe.#park method
+  virtual void ThreadParkStart(bool is_absolute, int64_t millis_timeout)
+      REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+  // Called just after the thread has woken up from going to sleep for a park(). This will only be
+  // called for Unsafe.park() calls where the thread did (or at least could have) gone to sleep.
+  virtual void ThreadParkFinished(bool timed_out) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+  virtual ~ParkCallback() {}
+};
+
 // A callback to let parts of the runtime note that they are currently relying on a particular
 // method remaining in it's current state. Users should not rely on always being called. If multiple
 // callbacks are added the runtime will short-circuit when the first one returns 'true'.
@@ -193,6 +206,11 @@
   void AddMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
   void RemoveMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void ThreadParkStart(bool is_absolute, int64_t timeout) REQUIRES_SHARED(Locks::mutator_lock_);
+  void ThreadParkFinished(bool timed_out) REQUIRES_SHARED(Locks::mutator_lock_);
+  void AddParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+  void RemoveParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Returns true if some MethodInspectionCallback indicates the method is being inspected/depended
   // on by some code.
   bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -243,6 +261,8 @@
       GUARDED_BY(Locks::mutator_lock_);
   std::vector<MonitorCallback*> monitor_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
+  std::vector<ParkCallback*> park_callbacks_
+      GUARDED_BY(Locks::mutator_lock_);
   std::vector<MethodInspectionCallback*> method_inspection_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
   std::vector<DdmCallback*> ddm_callbacks_
diff --git a/runtime/thread.cc b/runtime/thread.cc
index dda3b82..e9fed76 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -44,6 +44,7 @@
 #include "arch/context.h"
 #include "art_field-inl.h"
 #include "art_method-inl.h"
+#include "base/atomic.h"
 #include "base/bit_utils.h"
 #include "base/casts.h"
 #include "base/file_utils.h"
@@ -285,6 +286,120 @@
       << "No deoptimization context for thread " << *this;
 }
 
+enum {
+  kPermitAvailable = 0,  // Incrementing consumes the permit
+  kNoPermit = 1,  // Incrementing marks as waiter waiting
+  kNoPermitWaiterWaiting = 2
+};
+
+void Thread::Park(bool is_absolute, int64_t time) {
+  DCHECK(this == Thread::Current());
+#if ART_USE_FUTEXES
+  // Consume the permit, or mark as waiting. This cannot cause park_state to go
+  // outside of its valid range (0, 1, 2), because in all cases where 2 is
+  // assigned it is set back to 1 before returning, and this method cannot run
+  // concurrently with itself since it operates on the current thread.
+  int old_state = tls32_.park_state_.fetch_add(1, std::memory_order_relaxed);
+  if (old_state == kNoPermit) {
+    // no permit was available. block thread until later.
+    Runtime::Current()->GetRuntimeCallbacks()->ThreadParkStart(is_absolute, time);
+    int result = 0;
+    bool timed_out = false;
+    if (!is_absolute && time == 0) {
+      // Thread.getState() is documented to return waiting for untimed parks.
+      ScopedThreadSuspension sts(this, ThreadState::kWaiting);
+      DCHECK_EQ(NumberOfHeldMutexes(), 0u);
+      result = futex(tls32_.park_state_.Address(),
+                     FUTEX_WAIT_PRIVATE,
+                     /* sleep if val = */ kNoPermitWaiterWaiting,
+                     /* timeout */ nullptr,
+                     nullptr,
+                     0);
+    } else if (time > 0) {
+      // Only actually suspend and futex_wait if we're going to wait for some
+      // positive amount of time - the kernel will reject negative times with
+      // EINVAL, and a zero time will just noop.
+
+      // Thread.getState() is documented to return timed wait for timed parks.
+      ScopedThreadSuspension sts(this, ThreadState::kTimedWaiting);
+      DCHECK_EQ(NumberOfHeldMutexes(), 0u);
+      timespec timespec;
+      if (is_absolute) {
+        // Time is millis when scheduled for an absolute time
+        timespec.tv_nsec = (time % 1000) * 1000000;
+        timespec.tv_sec = time / 1000;
+        // This odd looking pattern is recommended by futex documentation to
+        // wait until an absolute deadline, with otherwise identical behavior to
+        // FUTEX_WAIT_PRIVATE. This also allows parkUntil() to return at the
+        // correct time when the system clock changes.
+        result = futex(tls32_.park_state_.Address(),
+                       FUTEX_WAIT_BITSET_PRIVATE | FUTEX_CLOCK_REALTIME,
+                       /* sleep if val = */ kNoPermitWaiterWaiting,
+                       &timespec,
+                       nullptr,
+                       FUTEX_BITSET_MATCH_ANY);
+      } else {
+        // Time is nanos when scheduled for a relative time
+        timespec.tv_sec = time / 1000000000;
+        timespec.tv_nsec = time % 1000000000;
+        result = futex(tls32_.park_state_.Address(),
+                       FUTEX_WAIT_PRIVATE,
+                       /* sleep if val = */ kNoPermitWaiterWaiting,
+                       &timespec,
+                       nullptr,
+                       0);
+      }
+    }
+    if (result == -1) {
+      switch (errno) {
+        case ETIMEDOUT:
+          timed_out = true;
+          FALLTHROUGH_INTENDED;
+        case EAGAIN:
+        case EINTR: break;  // park() is allowed to spuriously return
+        default: PLOG(FATAL) << "Failed to park";
+      }
+    }
+    // Mark as no longer waiting, and consume permit if there is one.
+    tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed);
+    // TODO: Call to signal jvmti here
+    Runtime::Current()->GetRuntimeCallbacks()->ThreadParkFinished(timed_out);
+  } else {
+    // the fetch_add has consumed the permit. immediately return.
+    DCHECK_EQ(old_state, kPermitAvailable);
+  }
+#else
+  #pragma clang diagnostic push
+  #pragma clang diagnostic warning "-W#warnings"
+  #warning "LockSupport.park/unpark implemented as noops without FUTEX support."
+  #pragma clang diagnostic pop
+  UNUSED(is_absolute, time);
+  UNIMPLEMENTED(WARNING);
+  sched_yield();
+#endif
+}
+
+void Thread::Unpark() {
+#if ART_USE_FUTEXES
+  // Set permit available; will be consumed either by fetch_add (when the thread
+  // tries to park) or store (when the parked thread is woken up)
+  if (tls32_.park_state_.exchange(kPermitAvailable, std::memory_order_relaxed)
+      == kNoPermitWaiterWaiting) {
+    int result = futex(tls32_.park_state_.Address(),
+                       FUTEX_WAKE_PRIVATE,
+                       /* number of waiters = */ 1,
+                       nullptr,
+                       nullptr,
+                       0);
+    if (result == -1) {
+      PLOG(FATAL) << "Failed to unpark";
+    }
+  }
+#else
+  UNIMPLEMENTED(WARNING);
+#endif
+}
+
 void Thread::PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type) {
   StackedShadowFrameRecord* record = new StackedShadowFrameRecord(
       sf, type, tlsPtr_.stacked_shadow_frame_record);
@@ -489,6 +604,22 @@
 
     runtime->GetRuntimeCallbacks()->ThreadStart(self);
 
+    // Unpark ourselves if the java peer was unparked before it started (see
+    // b/28845097#comment49 for more information)
+
+    ArtField* unparkedField = jni::DecodeArtField(
+        WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+    bool should_unpark = false;
+    {
+      // Hold the lock here, so that if another thread calls unpark before the thread starts
+      // we don't observe the unparkedBeforeStart field before the unparker writes to it,
+      // which could cause a lost unpark.
+      art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+      should_unpark = unparkedField->GetBoolean(self->tlsPtr_.opeer) == JNI_TRUE;
+    }
+    if (should_unpark) {
+      self->Unpark();
+    }
     // Invoke the 'run' method of our java.lang.Thread.
     ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
     jmethodID mid = WellKnownClasses::java_lang_Thread_run;
@@ -2123,7 +2254,7 @@
     : tls32_(daemon),
       wait_monitor_(nullptr),
       is_runtime_thread_(false) {
-  wait_mutex_ = new Mutex("a thread wait mutex");
+  wait_mutex_ = new Mutex("a thread wait mutex", LockLevel::kThreadWaitLock);
   wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
   tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
   tlsPtr_.name = new std::string(kThreadNameDuringStartup);
@@ -2133,6 +2264,9 @@
   tls32_.state_and_flags.as_struct.flags = 0;
   tls32_.state_and_flags.as_struct.state = kNative;
   tls32_.interrupted.store(false, std::memory_order_relaxed);
+  // Initialize with no permit; if the java Thread was unparked before being
+  // started, it will unpark itself before calling into java code.
+  tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed);
   memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes));
   std::fill(tlsPtr_.rosalloc_runs,
             tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread,
@@ -2449,12 +2583,15 @@
 }
 
 void Thread::Interrupt(Thread* self) {
-  MutexLock mu(self, *wait_mutex_);
-  if (tls32_.interrupted.load(std::memory_order_seq_cst)) {
-    return;
+  {
+    MutexLock mu(self, *wait_mutex_);
+    if (tls32_.interrupted.load(std::memory_order_seq_cst)) {
+      return;
+    }
+    tls32_.interrupted.store(true, std::memory_order_seq_cst);
+    NotifyLocked(self);
   }
-  tls32_.interrupted.store(true, std::memory_order_seq_cst);
-  NotifyLocked(self);
+  Unpark();
 }
 
 void Thread::Notify() {
diff --git a/runtime/thread.h b/runtime/thread.h
index 941867c..b304cef 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -581,6 +581,11 @@
     return poison_object_cookie_;
   }
 
+  // Parking for 0ns of relative time means an untimed park, negative (though
+  // should be handled in java code) returns immediately
+  void Park(bool is_absolute, int64_t time) REQUIRES_SHARED(Locks::mutator_lock_);
+  void Unpark();
+
  private:
   void NotifyLocked(Thread* self) REQUIRES(wait_mutex_);
 
@@ -1543,6 +1548,8 @@
     // Thread "interrupted" status; stays raised until queried or thrown.
     Atomic<bool32_t> interrupted;
 
+    AtomicInteger park_state_;
+
     // True if the thread is allowed to access a weak ref (Reference::GetReferent() and system
     // weaks) and to potentially mark an object alive/gray. This is used for concurrent reference
     // processing of the CC collector only. This is thread local so that we can enable/disable weak
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 206418f..d390611 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -122,12 +122,14 @@
 jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
 jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
 jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+jfieldID WellKnownClasses::java_lang_Thread_parkBlocker;
 jfieldID WellKnownClasses::java_lang_Thread_daemon;
 jfieldID WellKnownClasses::java_lang_Thread_group;
 jfieldID WellKnownClasses::java_lang_Thread_lock;
 jfieldID WellKnownClasses::java_lang_Thread_name;
 jfieldID WellKnownClasses::java_lang_Thread_priority;
 jfieldID WellKnownClasses::java_lang_Thread_nativePeer;
+jfieldID WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
 jfieldID WellKnownClasses::java_lang_ThreadGroup_groups;
 jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups;
 jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
@@ -291,7 +293,7 @@
 
 void WellKnownClasses::Init(JNIEnv* env) {
   hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
-      hiddenapi::EnforcementPolicy::kNoChecks);
+      hiddenapi::EnforcementPolicy::kDisabled);
 
   dalvik_annotation_optimization_CriticalNative =
       CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
@@ -370,12 +372,14 @@
   dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
   dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
   dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;");
+  java_lang_Thread_parkBlocker = CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
   java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
   java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
   java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
   java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;");
   java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
   java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
+  java_lang_Thread_unparkedBeforeStart = CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
   java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
   java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I");
   java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
@@ -516,6 +520,7 @@
   dalvik_system_DexPathList_dexElements = nullptr;
   dalvik_system_DexPathList__Element_dexFile = nullptr;
   dalvik_system_VMRuntime_nonSdkApiUsageConsumer = nullptr;
+  java_lang_Thread_parkBlocker = nullptr;
   java_lang_Thread_daemon = nullptr;
   java_lang_Thread_group = nullptr;
   java_lang_Thread_lock = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index ce5ab1d..130747c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -131,12 +131,14 @@
   static jfieldID dalvik_system_DexPathList_dexElements;
   static jfieldID dalvik_system_DexPathList__Element_dexFile;
   static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+  static jfieldID java_lang_Thread_parkBlocker;
   static jfieldID java_lang_Thread_daemon;
   static jfieldID java_lang_Thread_group;
   static jfieldID java_lang_Thread_lock;
   static jfieldID java_lang_Thread_name;
   static jfieldID java_lang_Thread_priority;
   static jfieldID java_lang_Thread_nativePeer;
+  static jfieldID java_lang_Thread_unparkedBeforeStart;
   static jfieldID java_lang_ThreadGroup_groups;
   static jfieldID java_lang_ThreadGroup_ngroups;
   static jfieldID java_lang_ThreadGroup_mainThreadGroup;
diff --git a/simulator/Android.bp b/simulator/Android.bp
index 8690426..223c891 100644
--- a/simulator/Android.bp
+++ b/simulator/Android.bp
@@ -45,7 +45,7 @@
     shared_libs: [
         "libart",
         "libartbase",
-        "libvixl-arm64",
+        "libvixl",
     ],
 }
 
@@ -58,7 +58,7 @@
     shared_libs: [
         "libartd",
         "libartbased",
-        "libvixld-arm64",
+        "libvixld",
     ],
 }
 
diff --git a/test/004-ThreadStress/src-art/Main.java b/test/004-ThreadStress/src-art/Main.java
index 3a89f4f..e717852 100644
--- a/test/004-ThreadStress/src-art/Main.java
+++ b/test/004-ThreadStress/src-art/Main.java
@@ -26,6 +26,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Semaphore;
+import java.util.concurrent.locks.LockSupport;
 
 // Run on host with:
 //   javac ThreadTest.java && java ThreadStress && rm *.class
@@ -52,6 +53,7 @@
 //    -sleep:X .......... frequency of Sleep (double)
 //    -wait:X ........... frequency of Wait (double)
 //    -timedwait:X ...... frequency of TimedWait (double)
+//    -timedpark:X ...... frequency of TimedPark (double)
 //    -syncandwork:X .... frequency of SyncAndWork (double)
 //    -queuedwait:X ..... frequency of QueuedWait (double)
 
@@ -251,6 +253,18 @@
         }
     }
 
+    private final static class TimedPark extends Operation {
+        private final static int SLEEP_TIME = 100;
+
+        public TimedPark() {}
+
+        @Override
+        public boolean perform() {
+            LockSupport.parkNanos(this, 100*1000000);
+            return true;
+        }
+    }
+
     private final static class SyncAndWork extends Operation {
         private final Object lock;
 
@@ -320,7 +334,8 @@
         frequencyMap.put(new NonMovingAlloc(), 0.025);        //   5/200
         frequencyMap.put(new StackTrace(), 0.1);              //  20/200
         frequencyMap.put(new Exit(), 0.225);                  //  45/200
-        frequencyMap.put(new Sleep(), 0.125);                 //  25/200
+        frequencyMap.put(new Sleep(), 0.075);                 //  15/200
+        frequencyMap.put(new TimedPark(), 0.05);              //  10/200
         frequencyMap.put(new TimedWait(lock), 0.05);          //  10/200
         frequencyMap.put(new Wait(lock), 0.075);              //  15/200
         frequencyMap.put(new QueuedWait(semaphore), 0.05);    //  10/200
@@ -341,9 +356,10 @@
     private final static Map<Operation, Double> createLockFrequencyMap(Object lock) {
       Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
       frequencyMap.put(new Sleep(), 0.2);                     //  40/200
-      frequencyMap.put(new TimedWait(lock), 0.2);             //  40/200
+      frequencyMap.put(new TimedWait(lock), 0.1);             //  20/200
       frequencyMap.put(new Wait(lock), 0.2);                  //  40/200
       frequencyMap.put(new SyncAndWork(lock), 0.4);           //  80/200
+      frequencyMap.put(new TimedPark(), 0.1);                 //  20/200
 
       return frequencyMap;
     }
@@ -389,6 +405,8 @@
             op = new Wait(lock);
         } else if (split[0].equals("-timedwait")) {
             op = new TimedWait(lock);
+        } else if (split[0].equals("-timedpark")) {
+            op = new TimedPark();
         } else if (split[0].equals("-syncandwork")) {
             op = new SyncAndWork(lock);
         } else if (split[0].equals("-queuedwait")) {
@@ -693,7 +711,7 @@
         }
 
         // The notifier thread is a daemon just loops forever to wake
-        // up threads in operation Wait.
+        // up threads in operations Wait and Park.
         if (lock != null) {
             Thread notifier = new Thread("Notifier") {
                 public void run() {
@@ -701,6 +719,11 @@
                         synchronized (lock) {
                             lock.notifyAll();
                         }
+                        for (Thread runner : runners) {
+                          if (runner != null) {
+                            LockSupport.unpark(runner);
+                          }
+                        }
                     }
                 }
             };
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index d43d374..9176e89 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -32,6 +32,20 @@
     }
   }
 
+  private static void check(float actual, float expected, String msg) {
+    if (actual != expected) {
+      System.out.println(msg + " : " + actual + " != " + expected);
+      System.exit(1);
+    }
+  }
+
+  private static void check(double actual, double expected, String msg) {
+    if (actual != expected) {
+      System.out.println(msg + " : " + actual + " != " + expected);
+      System.exit(1);
+    }
+  }
+
   private static void check(Object actual, Object expected, String msg) {
     if (actual != expected) {
       System.out.println(msg + " : " + actual + " != " + expected);
@@ -54,6 +68,7 @@
     testArrayIndexScale(unsafe);
     testGetAndPutAndCAS(unsafe);
     testGetAndPutVolatile(unsafe);
+    testCopyMemoryPrimitiveArrays(unsafe);
   }
 
   private static void testArrayBaseOffset(Unsafe unsafe) {
@@ -237,6 +252,38 @@
           "Unsafe.getObjectVolatile(Object, long)");
   }
 
+  // Regression test for "copyMemory" operations hitting a DCHECK() for float/double arrays.
+  private static void testCopyMemoryPrimitiveArrays(Unsafe unsafe) {
+    int size = 4 * 1024;
+    long memory = unsafeTestMalloc(size);
+
+    int floatSize = 4;
+    float[] inputFloats = new float[size / floatSize];
+    for (int i = 0; i != inputFloats.length; ++i) {
+      inputFloats[i] = ((float)i) + 0.5f;
+    }
+    float[] outputFloats = new float[size / floatSize];
+    unsafe.copyMemoryFromPrimitiveArray(inputFloats, 0, memory, size);
+    unsafe.copyMemoryToPrimitiveArray(memory, outputFloats, 0, size);
+    for (int i = 0; i != inputFloats.length; ++i) {
+      check(inputFloats[i], outputFloats[i], "unsafe.copyMemory/float");
+    }
+
+    int doubleSize = 8;
+    double[] inputDoubles = new double[size / doubleSize];
+    for (int i = 0; i != inputDoubles.length; ++i) {
+      inputDoubles[i] = ((double)i) + 0.5;
+    }
+    double[] outputDoubles = new double[size / doubleSize];
+    unsafe.copyMemoryFromPrimitiveArray(inputDoubles, 0, memory, size);
+    unsafe.copyMemoryToPrimitiveArray(memory, outputDoubles, 0, size);
+    for (int i = 0; i != inputDoubles.length; ++i) {
+      check(inputDoubles[i], outputDoubles[i], "unsafe.copyMemory/double");
+    }
+
+    unsafeTestFree(memory);
+  }
+
   private static class TestClass {
     public int intVar = 0;
     public long longVar = 0;
@@ -251,4 +298,6 @@
 
   private static native int vmArrayBaseOffset(Class<?> clazz);
   private static native int vmArrayIndexScale(Class<?> clazz);
+  private static native long unsafeTestMalloc(long size);
+  private static native void unsafeTestFree(long memory);
 }
diff --git a/test/004-UnsafeTest/unsafe_test.cc b/test/004-UnsafeTest/unsafe_test.cc
index 18d9ea8..e970aaa 100644
--- a/test/004-UnsafeTest/unsafe_test.cc
+++ b/test/004-UnsafeTest/unsafe_test.cc
@@ -15,6 +15,7 @@
  */
 
 #include "art_method-inl.h"
+#include "base/casts.h"
 #include "jni.h"
 #include "mirror/array.h"
 #include "mirror/class-inl.h"
@@ -37,4 +38,16 @@
   return Primitive::ComponentSize(klass->GetComponentType()->GetPrimitiveType());
 }
 
+extern "C" JNIEXPORT jlong JNICALL Java_Main_unsafeTestMalloc(JNIEnv*, jclass, jlong size) {
+  void* memory = malloc(dchecked_integral_cast<size_t>(size));
+  CHECK(memory != nullptr);
+  return reinterpret_cast64<jlong>(memory);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_unsafeTestFree(JNIEnv*, jclass, jlong memory) {
+  void* mem = reinterpret_cast64<void*>(memory);
+  CHECK(mem != nullptr);
+  free(mem);
+}
+
 }  // namespace art
diff --git a/test/099-vmdebug/check b/test/099-vmdebug/check
index d124ce8..6a3fed5 100755
--- a/test/099-vmdebug/check
+++ b/test/099-vmdebug/check
@@ -15,6 +15,6 @@
 # limitations under the License.
 
 # Strip the process pids and line numbers from exact error messages.
-sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
+sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
 
 diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/143-string-value/check b/test/143-string-value/check
index 2a3476c..b5e51ce 100755
--- a/test/143-string-value/check
+++ b/test/143-string-value/check
@@ -15,6 +15,6 @@
 # limitations under the License.
 
 # Strip error log messages.
-sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
+sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
 
 diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/160-read-barrier-stress/src/Main.java b/test/160-read-barrier-stress/src/Main.java
index 5865094..5e49e66 100644
--- a/test/160-read-barrier-stress/src/Main.java
+++ b/test/160-read-barrier-stress/src/Main.java
@@ -121,6 +121,23 @@
             assertSameObject(f4444, la[i4444]);
             assertDifferentObject(f4999, la[i4998]);
             assertSameObject(f4999, la[i4999]);
+
+            la = largeArray;
+            // Group the ArrayGets so they aren't divided by a function call; this will enable
+            // interm. address sharing for arm64.
+            Object tmp1 = la[i0];
+            Object tmp2 = la[i0 + 1];
+            Object tmp3 = la[i0 + 1024];
+            Object tmp4 = la[i0 + 4444];
+            Object tmp5 = la[i0 + 4998];
+            Object tmp6 = la[i0 + 4999];
+
+            assertSameObject(f0000, tmp1);
+            assertDifferentObject(f0000, tmp2);
+            assertSameObject(f1024, tmp3);
+            assertSameObject(f4444, tmp4);
+            assertDifferentObject(f4999, tmp5);
+            assertSameObject(f4999, tmp6);
         }
     }
 
diff --git a/test/1931-monitor-events/check b/test/1931-monitor-events/check
new file mode 100644
index 0000000..8a7f844
--- /dev/null
+++ b/test/1931-monitor-events/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Art sends events for park/unpark, and the RI doesn't. Remove it from the expected output.
+if [[ "$TEST_RUNTIME" == "jvm" ]]; then
+  patch -p0 expected.txt < jvm-expected.patch >/dev/null
+fi
+
+./default-check "$@"
diff --git a/test/1931-monitor-events/expected.txt b/test/1931-monitor-events/expected.txt
index 33a9bd3..f368ae2 100644
--- a/test/1931-monitor-events/expected.txt
+++ b/test/1931-monitor-events/expected.txt
@@ -1,6 +1,9 @@
 Testing contended locking.
 Locker thread 1 for NamedLock[Lock testLock] contended-LOCKING NamedLock[Lock testLock]
 Locker thread 1 for NamedLock[Lock testLock] LOCKED NamedLock[Lock testLock]
+Testing park.
+ParkThread start-monitor-wait NamedLock[Parking blocker object] timeout: 1
+ParkThread monitor-waited NamedLock[Parking blocker object] timed_out: true
 Testing monitor wait.
 Locker thread 2 for NamedLock[Lock testWait] start-monitor-wait NamedLock[Lock testWait] timeout: 0
 Locker thread 2 for NamedLock[Lock testWait] monitor-waited NamedLock[Lock testWait] timed_out: false
diff --git a/test/1931-monitor-events/jvm-expected.patch b/test/1931-monitor-events/jvm-expected.patch
new file mode 100644
index 0000000..7595b14
--- /dev/null
+++ b/test/1931-monitor-events/jvm-expected.patch
@@ -0,0 +1,3 @@
+5,6d4
+< ParkThread start-monitor-wait NamedLock[Parking blocker object] timeout: 1
+< ParkThread monitor-waited NamedLock[Parking blocker object] timed_out: true
diff --git a/test/1931-monitor-events/src/art/Test1931.java b/test/1931-monitor-events/src/art/Test1931.java
index ccefede..f549789 100644
--- a/test/1931-monitor-events/src/art/Test1931.java
+++ b/test/1931-monitor-events/src/art/Test1931.java
@@ -23,6 +23,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.*;
+import java.util.concurrent.locks.LockSupport;
 import java.util.ListIterator;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -67,6 +68,9 @@
     System.out.println("Testing contended locking.");
     testLock(new Monitors.NamedLock("Lock testLock"));
 
+    System.out.println("Testing park.");
+    testPark(new Monitors.NamedLock("Parking blocker object"));
+
     System.out.println("Testing monitor wait.");
     testWait(new Monitors.NamedLock("Lock testWait"));
 
@@ -88,6 +92,14 @@
     testInteruptWait(new Monitors.NamedLock("Lock testInteruptWait"));
   }
 
+  public static void testPark(Object blocker) throws Exception {
+    Thread holder = new Thread(() -> {
+      LockSupport.parkNanos(blocker, 10); // Should round up to one millisecond
+    }, "ParkThread");
+    holder.start();
+    holder.join();
+  }
+
   public static void testInteruptWait(final Monitors.NamedLock lk) throws Exception {
     final Monitors.LockController controller1 = new Monitors.LockController(lk);
     controller1.DoLock();
diff --git a/test/1958-transform-try-jit/expected.txt b/test/1958-transform-try-jit/expected.txt
new file mode 100644
index 0000000..8cfaea2
--- /dev/null
+++ b/test/1958-transform-try-jit/expected.txt
@@ -0,0 +1,2 @@
+Before redefinition: hello
+After redefinition: Goodbye
diff --git a/test/1958-transform-try-jit/info.txt b/test/1958-transform-try-jit/info.txt
new file mode 100644
index 0000000..0bf5ee5
--- /dev/null
+++ b/test/1958-transform-try-jit/info.txt
@@ -0,0 +1,5 @@
+Tests that JVMTI transformation seems to work even when we try to get a method
+inlined by the jit.
+
+Note this test deliberately avoids any internal libart calls so it can be
+included in CTS.
diff --git a/test/1958-transform-try-jit/run b/test/1958-transform-try-jit/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1958-transform-try-jit/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1958-transform-try-jit/src/Main.java b/test/1958-transform-try-jit/src/Main.java
new file mode 100644
index 0000000..7f45c20
--- /dev/null
+++ b/test/1958-transform-try-jit/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1958.run();
+  }
+}
diff --git a/test/1958-transform-try-jit/src/art/Redefinition.java b/test/1958-transform-try-jit/src/art/Redefinition.java
new file mode 100644
index 0000000..56d2938
--- /dev/null
+++ b/test/1958-transform-try-jit/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/1958-transform-try-jit/src/art/Test1958.java b/test/1958-transform-try-jit/src/art/Test1958.java
new file mode 100644
index 0000000..100daa1
--- /dev/null
+++ b/test/1958-transform-try-jit/src/art/Test1958.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Test1958 {
+  static class Runner {
+    public static String doSayHi() {
+      return sayHiHelper(true);
+    }
+
+    public static String dontSayHi() {
+      return sayHiHelper(false);
+    }
+
+    // We are trying to get the definition of Transform.sayHi inlined into this function.
+    public static String sayHiHelper(boolean sayHi) {
+      if (sayHi) {
+        return Transform.sayHi();
+      } else {
+        return "NOPE!";
+      }
+    }
+  }
+
+  static class Transform {
+    public static String sayHi() {
+      // Use lower 'h' to make sure the string will have a different string id
+      // than the transformation (the transformation code is the same except
+      // the actual printed String, which was making the test inacurately passing
+      // in JIT mode when loading the string from the dex cache, as the string ids
+      // of the two different strings were the same).
+      // We know the string ids will be different because lexicographically:
+      // "Goodbye" < "LTransform;" < "hello".
+      return "hello";
+    }
+  }
+
+  /**
+   * base64 encoded class/dex file for
+   * static class Transform {
+   *   public static String sayHi() {
+   *    return "Goodbye";
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADUAFQoABAANCAAOBwAQBwATAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" +
+    "ZXJUYWJsZQEABXNheUhpAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQANVGVz" +
+    "dDE5NTguamF2YQwABQAGAQAHR29vZGJ5ZQcAFAEAFmFydC9UZXN0MTk1OCRUcmFuc2Zvcm0BAAlU" +
+    "cmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAMYXJ0L1Rlc3QxOTU4" +
+    "ACAAAwAEAAAAAAACAAAABQAGAAEABwAAAB0AAQABAAAABSq3AAGxAAAAAQAIAAAABgABAAAABgAJ" +
+    "AAkACgABAAcAAAAbAAEAAAAAAAMSArAAAAABAAgAAAAGAAEAAAAIAAIACwAAAAIADAASAAAACgAB" +
+    "AAMADwARAAg=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQCRmEaLPLzpKe+CHcDM8YhIJCPWwcFR5yegAwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAR" +
+    "AAAAcAAAAAcAAAC0AAAAAgAAANAAAAAAAAAAAAAAAAMAAADoAAAAAQAAAAABAACAAgAAIAEAAFgB" +
+    "AABgAQAAaQEAAGwBAACGAQAAlgEAALoBAADaAQAA7gEAAAICAAARAgAAHAIAAB8CAAAsAgAAMgIA" +
+    "ADkCAABAAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACwAAAAIAAAAFAAAAAAAAAAsAAAAGAAAA" +
+    "AAAAAAAAAQAAAAAAAAAAAA4AAAAEAAEAAAAAAAAAAAAAAAAABAAAAAAAAAAJAAAA5AIAAMYCAAAA" +
+    "AAAAAQAAAAAAAABUAQAAAwAAABoAAQARAAAAAQABAAEAAABQAQAABAAAAHAQAgAAAA4ABgAOAAgA" +
+    "DgAGPGluaXQ+AAdHb29kYnllAAFMABhMYXJ0L1Rlc3QxOTU4JFRyYW5zZm9ybTsADkxhcnQvVGVz" +
+    "dDE5NTg7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" +
+    "YXRpb24vSW5uZXJDbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7" +
+    "AA1UZXN0MTk1OC5qYXZhAAlUcmFuc2Zvcm0AAVYAC2FjY2Vzc0ZsYWdzAARuYW1lAAVzYXlIaQAF" +
+    "dmFsdWUAdX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0x" +
+    "IjoiNTFjYWNlMWFiZGQwOGIzMjBmMjVmYjgxMTZjMjQzMmIwMmYwOTI5NSIsInZlcnNpb24iOiIx" +
+    "LjQuNS1kZXYifQACAgEPGAECAwIMBAgNFwoAAAIAAICABLgCAQmgAgAAAAACAAAAtwIAAL0CAADY" +
+    "AgAAAAAAAAAAAAAAAAAADgAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMA" +
+    "AAACAAAA0AAAAAUAAAADAAAA6AAAAAYAAAABAAAAAAEAAAEgAAACAAAAIAEAAAMgAAACAAAAUAEA" +
+    "AAIgAAARAAAAWAEAAAQgAAACAAAAtwIAAAAgAAABAAAAxgIAAAMQAAACAAAA1AIAAAYgAAABAAAA" +
+    "5AIAAAAQAAABAAAA9AIAAA==");
+
+  public static void run() throws Exception {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    Method doSayHi = Runner.class.getDeclaredMethod("doSayHi");
+    Method dontSayHi = Runner.class.getDeclaredMethod("dontSayHi");
+    // Run the method enough times that the jit thinks it's interesting (default 10000).
+    for (int i = 0; i < 20000; i++) {
+      doSayHi.invoke(null);
+      dontSayHi.invoke(null);
+    }
+    // Sleep for 10 seconds to let the jit finish any work it's doing.
+    Thread.sleep(10 * 1000);
+    // Check what we get right now.
+    System.out.println("Before redefinition: " + doSayHi.invoke(null));
+    Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    System.out.println("After redefinition: " + doSayHi.invoke(null));
+  }
+}
diff --git a/test/411-checker-hdiv-hrem-pow2/src/RemTest.java b/test/411-checker-hdiv-hrem-pow2/src/RemTest.java
index 72725c1..54d7847 100644
--- a/test/411-checker-hdiv-hrem-pow2/src/RemTest.java
+++ b/test/411-checker-hdiv-hrem-pow2/src/RemTest.java
@@ -92,6 +92,17 @@
   /// CHECK:                 cmp w{{\d+}}, #0x0
   /// CHECK:                 and w{{\d+}}, w{{\d+}}, #0x1
   /// CHECK:                 cneg w{{\d+}}, w{{\d+}}, lt
+  /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntMod2(int) disassembly (after)
+  /// CHECK:          Rem [{{i\d+}},{{i\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shr
+  /// CHECK-NOT:      imul
+  /// CHECK:          mov
+  /// CHECK:          and
+  /// CHECK:          jz/eq
+  /// CHECK:          lea
+  /// CHECK:          test
+  /// CHECK:          cmovl/nge
   private static Integer $noinline$IntMod2(int v) {
     int r = v % 2;
     return r;
@@ -101,6 +112,17 @@
   /// CHECK:                 cmp w{{\d+}}, #0x0
   /// CHECK:                 and w{{\d+}}, w{{\d+}}, #0x1
   /// CHECK:                 cneg w{{\d+}}, w{{\d+}}, lt
+  /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntModMinus2(int) disassembly (after)
+  /// CHECK:          Rem [{{i\d+}},{{i\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shr
+  /// CHECK-NOT:      imul
+  /// CHECK:          mov
+  /// CHECK:          and
+  /// CHECK:          jz/eq
+  /// CHECK:          lea
+  /// CHECK:          test
+  /// CHECK:          cmovl/nge
   private static Integer $noinline$IntModMinus2(int v) {
     int r = v % -2;
     return r;
@@ -111,6 +133,17 @@
   /// CHECK:                 and w{{\d+}}, w{{\d+}}, #0xf
   /// CHECK:                 and w{{\d+}}, w{{\d+}}, #0xf
   /// CHECK:                 csneg w{{\d+}}, w{{\d+}}, mi
+  /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntMod16(int) disassembly (after)
+  /// CHECK:          Rem [{{i\d+}},{{i\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shr
+  /// CHECK-NOT:      imul
+  /// CHECK:          mov
+  /// CHECK:          and
+  /// CHECK:          jz/eq
+  /// CHECK:          lea
+  /// CHECK:          test
+  /// CHECK:          cmovl/nge
   private static Integer $noinline$IntMod16(int v) {
     int r = v % 16;
     return r;
@@ -121,6 +154,17 @@
   /// CHECK:                 and w{{\d+}}, w{{\d+}}, #0xf
   /// CHECK:                 and w{{\d+}}, w{{\d+}}, #0xf
   /// CHECK:                 csneg w{{\d+}}, w{{\d+}}, mi
+  /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntModMinus16(int) disassembly (after)
+  /// CHECK:          Rem [{{i\d+}},{{i\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shr
+  /// CHECK-NOT:      imul
+  /// CHECK:          mov
+  /// CHECK:          and
+  /// CHECK:          jz/eq
+  /// CHECK:          lea
+  /// CHECK:          test
+  /// CHECK:          cmovl/nge
   private static Integer $noinline$IntModMinus16(int v) {
     int r = v % -16;
     return r;
@@ -131,6 +175,17 @@
   /// CHECK:                 and w{{\d+}}, w{{\d+}}, #0x7fffffff
   /// CHECK:                 and w{{\d+}}, w{{\d+}}, #0x7fffffff
   /// CHECK:                 csneg w{{\d+}}, w{{\d+}}, mi
+  /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntModIntMin(int) disassembly (after)
+  /// CHECK:          Rem [{{i\d+}},{{i\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shr
+  /// CHECK-NOT:      imul
+  /// CHECK:          mov
+  /// CHECK:          and
+  /// CHECK:          jz/eq
+  /// CHECK:          lea
+  /// CHECK:          test
+  /// CHECK:          cmovl/nge
   private static Integer $noinline$IntModIntMin(int v) {
     int r = v % Integer.MIN_VALUE;
     return r;
@@ -211,6 +266,18 @@
   /// CHECK:                 cmp x{{\d+}}, #0x0
   /// CHECK:                 and x{{\d+}}, x{{\d+}}, #0x1
   /// CHECK:                 cneg x{{\d+}}, x{{\d+}}, lt
+  /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongMod2(long) disassembly (after)
+  /// CHECK:          Rem [{{j\d+}},{{j\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shrq
+  /// CHECK-NOT:      imulq
+  /// CHECK:          movq
+  /// CHECK:          andq
+  /// CHECK:          jz/eq
+  /// CHECK:          movq
+  /// CHECK:          sarq
+  /// CHECK:          shlq
+  /// CHECK:          orq
   private static Long $noinline$LongMod2(long v) {
     long r = v % 2;
     return r;
@@ -220,6 +287,18 @@
   /// CHECK:                 cmp x{{\d+}}, #0x0
   /// CHECK:                 and x{{\d+}}, x{{\d+}}, #0x1
   /// CHECK:                 cneg x{{\d+}}, x{{\d+}}, lt
+  /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongModMinus2(long) disassembly (after)
+  /// CHECK:          Rem [{{j\d+}},{{j\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shrq
+  /// CHECK-NOT:      imulq
+  /// CHECK:          movq
+  /// CHECK:          andq
+  /// CHECK:          jz/eq
+  /// CHECK:          movq
+  /// CHECK:          sarq
+  /// CHECK:          shlq
+  /// CHECK:          orq
   private static Long $noinline$LongModMinus2(long v) {
     long r = v % -2;
     return r;
@@ -230,6 +309,19 @@
   /// CHECK:                 and x{{\d+}}, x{{\d+}}, #0xf
   /// CHECK:                 and x{{\d+}}, x{{\d+}}, #0xf
   /// CHECK:                 csneg x{{\d+}}, x{{\d+}}, mi
+
+  /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongMod16(long) disassembly (after)
+  /// CHECK:          Rem [{{j\d+}},{{j\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shrq
+  /// CHECK-NOT:      imulq
+  /// CHECK:          movq
+  /// CHECK:          andq
+  /// CHECK:          jz/eq
+  /// CHECK:          movq
+  /// CHECK:          sarq
+  /// CHECK:          shlq
+  /// CHECK:          orq
   private static Long $noinline$LongMod16(long v) {
     long r = v % 16;
     return r;
@@ -240,6 +332,18 @@
   /// CHECK:                 and x{{\d+}}, x{{\d+}}, #0xf
   /// CHECK:                 and x{{\d+}}, x{{\d+}}, #0xf
   /// CHECK:                 csneg x{{\d+}}, x{{\d+}}, mi
+  /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongModMinus16(long) disassembly (after)
+  /// CHECK:          Rem [{{j\d+}},{{j\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shrq
+  /// CHECK-NOT:      imulq
+  /// CHECK:          movq
+  /// CHECK:          andq
+  /// CHECK:          jz/eq
+  /// CHECK:          movq
+  /// CHECK:          sarq
+  /// CHECK:          shlq
+  /// CHECK:          orq
   private static Long $noinline$LongModMinus16(long v) {
     long r = v % -16;
     return r;
@@ -250,6 +354,18 @@
   /// CHECK:                 and x{{\d+}}, x{{\d+}}, #0x7fffffffffffffff
   /// CHECK:                 and x{{\d+}}, x{{\d+}}, #0x7fffffffffffffff
   /// CHECK:                 csneg x{{\d+}}, x{{\d+}}, mi
+  /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongModLongMin(long) disassembly (after)
+  /// CHECK:          Rem [{{j\d+}},{{j\d+}}]
+  /// CHECK-NOT:      imul
+  /// CHECK-NOT:      shrq
+  /// CHECK-NOT:      imulq
+  /// CHECK:          movq
+  /// CHECK:          andq
+  /// CHECK:          jz/eq
+  /// CHECK:          movq
+  /// CHECK:          sarq
+  /// CHECK:          shlq
+  /// CHECK:          orq
   private static Long $noinline$LongModLongMin(long v) {
     long r = v % Long.MIN_VALUE;
     return r;
diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java
index f83c924..f39b5e2 100644
--- a/test/527-checker-array-access-split/src/Main.java
+++ b/test/527-checker-array-access-split/src/Main.java
@@ -572,6 +572,75 @@
     buf1[end] = 'n';
   }
 
+  //
+  // Check that IntermediateAddress can be shared for object ArrayGets.
+  //
+  /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) instruction_simplifier_arm64 (before)
+  /// CHECK: <<Parameter:l\d+>>     ParameterValue
+  /// CHECK: <<Array:l\d+>>         NullCheck [<<Parameter>>]
+  /// CHECK:                        ArrayGet [<<Array>>,{{i\d+}}]
+  /// CHECK:                        ArrayGet [<<Array>>,{{i\d+}}]
+  /// CHECK:                        ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+  /// CHECK:                        ArrayGet [<<Array>>,{{i\d+}}]
+  /// CHECK:                        ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+  /// CHECK:                        ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+
+  /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) instruction_simplifier_arm64 (after)
+  /// CHECK: <<Parameter:l\d+>>     ParameterValue
+  /// CHECK: <<DataOffset:i\d+>>    IntConstant 12
+  /// CHECK: <<Array:l\d+>>         NullCheck [<<Parameter>>]
+  /// CHECK: <<IntAddr1:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
+  /// CHECK:                        ArrayGet [<<IntAddr1>>,{{i\d+}}]
+  /// CHECK: <<IntAddr2:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
+  /// CHECK:                        ArrayGet [<<IntAddr2>>,{{i\d+}}]
+  /// CHECK:                        ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+  /// CHECK: <<IntAddr3:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
+  /// CHECK:                        ArrayGet [<<IntAddr3>>,{{i\d+}}]
+  /// CHECK:                        ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+  /// CHECK:                        ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+  //
+  /// CHECK-NOT:                    IntermediateAddress
+
+  /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) GVN$after_arch (after)
+  /// CHECK: <<Parameter:l\d+>>     ParameterValue
+  /// CHECK: <<DataOffset:i\d+>>    IntConstant 12
+  /// CHECK: <<Array:l\d+>>         NullCheck [<<Parameter>>]
+  /// CHECK: <<IntAddr1:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
+  /// CHECK:                        ArrayGet [<<IntAddr1>>,{{i\d+}}]
+  /// CHECK:                        ArrayGet [<<IntAddr1>>,{{i\d+}}]
+  /// CHECK:                        ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+  /// CHECK: <<IntAddr3:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
+  /// CHECK:                        ArrayGet [<<IntAddr3>>,{{i\d+}}]
+  /// CHECK:                        ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+  /// CHECK:                        ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+  //
+  /// CHECK-NOT:                    IntermediateAddress
+  public final static int checkObjectArrayGet(int index, Integer[] a, Integer[] b) {
+    Integer five = Integer.valueOf(5);
+    int tmp1 = a[index];
+    tmp1 += a[index + 1];
+    a[index + 1] = five;
+    tmp1 += a[index + 2];
+    a[index + 2] = five;
+    a[index + 3] = five;
+    return tmp1;
+  }
+
+  /// CHECK-START-ARM64: int Main.testIntAddressObjDisasm(java.lang.Integer[], int) disassembly (after)
+  /// CHECK: <<IntAddr:i\d+>>       IntermediateAddress
+  /// CHECK:                          add w<<AddrReg:\d+>>, {{w\d+}}, #0xc
+  /// CHECK:                        ArrayGet [<<IntAddr>>,{{i\d+}}]
+  /// CHECK:                          ldr {{w\d+}}, [x<<AddrReg>>, x{{\d+}}, lsl #2]
+  /// CHECK:                        ArrayGet [<<IntAddr>>,{{i\d+}}]
+  /// CHECK:                          ldr {{w\d+}}, [x<<AddrReg>>, x{{\d+}}, lsl #2]
+
+  /// CHECK-START-ARM64: int Main.testIntAddressObjDisasm(java.lang.Integer[], int) disassembly (after)
+  /// CHECK:                          add {{w\d+}}, {{w\d+}}, #0xc
+  /// CHECK-NOT:                      add {{w\d+}}, {{w\d+}}, #0xc
+  private int testIntAddressObjDisasm(Integer[] obj, int x) {
+    return obj[x] + obj[x + 1];
+  }
+
   public final static int ARRAY_SIZE = 128;
 
   public static void main(String[] args) {
diff --git a/test/552-checker-x86-avx2-bit-manipulation/expected.txt b/test/552-checker-x86-avx2-bit-manipulation/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/552-checker-x86-avx2-bit-manipulation/expected.txt
diff --git a/test/552-checker-x86-avx2-bit-manipulation/info.txt b/test/552-checker-x86-avx2-bit-manipulation/info.txt
new file mode 100644
index 0000000..37bc6dd
--- /dev/null
+++ b/test/552-checker-x86-avx2-bit-manipulation/info.txt
@@ -0,0 +1 @@
+Tests for generating bit manipulation instructions on x86
diff --git a/test/552-checker-x86-avx2-bit-manipulation/src/Main.java b/test/552-checker-x86-avx2-bit-manipulation/src/Main.java
new file mode 100644
index 0000000..b8138dd
--- /dev/null
+++ b/test/552-checker-x86-avx2-bit-manipulation/src/Main.java
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+  public static void assertIntEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void assertLongEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  /// CHECK-START-X86_64: long Main.and_not_64(long, long) instruction_simplifier_x86_64 (before)
+  /// CHECK-DAG:    Phi     loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:    Not     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Not     loop:none
+  /// CHECK-DAG:    And     loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: long Main.and_not_64(long, long) instruction_simplifier_x86_64 (after)
+  // CHECK-DAG:      X86AndNot loop:<<Loop:B\d+>> outer_loop:none
+  // CHECK-DAG:      X86AndNot loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: long Main.and_not_64(long, long) instruction_simplifier_x86_64 (after)
+  // CHECK-NOT:      Not       loop:<<Loop>>      outer_loop:none
+  // CHECK-NOT:      And       loop:<<Loop>>      outer_loop:none
+  // CHECK-NOT:      Not       loop:none
+  // CHECK-NOT:      And       loop:none
+  public static long and_not_64( long x, long y) {
+    long j = 1;
+    long k = 2;
+    for (long i = -64 ; i < 64; i++ ) {
+      x = x & ~i;
+      y = y | i;
+    }
+    return x & ~y;
+  }
+
+  /// CHECK-START-X86_64: int Main.and_not_32(int, int) instruction_simplifier_x86_64 (before)
+  /// CHECK-DAG:    Phi     loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:    Not     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Not     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Not     loop:none
+  /// CHECK-DAG:    And     loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: int Main.and_not_32(int, int) instruction_simplifier_x86_64 (after)
+  // CHECK-DAG:      X86AndNot loop:<<Loop:B\d+>> outer_loop:none
+  // CHECK-DAG:      X86AndNot loop:<<Loop>>      outer_loop:none
+  // CHECK-DAG:      X86AndNot loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: int Main.and_not_32(int, int) instruction_simplifier_x86_64 (after)
+  // CHECK-NOT:      Not       loop:<<Loop>>      outer_loop:none
+  // CHECK-NOT:      And       loop:<<Loop>>      outer_loop:none
+  // CHECK-NOT:      Not       loop:none
+  // CHECK-NOT:      And       loop:none
+  public static int and_not_32( int x, int y) {
+    int j = 1;
+    int k = 2;
+    for (int i = -64 ; i < 64; i++ ) {
+      x = x & ~i;
+      y = y | i;
+    }
+    return x & ~y;
+  }
+
+  /// CHECK-START-X86_64: int Main.reset_lowest_set_bit_32(int) instruction_simplifier_x86_64 (before)
+  /// CHECK-DAG:    Phi     loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:    Add     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Add     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Add     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Add     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Add     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Add     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Add     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Add     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: int Main.reset_lowest_set_bit_32(int) instruction_simplifier_x86_64 (after)
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop:B\d+>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: int Main.reset_lowest_set_bit_32(int) instruction_simplifier_x86_64 (after)
+  // CHECK-NOT:      And                        loop:<<Loop>> outer_loop:none
+  public static int reset_lowest_set_bit_32(int x) {
+    int y = x;
+    int j = 5;
+    int k = 10;
+    int l = 20;
+    for (int i = -64 ; i < 64; i++) {
+      y = i & i-1;
+      j += y;
+      j = j & j-1;
+      k +=j;
+      k = k & k-1;
+      l +=k;
+      l = l & l-1;
+    }
+    return y + j + k + l;
+  }
+
+  /// CHECK-START-X86_64: long Main.reset_lowest_set_bit_64(long) instruction_simplifier_x86_64 (before)
+  /// CHECK-DAG:    Phi     loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:    Sub     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Sub     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Sub     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    Sub     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:    And     loop:<<Loop>>      outer_loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: long Main.reset_lowest_set_bit_64(long) instruction_simplifier_x86_64 (after)
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop:B\d+>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:<<Loop>> outer_loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: long Main.reset_lowest_set_bit_64(long) instruction_simplifier_x86_64 (after)
+  // CHECK-NOT:      And                        loop:<<Loop>> outer_loop:none
+  // CHECK-NOT:      Sub                        loop:<<Loop>> outer_loop:none
+  public static long reset_lowest_set_bit_64(long x) {
+    long y = x;
+    long j = 5;
+    long k = 10;
+    long l = 20;
+    for (long i = -64 ; i < 64; i++) {
+      y = i & i-1;
+      j += y;
+      j = j & j-1;
+      k +=j;
+      k = k & k-1;
+      l +=k;
+      l = l & l-1;
+    }
+    return y + j + k + l;
+  }
+
+  /// CHECK-START-X86_64: int Main.get_mask_lowest_set_bit_32(int) instruction_simplifier_x86_64 (before)
+  /// CHECK-DAG:    Add     loop:none
+  /// CHECK-DAG:    Xor     loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: int Main.get_mask_lowest_set_bit_32(int) instruction_simplifier_x86_64 (after)
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: int Main.get_mask_lowest_set_bit_32(int) instruction_simplifier_x86_64 (after)
+  // CHECK-NOT:      Add    loop:none
+  // CHECK-NOT:      Xor    loop:none
+  public static int get_mask_lowest_set_bit_32(int x) {
+    return (x-1) ^ x;
+  }
+
+  /// CHECK-START-X86_64: long Main.get_mask_lowest_set_bit_64(long) instruction_simplifier_x86_64 (before)
+  /// CHECK-DAG:    Sub     loop:none
+  /// CHECK-DAG:    Xor     loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: long Main.get_mask_lowest_set_bit_64(long) instruction_simplifier_x86_64 (after)
+  // CHECK-DAG:      X86MaskOrResetLeastSetBit  loop:none
+
+  // TODO:re-enable when checker supports isa features
+  // CHECK-START-X86_64: long Main.get_mask_lowest_set_bit_64(long) instruction_simplifier_x86_64 (after)
+  // CHECK-NOT:      Sub    loop:none
+  // CHECK-NOT:      Xor    loop:none
+  public static long get_mask_lowest_set_bit_64(long x) {
+    return (x-1) ^ x;
+  }
+
+  public static void main(String[] args) {
+    int x = 50;
+    int y = x/2;
+    long a = Long.MAX_VALUE;
+    long b = Long.MAX_VALUE/2;
+    assertIntEquals(0,and_not_32(x,y));
+    assertLongEquals(0L, and_not_64(a,b));
+    assertIntEquals(-20502606, reset_lowest_set_bit_32(x));
+    assertLongEquals(-20502606L, reset_lowest_set_bit_64(a));
+    assertLongEquals(-20502606L, reset_lowest_set_bit_64(b));
+    assertIntEquals(1, get_mask_lowest_set_bit_32(y));
+    assertLongEquals(1L, get_mask_lowest_set_bit_64(b));
+  }
+}
diff --git a/test/580-crc32/expected.txt b/test/580-crc32/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/580-crc32/expected.txt
diff --git a/test/580-crc32/info.txt b/test/580-crc32/info.txt
new file mode 100644
index 0000000..24f31e0
--- /dev/null
+++ b/test/580-crc32/info.txt
@@ -0,0 +1 @@
+This test case is used to test java.util.zip.CRC32.
diff --git a/test/580-crc32/src/Main.java b/test/580-crc32/src/Main.java
new file mode 100644
index 0000000..7fc1273
--- /dev/null
+++ b/test/580-crc32/src/Main.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.zip.CRC32;
+
+/**
+ * The ART compiler can use intrinsics for the java.util.zip.CRC32 method:
+ *    private native static int update(int crc, int b)
+ *
+ * As the method is private it is not possible to check the use of intrinsics
+ * for it directly.
+ * The tests check that correct checksums are produced.
+ */
+public class Main {
+  private static CRC32 crc32 = new CRC32();
+
+  public Main() {
+  }
+
+  public static long TestInt(int value) {
+    crc32.reset();
+    crc32.update(value);
+    return crc32.getValue();
+  }
+
+  public static long TestInt(int... values) {
+    crc32.reset();
+    for (int value : values) {
+      crc32.update(value);
+    }
+    return crc32.getValue();
+  }
+
+  public static void assertEqual(long expected, long actual) {
+    if (expected != actual) {
+      throw new Error("Expected: " + expected + ", found: " + actual);
+    }
+  }
+
+  public static void main(String args[]) {
+    // public void update(int b)
+    //
+    // Tests for checksums of the byte 0x0
+    assertEqual(0xD202EF8DL, TestInt(0x0));
+    assertEqual(0xD202EF8DL, TestInt(0x0100));
+    assertEqual(0xD202EF8DL, TestInt(0x010000));
+    assertEqual(0xD202EF8DL, TestInt(0x01000000));
+    assertEqual(0xD202EF8DL, TestInt(0xff00));
+    assertEqual(0xD202EF8DL, TestInt(0xffff00));
+    assertEqual(0xD202EF8DL, TestInt(0xffffff00));
+    assertEqual(0xD202EF8DL, TestInt(0x1200));
+    assertEqual(0xD202EF8DL, TestInt(0x123400));
+    assertEqual(0xD202EF8DL, TestInt(0x12345600));
+    assertEqual(0xD202EF8DL, TestInt(Integer.MIN_VALUE));
+
+    // Tests for checksums of the byte 0x1
+    assertEqual(0xA505DF1BL, TestInt(0x1));
+    assertEqual(0xA505DF1BL, TestInt(0x0101));
+    assertEqual(0xA505DF1BL, TestInt(0x010001));
+    assertEqual(0xA505DF1BL, TestInt(0x01000001));
+    assertEqual(0xA505DF1BL, TestInt(0xff01));
+    assertEqual(0xA505DF1BL, TestInt(0xffff01));
+    assertEqual(0xA505DF1BL, TestInt(0xffffff01));
+    assertEqual(0xA505DF1BL, TestInt(0x1201));
+    assertEqual(0xA505DF1BL, TestInt(0x123401));
+    assertEqual(0xA505DF1BL, TestInt(0x12345601));
+
+    // Tests for checksums of the byte 0x0f
+    assertEqual(0x42BDF21CL, TestInt(0x0f));
+    assertEqual(0x42BDF21CL, TestInt(0x010f));
+    assertEqual(0x42BDF21CL, TestInt(0x01000f));
+    assertEqual(0x42BDF21CL, TestInt(0x0100000f));
+    assertEqual(0x42BDF21CL, TestInt(0xff0f));
+    assertEqual(0x42BDF21CL, TestInt(0xffff0f));
+    assertEqual(0x42BDF21CL, TestInt(0xffffff0f));
+    assertEqual(0x42BDF21CL, TestInt(0x120f));
+    assertEqual(0x42BDF21CL, TestInt(0x12340f));
+    assertEqual(0x42BDF21CL, TestInt(0x1234560f));
+
+    // Tests for checksums of the byte 0xff
+    assertEqual(0xFF000000L, TestInt(0x00ff));
+    assertEqual(0xFF000000L, TestInt(0x01ff));
+    assertEqual(0xFF000000L, TestInt(0x0100ff));
+    assertEqual(0xFF000000L, TestInt(0x010000ff));
+    assertEqual(0xFF000000L, TestInt(0x0000ffff));
+    assertEqual(0xFF000000L, TestInt(0x00ffffff));
+    assertEqual(0xFF000000L, TestInt(0xffffffff));
+    assertEqual(0xFF000000L, TestInt(0x12ff));
+    assertEqual(0xFF000000L, TestInt(0x1234ff));
+    assertEqual(0xFF000000L, TestInt(0x123456ff));
+    assertEqual(0xFF000000L, TestInt(Integer.MAX_VALUE));
+
+    // Tests for sequences
+    assertEqual(0xFF41D912L, TestInt(0, 0, 0));
+    assertEqual(0xFF41D912L, TestInt(0x0100, 0x010000, 0x01000000));
+    assertEqual(0xFF41D912L, TestInt(0xff00, 0xffff00, 0xffffff00));
+    assertEqual(0xFF41D912L, TestInt(0x1200, 0x123400, 0x12345600));
+
+    assertEqual(0x909FB2F2L, TestInt(1, 1, 1));
+    assertEqual(0x909FB2F2L, TestInt(0x0101, 0x010001, 0x01000001));
+    assertEqual(0x909FB2F2L, TestInt(0xff01, 0xffff01, 0xffffff01));
+    assertEqual(0x909FB2F2L, TestInt(0x1201, 0x123401, 0x12345601));
+
+    assertEqual(0xE33A9F71L, TestInt(0x0f, 0x0f, 0x0f));
+    assertEqual(0xE33A9F71L, TestInt(0x010f, 0x01000f, 0x0100000f));
+    assertEqual(0xE33A9F71L, TestInt(0xff0f, 0xffff0f, 0xffffff0f));
+    assertEqual(0xE33A9F71L, TestInt(0x120f, 0x12340f, 0x1234560f));
+
+    assertEqual(0xFFFFFF00L, TestInt(0x0ff, 0x0ff, 0x0ff));
+    assertEqual(0xFFFFFF00L, TestInt(0x01ff, 0x0100ff, 0x010000ff));
+    assertEqual(0xFFFFFF00L, TestInt(0x00ffff, 0x00ffffff, 0xffffffff));
+    assertEqual(0xFFFFFF00L, TestInt(0x12ff, 0x1234ff, 0x123456ff));
+
+    assertEqual(0xB6CC4292L, TestInt(0x01, 0x02));
+
+    assertEqual(0xB2DE047CL, TestInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE));
+  }
+}
diff --git a/test/624-checker-stringops/smali/Smali.smali b/test/624-checker-stringops/smali/Smali.smali
index 8600a0a..f8b9275 100644
--- a/test/624-checker-stringops/smali/Smali.smali
+++ b/test/624-checker-stringops/smali/Smali.smali
@@ -34,7 +34,7 @@
     invoke-virtual {v0, v1}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
     move-result-object v1
 
-    const-string v2, "x"
+    const-string v2, "y"
     invoke-virtual {v1, v2}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
     move-result-object v1
 
@@ -70,7 +70,7 @@
     invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
     move-result-object v1
 
-    const-string v2, "x"
+    const-string v2, "y"
     invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
     move-result-object v1
 
diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java
index 3aa6e56..f52d81a 100644
--- a/test/624-checker-stringops/src/Main.java
+++ b/test/624-checker-stringops/src/Main.java
@@ -120,7 +120,7 @@
   /// CHECK-DAG:                  InvokeVirtual [<<New>>]             intrinsic:StringBufferLength
   static int bufferLen2() {
     StringBuffer s = new StringBuffer();
-    return s.append("x").append("x").length();
+    return s.append("x").append("y").length();
   }
 
   static int bufferLen2Smali() throws Exception {
@@ -150,7 +150,7 @@
   /// CHECK-DAG:                  InvokeVirtual [<<New>>]             intrinsic:StringBuilderLength
   static int builderLen2() {
     StringBuilder s = new StringBuilder();
-    return s.append("x").append("x").length();
+    return s.append("x").append("y").length();
   }
 
   static int builderLen2Smali() throws Exception {
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 96754c3..8e3e4eb 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -28,9 +28,9 @@
 
 extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
   Runtime* runtime = Runtime::Current();
-  runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
+  runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+  runtime->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
   runtime->SetDedupeHiddenApiWarnings(false);
-  runtime->AlwaysSetHiddenApiWarningFlag();
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
@@ -287,13 +287,5 @@
   return static_cast<jint>(kAccHiddenApiBits);
 }
 
-extern "C" JNIEXPORT jboolean JNICALL Java_ChildClass_hasPendingWarning(JNIEnv*, jclass) {
-  return Runtime::Current()->HasPendingHiddenApiWarning();
-}
-
-extern "C" JNIEXPORT void JNICALL Java_ChildClass_clearWarning(JNIEnv*, jclass) {
-  Runtime::Current()->SetPendingHiddenApiWarning(false);
-}
-
 }  // namespace Test674HiddenApi
 }  // namespace art
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index db3ba6d..3427b8e 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -98,10 +98,8 @@
         expected = Behaviour.Granted;
       } else if (hiddenness == Hiddenness.Blacklist) {
         expected = Behaviour.Denied;
-      } else if (isDebuggable) {
-        expected = Behaviour.Warning;
       } else {
-        expected = Behaviour.Granted;
+        expected = Behaviour.Warning;
       }
 
       for (boolean isStatic : booleanValues) {
@@ -145,7 +143,7 @@
   }
 
   private static void checkMemberCallback(Class<?> klass, String name,
-          boolean isPublic, boolean isField) {
+          boolean isPublic, boolean isField, boolean expectedCallback) {
       try {
           RecordingConsumer consumer = new RecordingConsumer();
           VMRuntime.setNonSdkApiUsageConsumer(consumer);
@@ -168,8 +166,14 @@
               // only interested in whether the callback is invoked.
           }
 
-          if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) {
-              throw new RuntimeException("No callback for member: " + name);
+          boolean actualCallback = consumer.recordedValue != null &&
+                          consumer.recordedValue.contains(name);
+          if (expectedCallback != actualCallback) {
+              if (expectedCallback) {
+                throw new RuntimeException("Expected callback for member: " + name);
+              } else {
+                throw new RuntimeException("Did not expect callback for member: " + name);
+              }
           }
       } finally {
           VMRuntime.setNonSdkApiUsageConsumer(null);
@@ -181,7 +185,7 @@
 
     boolean isPublic = (visibility == Visibility.Public);
     boolean canDiscover = (behaviour != Behaviour.Denied);
-    boolean setsWarning = (behaviour == Behaviour.Warning);
+    boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
 
     if (klass.isInterface() && (!isStatic || !isPublic)) {
       // Interfaces only have public static fields.
@@ -243,8 +247,6 @@
                               canDiscover);
     }
 
-    // Finish here if we could not discover the field.
-
     if (canDiscover) {
       // Test that modifiers are unaffected.
 
@@ -254,44 +256,22 @@
 
       // Test getters and setters when meaningful.
 
-      clearWarning();
       if (!Reflection.canGetField(klass, name)) {
         throwAccessException(klass, name, true, "Field.getInt()");
       }
-      if (hasPendingWarning() != setsWarning) {
-        throwWarningException(klass, name, true, "Field.getInt()", setsWarning);
-      }
-
-      clearWarning();
       if (!Reflection.canSetField(klass, name)) {
         throwAccessException(klass, name, true, "Field.setInt()");
       }
-      if (hasPendingWarning() != setsWarning) {
-        throwWarningException(klass, name, true, "Field.setInt()", setsWarning);
-      }
-
-      clearWarning();
       if (!JNI.canGetField(klass, name, isStatic)) {
         throwAccessException(klass, name, true, "getIntField");
       }
-      if (hasPendingWarning() != setsWarning) {
-        throwWarningException(klass, name, true, "getIntField", setsWarning);
-      }
-
-      clearWarning();
       if (!JNI.canSetField(klass, name, isStatic)) {
         throwAccessException(klass, name, true, "setIntField");
       }
-      if (hasPendingWarning() != setsWarning) {
-        throwWarningException(klass, name, true, "setIntField", setsWarning);
-      }
     }
 
     // Test that callbacks are invoked correctly.
-    clearWarning();
-    if (setsWarning || !canDiscover) {
-      checkMemberCallback(klass, name, isPublic, true /* isField */);
-    }
+    checkMemberCallback(klass, name, isPublic, true /* isField */, invokesMemberCallback);
   }
 
   private static void checkMethod(Class<?> klass, String name, boolean isStatic,
@@ -304,7 +284,7 @@
     }
 
     boolean canDiscover = (behaviour != Behaviour.Denied);
-    boolean setsWarning = (behaviour == Behaviour.Warning);
+    boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
 
     // Test discovery with reflection.
 
@@ -354,39 +334,21 @@
       }
 
       // Test whether we can invoke the method. This skips non-static interface methods.
-
       if (!klass.isInterface() || isStatic) {
-        clearWarning();
         if (!Reflection.canInvokeMethod(klass, name)) {
           throwAccessException(klass, name, false, "invoke()");
         }
-        if (hasPendingWarning() != setsWarning) {
-          throwWarningException(klass, name, false, "invoke()", setsWarning);
-        }
-
-        clearWarning();
         if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
           throwAccessException(klass, name, false, "CallMethodA");
         }
-        if (hasPendingWarning() != setsWarning) {
-          throwWarningException(klass, name, false, "CallMethodA()", setsWarning);
-        }
-
-        clearWarning();
         if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
           throwAccessException(klass, name, false, "CallMethodV");
         }
-        if (hasPendingWarning() != setsWarning) {
-          throwWarningException(klass, name, false, "CallMethodV()", setsWarning);
-        }
       }
     }
 
     // Test that callbacks are invoked correctly.
-    clearWarning();
-    if (setsWarning || !canDiscover) {
-        checkMemberCallback(klass, name, isPublic, false /* isField */);
-    }
+    checkMemberCallback(klass, name, isPublic, false /* isField */, invokesMemberCallback);
   }
 
   private static void checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness,
@@ -403,7 +365,6 @@
     MethodType methodType = MethodType.methodType(void.class, args);
 
     boolean canDiscover = (behaviour != Behaviour.Denied);
-    boolean setsWarning = (behaviour == Behaviour.Warning);
 
     // Test discovery with reflection.
 
@@ -446,70 +407,41 @@
                               canDiscover);
     }
 
-    // Finish here if we could not discover the constructor.
+    if (canDiscover) {
+      // Test whether we can invoke the constructor.
 
-    if (!canDiscover) {
-      return;
-    }
-
-    // Test whether we can invoke the constructor.
-
-    clearWarning();
-    if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
-      throwAccessException(klass, fullName, false, "invoke()");
-    }
-    if (hasPendingWarning() != setsWarning) {
-      throwWarningException(klass, fullName, false, "invoke()", setsWarning);
-    }
-
-    clearWarning();
-    if (!JNI.canInvokeConstructorA(klass, signature)) {
-      throwAccessException(klass, fullName, false, "NewObjectA");
-    }
-    if (hasPendingWarning() != setsWarning) {
-      throwWarningException(klass, fullName, false, "NewObjectA", setsWarning);
-    }
-
-    clearWarning();
-    if (!JNI.canInvokeConstructorV(klass, signature)) {
-      throwAccessException(klass, fullName, false, "NewObjectV");
-    }
-    if (hasPendingWarning() != setsWarning) {
-      throwWarningException(klass, fullName, false, "NewObjectV", setsWarning);
+      if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
+        throwAccessException(klass, fullName, false, "invoke()");
+      }
+      if (!JNI.canInvokeConstructorA(klass, signature)) {
+        throwAccessException(klass, fullName, false, "NewObjectA");
+      }
+      if (!JNI.canInvokeConstructorV(klass, signature)) {
+        throwAccessException(klass, fullName, false, "NewObjectV");
+      }
     }
   }
 
   private static void checkNullaryConstructor(Class<?> klass, Behaviour behaviour)
       throws Exception {
     boolean canAccess = (behaviour != Behaviour.Denied);
-    boolean setsWarning = (behaviour == Behaviour.Warning);
 
-    clearWarning();
     if (Reflection.canUseNewInstance(klass) != canAccess) {
       throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
           "be able to construct " + klass.getName() + ". " +
           "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
     }
-    if (canAccess && hasPendingWarning() != setsWarning) {
-      throwWarningException(klass, "nullary constructor", false, "newInstance", setsWarning);
-    }
   }
 
   private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour)
       throws Exception {
     boolean canAccess = (behaviour != Behaviour.Denied);
-    boolean setsWarning = (behaviour == Behaviour.Warning);
 
-    clearWarning();
     if (Linking.canAccess(className, takesParameter) != canAccess) {
       throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
           "be able to verify " + className + "." +
           "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
     }
-    if (canAccess && hasPendingWarning() != setsWarning) {
-      throwWarningException(
-          Class.forName(className), "access", false, "static linking", setsWarning);
-    }
   }
 
   private static void throwDiscoveryException(Class<?> klass, String name, boolean isField,
@@ -528,15 +460,6 @@
         "everythingWhitelisted = " + everythingWhitelisted);
   }
 
-  private static void throwWarningException(Class<?> klass, String name, boolean isField,
-      String fn, boolean setsWarning) {
-    throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
-        klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
-        "set the warning flag. " +
-        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
-        "everythingWhitelisted = " + everythingWhitelisted);
-  }
-
   private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
     throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
         "." + name + " to not expose hidden modifiers");
@@ -545,7 +468,4 @@
   private static boolean isParentInBoot;
   private static boolean isChildInBoot;
   private static boolean everythingWhitelisted;
-
-  private static native boolean hasPendingWarning();
-  private static native void clearWarning();
 }
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index b0a400a..8dd49aa 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -21,7 +21,7 @@
  baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
  bar (IIILart/ControlData;)J 0 26
  foo (IIILart/ControlData;)I 0 21
- doTest ()V 33 25
+ doTest ()V 31 25
  run ()V 0 25
 ---------
  print (Ljava/lang/Thread;II)V 0 38
@@ -41,7 +41,7 @@
  baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
  bar (IIILart/ControlData;)J 0 26
  foo (IIILart/ControlData;)I 0 21
- doTest ()V 37 26
+ doTest ()V 35 26
  run ()V 0 25
 ---------
  getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
@@ -62,7 +62,7 @@
  baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
  bar (IIILart/ControlData;)J 0 26
  foo (IIILart/ControlData;)I 0 21
- doTest ()V 60 32
+ doTest ()V 58 32
  run ()V 0 25
 ---------
  bar (IIILart/ControlData;)J 0 26
@@ -388,7 +388,7 @@
 Test911
  getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
  printAll (I)V 0 75
- doTest ()V 120 59
+ doTest ()V 118 59
  run ()V 24 37
 
 ---------
@@ -643,7 +643,7 @@
 Test911
  getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
  printAll (I)V 0 75
- doTest ()V 125 61
+ doTest ()V 123 61
  run ()V 24 37
 
 ---------
@@ -675,7 +675,7 @@
 Test911
  getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
  printList ([Ljava/lang/Thread;I)V 0 68
- doTest ()V 110 54
+ doTest ()V 108 54
  run ()V 32 41
 
 ---------
@@ -732,7 +732,7 @@
 Test911
  getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
  printList ([Ljava/lang/Thread;I)V 0 68
- doTest ()V 115 56
+ doTest ()V 113 56
  run ()V 32 41
 
 ---------
@@ -857,7 +857,7 @@
 4
 JVMTI_ERROR_ILLEGAL_ARGUMENT
 [public static native java.lang.Object[] art.Frames.getFrameLocation(java.lang.Thread,int), ffffffff]
-[public static void art.Frames.doTestSameThread(), 40]
+[public static void art.Frames.doTestSameThread(), 3e]
 [public static void art.Frames.doTest() throws java.lang.Exception, 0]
 [public void art.Test911$1.run(), 28]
 JVMTI_ERROR_NO_MORE_FRAMES
diff --git a/test/911-get-stack-trace/src/art/PrintThread.java b/test/911-get-stack-trace/src/art/PrintThread.java
index d8b3cbc..798db06 100644
--- a/test/911-get-stack-trace/src/art/PrintThread.java
+++ b/test/911-get-stack-trace/src/art/PrintThread.java
@@ -42,7 +42,7 @@
   // may not exist depending on the environment.
   public final static String IGNORE_THREAD_NAME_REGEX =
       "Binder:|RenderThread|hwuiTask|Jit thread pool worker|Instr:|JDWP|Profile Saver|main|" +
-      "queued-work-looper|InstrumentationConnectionThread";
+      "queued-work-looper|InstrumentationConnectionThread|intel_svc_streamer_thread";
   public final static Matcher IGNORE_THREADS =
       Pattern.compile(IGNORE_THREAD_NAME_REGEX).matcher("");
 
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index 065b854..1bd56d1 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -1,9 +1,9 @@
 ---
 true true
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780000, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780004, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780001, length=-1]
@@ -44,14 +44,14 @@
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
 root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
 root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780005, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780009, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780006, length=-1]
@@ -90,18 +90,18 @@
 5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123456780005, length=-1]
 ---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 ---
 3@1001 --(class)--> 1001@0 [size=123456780011, length=-1]
 ---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 ---
 3@1001 --(class)--> 1001@0 [size=123456780016, length=-1]
 ---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123456780020, length=-1]
 3@1001 --(class)--> 1001@0 [size=123456780021, length=-1]
@@ -110,14 +110,14 @@
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
 root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
 root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123456780025, length=-1]
 3@1001 --(class)--> 1001@0 [size=123456780026, length=-1]
@@ -198,10 +198,10 @@
 ---
 ---
 ---- untagged objects
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780050, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780054, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780051, length=-1]
@@ -242,14 +242,14 @@
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
 root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
 root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780055, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780059, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780056, length=-1]
@@ -289,9 +289,9 @@
 6@1000 --(class)--> 1000@0 [size=123456780055, length=-1]
 ---
 ---- tagged classes
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780060, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780064, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780061, length=-1]
@@ -316,9 +316,9 @@
 5@1002 --(field@8)--> 500@0 [size=20, length=2]
 6@1000 --(class)--> 1000@0 [size=123456780060, length=-1]
 ---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780065, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780069, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780066, length=-1]
diff --git a/test/Android.bp b/test/Android.bp
index 561f95e..d85e2a6 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -59,8 +59,7 @@
     shared_libs: [
         "libartd",
         "libartd-disassembler",
-        "libvixld-arm",
-        "libvixld-arm64",
+        "libvixld",
         "libart-gtest",
         "libdexfiled",
         "libprofiled",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 64c1d4f..148aea4 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -59,6 +59,7 @@
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
   $(ART_HOST_EXECUTABLES) \
   $(HOST_OUT_EXECUTABLES)/hprof-conv \
+  $(HOST_OUT_EXECUTABLES)/timeout_dumper \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagent) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagentd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtistress) \
diff --git a/test/VerifySoftFailDuringClinit/ClassToInitialize.smali b/test/VerifySoftFailDuringClinit/ClassToInitialize.smali
new file mode 100644
index 0000000..0d12ec8
--- /dev/null
+++ b/test/VerifySoftFailDuringClinit/ClassToInitialize.smali
@@ -0,0 +1,22 @@
+# 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.
+
+.class public LClassToInitialize;
+.super Ljava/lang/Object;
+
+.method public static constructor <clinit>()V
+  .registers 0
+  invoke-static {}, LVerifySoftFail;->empty()V
+  return-void
+.end method
diff --git a/test/VerifySoftFailDuringClinit/VerifySoftFail.smali b/test/VerifySoftFailDuringClinit/VerifySoftFail.smali
new file mode 100644
index 0000000..e0f4946
--- /dev/null
+++ b/test/VerifySoftFailDuringClinit/VerifySoftFail.smali
@@ -0,0 +1,27 @@
+# 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.
+
+.class public LVerifySoftFail;
+.super Ljava/lang/Object;
+
+.method public static empty()V
+  .registers 0
+  return-void
+.end method
+
+.method public static softFail()V
+  .registers 0
+  invoke-static {}, LMissingClass;->test()V
+  return-void
+.end method
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 900b1d7..6488d24 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -31,6 +31,8 @@
 GDB_SERVER="gdbserver"
 HAVE_IMAGE="y"
 HOST="n"
+BIONIC="n"
+CREATE_ANDROID_ROOT="n"
 INTERPRETER="n"
 JIT="n"
 INVOKE_WITH=""
@@ -209,6 +211,13 @@
         HOST="y"
         ANDROID_ROOT="$ANDROID_HOST_OUT"
         shift
+    elif [ "x$1" = "x--bionic" ]; then
+        BIONIC="y"
+        # We need to create an ANDROID_ROOT because currently we cannot create
+        # the frameworks/libcore with linux_bionic so we need to use the normal
+        # host ones which are in a different location.
+        CREATE_ANDROID_ROOT="y"
+        shift
     elif [ "x$1" = "x--no-prebuild" ]; then
         PREBUILD="n"
         shift
@@ -374,6 +383,10 @@
     done
 fi
 
+if [ "$CREATE_ANDROID_ROOT" = "y" ]; then
+    ANDROID_ROOT=$DEX_LOCATION/android-root
+fi
+
 if [ "x$1" = "x" ] ; then
   MAIN="Main"
 else
@@ -644,6 +657,15 @@
   fi
 fi
 
+if [ "$BIONIC" = "y" ]; then
+  # This is the location that soong drops linux_bionic builds. Despite being
+  # called linux_bionic-x86 the build is actually amd64 (x86_64) only.
+  if [ ! -e "$OUT_DIR/soong/host/linux_bionic-x86" ]; then
+    echo "linux_bionic-x86 target doesn't seem to have been built!" >&2
+    exit 1
+  fi
+fi
+
 # Prevent test from silently falling back to interpreter in no-prebuild mode. This happens
 # when DEX_LOCATION path is too long, because vdex/odex filename is constructed by taking
 # full path to dex, stripping leading '/', appending '@classes.vdex' and changing every
@@ -671,6 +693,21 @@
 mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
 strip_cmdline="true"
 sync_cmdline="true"
+linkroot_cmdline="true"
+linkroot_overlay_cmdline="true"
+
+linkdirs() {
+  find "$1" -maxdepth 1 -mindepth 1 -type d | xargs -i ln -sf '{}' "$2"
+}
+
+if [ "$CREATE_ANDROID_ROOT" = "y" ]; then
+  mkdir_locations="${mkdir_locations} ${ANDROID_ROOT}"
+  linkroot_cmdline="linkdirs ${ANDROID_HOST_OUT} ${ANDROID_ROOT}"
+  if [ "${BIONIC}" = "y" ]; then
+    # TODO Make this overlay more generic.
+    linkroot_overlay_cmdline="linkdirs $OUT_DIR/soong/host/linux_bionic-x86 ${ANDROID_ROOT}"
+  fi
+fi
 
 # PROFILE takes precedence over RANDOM_PROFILE, since PROFILE tests require a
 # specific profile to run properly.
@@ -942,14 +979,15 @@
       # Note: We first send SIGRTMIN+2 (usually 36) to ART, which will induce a full thread dump
       #       before abort. However, dumping threads might deadlock, so we also use the "-k"
       #       option to definitely kill the child.
-      cmdline="timeout -k 120s -s SIGRTMIN+2 ${TIME_OUT_VALUE}s $cmdline"
+      cmdline="timeout -k 120s -s SIGRTMIN+2 ${TIME_OUT_VALUE}s timeout_dumper $cmdline"
     fi
 
     if [ "$DEV_MODE" = "y" ]; then
       for var in ANDROID_PRINTF_LOG ANDROID_DATA ANDROID_ROOT LD_LIBRARY_PATH DYLD_LIBRARY_PATH PATH LD_USE_LOAD_BIAS; do
         echo EXPORT $var=${!var}
       done
-      echo "mkdir -p ${mkdir_locations} && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
+      echo "$(declare -f linkdirs)"
+      echo "mkdir -p ${mkdir_locations} && $linkroot_cmdline && $linkroot_overlay_cmdline && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
     fi
 
     cd $ANDROID_BUILD_TOP
@@ -963,6 +1001,8 @@
     export ASAN_OPTIONS=$RUN_TEST_ASAN_OPTIONS
 
     mkdir -p ${mkdir_locations} || exit 1
+    $linkroot_cmdline || { echo "create symlink android-root failed." >&2 ; exit 2; }
+    $linkroot_overlay_cmdline || { echo "overlay android-root failed." >&2 ; exit 2; }
     $profman_cmdline || { echo "Profman failed." >&2 ; exit 2; }
     $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
     $dm_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
diff --git a/test/run-test b/test/run-test
index 229e201..2363152 100755
--- a/test/run-test
+++ b/test/run-test
@@ -88,6 +88,8 @@
     export ANDROID_HOST_OUT=${OUT_DIR}/host/linux-x86
 fi
 
+host_lib_root=${ANDROID_HOST_OUT}
+
 # Allow changing DESUGAR script to something else, or to disable it with DESUGAR=false.
 if [ -z "$DESUGAR" ]; then
   export DESUGAR="$ANDROID_BUILD_TOP/art/tools/desugar.sh"
@@ -385,6 +387,14 @@
         run_args="${run_args} --64"
         suffix64="64"
         shift
+    elif [ "x$1" = "x--bionic" ]; then
+        # soong linux_bionic builds are 64bit only.
+        run_args="${run_args} --bionic --host --64"
+        suffix64="64"
+        target_mode="no"
+        DEX_LOCATION=$tmp_dir
+        host_lib_root=$OUT_DIR/soong/host/linux_bionic-x86
+        shift
     elif [ "x$1" = "x--trace" ]; then
         trace="true"
         shift
@@ -584,7 +594,7 @@
     if [ "$target_mode" = "no" ]; then
         guess_host_arch_name
         run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}.art"
-        run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}"
+        run_args="${run_args} --runtime-option -Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}"
     else
         guess_target_arch_name
         run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}"
@@ -711,6 +721,7 @@
         echo "    --output-path [path]  Location where to store the build" \
              "files."
         echo "    --64                  Run the test in 64-bit mode"
+        echo "    --bionic              Use the (host, 64-bit only) linux_bionic libc runtime"
         echo "    --trace               Run with method tracing"
         echo "    --strace              Run with syscall tracing from strace."
         echo "    --stream              Run method tracing in streaming mode (requires --trace)"
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 14287b0..b4a4ada 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -62,6 +62,18 @@
 print(custom_env)
 os.environ.update(custom_env)
 
+# build is just a binary/script that is directly executed to build any artifacts needed for the
+# test.
+if 'build' in target:
+  build_command = target.get('build').format(
+      ANDROID_BUILD_TOP = env.ANDROID_BUILD_TOP,
+      MAKE_OPTIONS='DX=  -j{threads}'.format(threads = n_threads))
+  sys.stdout.write(str(build_command) + '\n')
+  sys.stdout.flush()
+  if subprocess.call(build_command.split()):
+    sys.exit(1)
+
+# make runs soong/kati to build the target listed in the entry.
 if 'make' in target:
   build_command = 'build/soong/soong_ui.bash --make-mode'
   build_command += ' DX='
@@ -107,7 +119,8 @@
     run_test_command += ['--host']
     run_test_command += ['--dex2oat-jobs']
     run_test_command += ['4']
-  run_test_command += ['-b']
+  if '--no-build-dependencies' not in test_flags:
+    run_test_command += ['-b']
   run_test_command += ['--verbose']
 
   sys.stdout.write(str(run_test_command) + '\n')
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 978e9cb3..077129f 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -40,6 +40,12 @@
     'art-interpreter' : {
         'run-test' : ['--interpreter']
     },
+    'art-interpreter-cxx' : {
+        'run-test' : ['--interpreter'],
+        'env' : {
+            'ART_USE_CXX_INTERPRETER' : 'true'
+        }
+    },
     'art-interpreter-access-checks' : {
         'run-test' : ['--interp-ac']
     },
@@ -323,4 +329,11 @@
     'art-golem-linux-x64': {
         'golem' : 'linux-x64'
     },
+    'art-linux-bionic-x64': {
+        'build': '{ANDROID_BUILD_TOP}/art/tools/build_linux_bionic_tests.sh {MAKE_OPTIONS}',
+        'run-test': ['--run-test-option=--bionic',
+                     '--host',
+                     '--64',
+                     '--no-build-dependencies'],
+    },
 }
diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh
new file mode 100755
index 0000000..dba2d50
--- /dev/null
+++ b/tools/build_linux_bionic_tests.sh
@@ -0,0 +1,101 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+if [[ -z $ANDROID_BUILD_TOP ]]; then
+  pushd .
+else
+  pushd $ANDROID_BUILD_TOP
+fi
+
+if [ ! -d art ]; then
+  echo "Script needs to be run at the root of the android tree"
+  exit 1
+fi
+
+source build/envsetup.sh >&/dev/null # for get_build_var
+
+out_dir=$(get_build_var OUT_DIR)
+
+# TODO(b/31559095) Figure out a better way to do this.
+#
+# There is no good way to force soong to generate host-bionic builds currently
+# so this is a hacky workaround.
+
+# First build all the targets still in .mk files (also build normal glibc host
+# targets so we know what's needed to run the tests).
+build/soong/soong_ui.bash --make-mode "$@" test-art-host-run-test-dependencies build-art-host-tests
+if [ $? != 0 ]; then
+  exit 1
+fi
+
+tmp_soong_var=$(mktemp --tmpdir soong.variables.bak.XXXXXX)
+
+echo "Saving soong.variables to " $tmp_soong_var
+cat $out_dir/soong/soong.variables > ${tmp_soong_var}
+python3 <<END - ${tmp_soong_var} ${out_dir}/soong/soong.variables
+import json
+import sys
+x = json.load(open(sys.argv[1]))
+x['Allow_missing_dependencies'] = True
+x['HostArch'] = 'x86_64'
+x['CrossHost'] = 'linux_bionic'
+x['CrossHostArch'] = 'x86_64'
+if 'CrossHostSecondaryArch' in x:
+  del x['CrossHostSecondaryArch']
+json.dump(x, open(sys.argv[2], mode='w'))
+END
+if [ $? != 0 ]; then
+  mv $tmp_soong_var $out_dir/soong/soong.variables
+  exit 2
+fi
+
+soong_out=$out_dir/soong/host/linux_bionic-x86
+declare -a bionic_targets
+# These are the binaries actually used in tests. Since some of the files are
+# java targets or 32 bit we cannot just do the same find for the bin files.
+#
+# We look at what the earlier build generated to figure out what to ask soong to
+# build since we cannot use the .mk defined phony targets.
+bionic_targets=(
+  $soong_out/bin/dalvikvm
+  $soong_out/bin/dalvikvm64
+  $soong_out/bin/dex2oat
+  $soong_out/bin/dex2oatd
+  $soong_out/bin/profman
+  $soong_out/bin/profmand
+  $soong_out/bin/hiddenapi
+  $soong_out/bin/hprof-conv
+  $(find $ANDROID_HOST_OUT/lib64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g")
+  $(find $ANDROID_HOST_OUT/nativetest64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g"))
+
+echo building ${bionic_targets[*]}
+
+build/soong/soong_ui.bash --make-mode --skip-make "$@" ${bionic_targets[*]}
+ret=$?
+
+mv $tmp_soong_var $out_dir/soong/soong.variables
+
+# Having built with host-bionic confuses soong somewhat by making it think the
+# linux_bionic targets are needed for art phony targets like
+# test-art-host-run-test-dependencies. To work around this blow away all
+# ninja files in OUT_DIR. The build system is smart enough to not need to
+# rebuild stuff so this should be fine.
+rm -f $OUT_DIR/*.ninja $OUT_DIR/soong/*.ninja
+
+popd
+
+exit $ret
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 3d70087..da463d5 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -61,7 +61,7 @@
 done
 
 # Allow to build successfully in master-art.
-extra_args=SOONG_ALLOW_MISSING_DEPENDENCIES=true
+extra_args="SOONG_ALLOW_MISSING_DEPENDENCIES=true TEMPORARY_DISABLE_PATH_RESTRICTIONS=true"
 
 if [[ $mode == "host" ]]; then
   make_command="make $j_arg $extra_args $showcommands build-art-host-tests $common_targets"
@@ -81,6 +81,8 @@
     # These targets are needed for the chroot environment.
     make_command+=" crash_dump event-log-tags"
   fi
+  # Build the Runtime APEX.
+  make_command+=" com.android.runtime"
   mode_suffix="-target"
 fi
 
@@ -92,4 +94,5 @@
 
 
 echo "Executing $make_command"
+# Disable path restrictions to enable luci builds using vpython.
 bash -c "$make_command"
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index f61b3e8..65a4945 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -813,6 +813,7 @@
     // Create a new MapItem entry with new MapList details.
     DexFile::MapItem new_item;
     new_item.type_ = old_item.type_;
+    new_item.unused_ = 0u;  // initialize to ensure dex output is deterministic (b/119308882)
     new_item.size_ = old_item.size_;
     new_item.offset_ = new_map_offset;
 
diff --git a/tools/libcore_gcstress_failures.txt b/tools/libcore_gcstress_failures.txt
index fff1c70..eec45fa 100644
--- a/tools/libcore_gcstress_failures.txt
+++ b/tools/libcore_gcstress_failures.txt
@@ -27,12 +27,14 @@
   description: "Timeouts.",
   result: EXEC_FAILED,
   modes: [device],
-  names: ["libcore.java.lang.StringTest#testFastPathString_wellFormedUtf8Sequence",
+  names: ["jsr166.TimeUnitTest#testConvert",
+          "libcore.java.lang.StringTest#testFastPathString_wellFormedUtf8Sequence",
+          "libcore.java.text.DecimalFormatTest#testCurrencySymbolSpacing",
+          "libcore.java.text.SimpleDateFormatTest#testLocales",
           "org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_remove",
           "org.apache.harmony.tests.java.text.DateFormatTest#test_getAvailableLocales",
+          "org.apache.harmony.tests.java.lang.String2Test#test_getBytes",
           "org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
-          "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext",
-          "libcore.java.text.DecimalFormatTest#testCurrencySymbolSpacing",
-          "libcore.java.text.SimpleDateFormatTest#testLocales"]
+          "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"]
 }
 ]
diff --git a/tools/timeout_dumper/Android.bp b/tools/timeout_dumper/Android.bp
new file mode 100644
index 0000000..dfd5442
--- /dev/null
+++ b/tools/timeout_dumper/Android.bp
@@ -0,0 +1,39 @@
+//
+// 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.
+//
+
+art_cc_binary {
+    name: "timeout_dumper",
+
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    device_supported: false,
+
+    defaults: ["art_defaults"],
+
+    srcs: ["timeout_dumper.cc"],
+
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+    ],
+    sanitize: {
+        address: true,
+    },
+}
diff --git a/tools/timeout_dumper/timeout_dumper.cc b/tools/timeout_dumper/timeout_dumper.cc
new file mode 100644
index 0000000..30e194c
--- /dev/null
+++ b/tools/timeout_dumper/timeout_dumper.cc
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <csignal>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <thread>
+#include <memory>
+#include <set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+namespace art {
+namespace {
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+constexpr bool kUseAddr2line = true;
+
+namespace timeout_signal {
+
+class SignalSet {
+ public:
+  SignalSet() {
+    if (sigemptyset(&set_) == -1) {
+      PLOG(FATAL) << "sigemptyset failed";
+    }
+  }
+
+  void Add(int signal) {
+    if (sigaddset(&set_, signal) == -1) {
+      PLOG(FATAL) << "sigaddset " << signal << " failed";
+    }
+  }
+
+  void Block() {
+    if (pthread_sigmask(SIG_BLOCK, &set_, nullptr) != 0) {
+      PLOG(FATAL) << "pthread_sigmask failed";
+    }
+  }
+
+  int Wait() {
+    // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures.
+    int signal_number;
+    int rc = TEMP_FAILURE_RETRY(sigwait(&set_, &signal_number));
+    if (rc != 0) {
+      PLOG(FATAL) << "sigwait failed";
+    }
+    return signal_number;
+  }
+
+ private:
+  sigset_t set_;
+};
+
+int GetTimeoutSignal() {
+  return SIGRTMIN + 2;
+}
+
+}  // namespace timeout_signal
+
+namespace addr2line {
+
+constexpr const char* kAddr2linePath =
+    "/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/bin/x86_64-linux-addr2line";
+
+std::unique_ptr<std::string> FindAddr2line() {
+  const char* env_value = getenv("ANDROID_BUILD_TOP");
+  if (env_value != nullptr) {
+    std::string path = std::string(env_value) + kAddr2linePath;
+    if (access(path.c_str(), X_OK) == 0) {
+      return std::make_unique<std::string>(path);
+    }
+  }
+
+  std::string path = std::string(".") + kAddr2linePath;
+  if (access(path.c_str(), X_OK) == 0) {
+    return std::make_unique<std::string>(path);
+  }
+
+  constexpr const char* kHostAddr2line = "/usr/bin/addr2line";
+  if (access(kHostAddr2line, F_OK) == 0) {
+    return std::make_unique<std::string>(kHostAddr2line);
+  }
+
+  return nullptr;
+}
+
+// The state of an open pipe to addr2line. In "server" mode, addr2line takes input on stdin
+// and prints the result to stdout. This struct keeps the state of the open connection.
+struct Addr2linePipe {
+  Addr2linePipe(int in_fd, int out_fd, const std::string& file_name, pid_t pid)
+      : in(in_fd), out(out_fd), file(file_name), child_pid(pid), odd(true) {}
+
+  ~Addr2linePipe() {
+    kill(child_pid, SIGKILL);
+  }
+
+  unique_fd in;      // The file descriptor that is connected to the output of addr2line.
+  unique_fd out;     // The file descriptor that is connected to the input of addr2line.
+
+  const std::string file;     // The file addr2line is working on, so that we know when to close
+                              // and restart.
+  const pid_t child_pid;      // The pid of the child, which we should kill when we're done.
+  bool odd;                   // Print state for indentation of lines.
+};
+
+std::unique_ptr<Addr2linePipe> Connect(const std::string& name, const char* args[]) {
+  int caller_to_addr2line[2];
+  int addr2line_to_caller[2];
+
+  if (pipe(caller_to_addr2line) == -1) {
+    return nullptr;
+  }
+  if (pipe(addr2line_to_caller) == -1) {
+    close(caller_to_addr2line[0]);
+    close(caller_to_addr2line[1]);
+    return nullptr;
+  }
+
+  pid_t pid = fork();
+  if (pid == -1) {
+    close(caller_to_addr2line[0]);
+    close(caller_to_addr2line[1]);
+    close(addr2line_to_caller[0]);
+    close(addr2line_to_caller[1]);
+    return nullptr;
+  }
+
+  if (pid == 0) {
+    dup2(caller_to_addr2line[0], STDIN_FILENO);
+    dup2(addr2line_to_caller[1], STDOUT_FILENO);
+
+    close(caller_to_addr2line[0]);
+    close(caller_to_addr2line[1]);
+    close(addr2line_to_caller[0]);
+    close(addr2line_to_caller[1]);
+
+    execv(args[0], const_cast<char* const*>(args));
+    exit(1);
+  } else {
+    close(caller_to_addr2line[0]);
+    close(addr2line_to_caller[1]);
+    return std::make_unique<Addr2linePipe>(addr2line_to_caller[0],
+                                           caller_to_addr2line[1],
+                                           name,
+                                           pid);
+  }
+}
+
+void WritePrefix(std::ostream& os, const char* prefix, bool odd) {
+  if (prefix != nullptr) {
+    os << prefix;
+  }
+  os << "  ";
+  if (!odd) {
+    os << " ";
+  }
+}
+
+void Drain(size_t expected,
+           const char* prefix,
+           std::unique_ptr<Addr2linePipe>* pipe /* inout */,
+           std::ostream& os) {
+  DCHECK(pipe != nullptr);
+  DCHECK(pipe->get() != nullptr);
+  int in = pipe->get()->in.get();
+  DCHECK_GE(in, 0);
+
+  bool prefix_written = false;
+
+  for (;;) {
+    constexpr uint32_t kWaitTimeExpectedMilli = 500;
+    constexpr uint32_t kWaitTimeUnexpectedMilli = 50;
+
+    int timeout = expected > 0 ? kWaitTimeExpectedMilli : kWaitTimeUnexpectedMilli;
+    struct pollfd read_fd{in, POLLIN, 0};
+    int retval = TEMP_FAILURE_RETRY(poll(&read_fd, 1, timeout));
+    if (retval == -1) {
+      // An error occurred.
+      pipe->reset();
+      return;
+    }
+
+    if (retval == 0) {
+      // Timeout.
+      return;
+    }
+
+    if (!(read_fd.revents & POLLIN)) {
+      // addr2line call exited.
+      pipe->reset();
+      return;
+    }
+
+    constexpr size_t kMaxBuffer = 128;  // Relatively small buffer. Should be OK as we're on an
+    // alt stack, but just to be sure...
+    char buffer[kMaxBuffer];
+    memset(buffer, 0, kMaxBuffer);
+    int bytes_read = TEMP_FAILURE_RETRY(read(in, buffer, kMaxBuffer - 1));
+    if (bytes_read <= 0) {
+      // This should not really happen...
+      pipe->reset();
+      return;
+    }
+    buffer[bytes_read] = '\0';
+
+    char* tmp = buffer;
+    while (*tmp != 0) {
+      if (!prefix_written) {
+        WritePrefix(os, prefix, (*pipe)->odd);
+        prefix_written = true;
+      }
+      char* new_line = strchr(tmp, '\n');
+      if (new_line == nullptr) {
+        os << tmp;
+
+        break;
+      } else {
+        os << std::string(tmp, new_line - tmp + 1);
+
+        tmp = new_line + 1;
+        prefix_written = false;
+        (*pipe)->odd = !(*pipe)->odd;
+
+        if (expected > 0) {
+          expected--;
+        }
+      }
+    }
+  }
+}
+
+void Addr2line(const std::string& addr2line,
+               const std::string& map_src,
+               uintptr_t offset,
+               std::ostream& os,
+               const char* prefix,
+               std::unique_ptr<Addr2linePipe>* pipe /* inout */) {
+  DCHECK(pipe != nullptr);
+
+  if (map_src == "[vdso]" || android::base::EndsWith(map_src, ".vdex")) {
+    // addr2line will not work on the vdso.
+    // vdex files are special frames injected for the interpreter
+    // so they don't have any line number information available.
+    return;
+  }
+
+  if (*pipe == nullptr || (*pipe)->file != map_src) {
+    if (*pipe != nullptr) {
+      Drain(0, prefix, pipe, os);
+    }
+    pipe->reset();  // Close early.
+
+    const char* args[] = {
+        addr2line.c_str(),
+        "--functions",
+        "--inlines",
+        "--demangle",
+        "-e",
+        map_src.c_str(),
+        nullptr
+    };
+    *pipe = Connect(map_src, args);
+  }
+
+  Addr2linePipe* pipe_ptr = pipe->get();
+  if (pipe_ptr == nullptr) {
+    // Failed...
+    return;
+  }
+
+  // Send the offset.
+  const std::string hex_offset = StringPrintf("%zx\n", offset);
+
+  if (!android::base::WriteFully(pipe_ptr->out.get(), hex_offset.data(), hex_offset.length())) {
+    // Error. :-(
+    pipe->reset();
+    return;
+  }
+
+  // Now drain (expecting two lines).
+  Drain(2U, prefix, pipe, os);
+}
+
+}  // namespace addr2line
+
+namespace ptrace {
+
+std::set<pid_t> PtraceSiblings(pid_t pid) {
+  std::set<pid_t> ret;
+  std::string task_path = android::base::StringPrintf("/proc/%d/task", pid);
+
+  std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path.c_str()), closedir);
+
+  // Bail early if the task directory cannot be opened.
+  if (d == nullptr) {
+    PLOG(ERROR) << "Failed to scan task folder";
+    return ret;
+  }
+
+  struct dirent* de;
+  while ((de = readdir(d.get())) != nullptr) {
+    // Ignore "." and "..".
+    if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+      continue;
+    }
+
+    char* end;
+    pid_t tid = strtoul(de->d_name, &end, 10);
+    if (*end) {
+      continue;
+    }
+
+    if (tid == pid) {
+      continue;
+    }
+
+    if (::ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
+      PLOG(ERROR) << "Failed to attach to tid " << tid;
+      continue;
+    }
+
+    ret.insert(tid);
+  }
+  return ret;
+}
+
+}  // namespace ptrace
+
+template <typename T>
+bool WaitLoop(uint32_t max_wait_micros, const T& handler) {
+  constexpr uint32_t kWaitMicros = 10;
+  const size_t kMaxLoopCount = max_wait_micros / kWaitMicros;
+
+  for (size_t loop_count = 1; loop_count <= kMaxLoopCount; ++loop_count) {
+    bool ret;
+    if (handler(&ret)) {
+      return ret;
+    }
+    usleep(kWaitMicros);
+  }
+  return false;
+}
+
+bool WaitForMainSigStop(const std::atomic<bool>& saw_wif_stopped_for_main) {
+  auto handler = [&](bool* res) {
+    if (saw_wif_stopped_for_main) {
+      *res = true;
+      return true;
+    }
+    return false;
+  };
+  constexpr uint32_t kMaxWaitMicros = 30 * 1000 * 1000;  // 30s wait.
+  return WaitLoop(kMaxWaitMicros, handler);
+}
+
+bool WaitForSigStopped(pid_t pid, uint32_t max_wait_micros) {
+  auto handler = [&](bool* res) {
+    int status;
+    pid_t rc = TEMP_FAILURE_RETRY(waitpid(pid, &status, WNOHANG));
+    if (rc == -1) {
+      PLOG(ERROR) << "Failed to waitpid for " << pid;
+      *res = false;
+      return true;
+    }
+    if (rc == pid) {
+      if (!(WIFSTOPPED(status))) {
+        LOG(ERROR) << "Did not get expected stopped signal for " << pid;
+        *res = false;
+      } else {
+        *res = true;
+      }
+      return true;
+    }
+    return false;
+  };
+  return WaitLoop(max_wait_micros, handler);
+}
+
+#ifdef __LP64__
+constexpr bool kIs64Bit = true;
+#else
+constexpr bool kIs64Bit = false;
+#endif
+
+void DumpThread(pid_t pid,
+                pid_t tid,
+                const std::string* addr2line_path,
+                const char* prefix,
+                BacktraceMap* map) {
+  // Use std::cerr to avoid the LOG prefix.
+  std::cerr << std::endl << "=== pid: " << pid << " tid: " << tid << " ===" << std::endl;
+
+  constexpr uint32_t kMaxWaitMicros = 1000 * 1000;  // 1s.
+  if (pid != tid && !WaitForSigStopped(tid, kMaxWaitMicros)) {
+    LOG(ERROR) << "Failed to wait for sigstop on " << tid;
+  }
+
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
+  if (backtrace == nullptr) {
+    LOG(ERROR) << prefix << "(failed to create Backtrace for thread " << tid << ")";
+    return;
+  }
+  backtrace->SetSkipFrames(0);
+  if (!backtrace->Unwind(0, nullptr)) {
+    LOG(ERROR) << prefix << "(backtrace::Unwind failed for thread " << tid
+               << ": " <<  backtrace->GetErrorString(backtrace->GetError()) << ")";
+    return;
+  }
+  if (backtrace->NumFrames() == 0) {
+    LOG(ERROR) << prefix << "(no native stack frames for thread " << tid << ")";
+    return;
+  }
+
+  std::unique_ptr<addr2line::Addr2linePipe> addr2line_state;
+
+  for (Backtrace::const_iterator it = backtrace->begin();
+      it != backtrace->end(); ++it) {
+    std::ostringstream oss;
+    oss << prefix << StringPrintf("#%02zu pc ", it->num);
+    bool try_addr2line = false;
+    if (!BacktraceMap::IsValid(it->map)) {
+      oss << StringPrintf(kIs64Bit ? "%016" PRIx64 "  ???" : "%08" PRIx64 "  ???", it->pc);
+    } else {
+      oss << StringPrintf(kIs64Bit ? "%016" PRIx64 "  " : "%08" PRIx64 "  ", it->rel_pc);
+      if (it->map.name.empty()) {
+        oss << StringPrintf("<anonymous:%" PRIx64 ">", it->map.start);
+      } else {
+        oss << it->map.name;
+      }
+      if (it->map.offset != 0) {
+        oss << StringPrintf(" (offset %" PRIx64 ")", it->map.offset);
+      }
+      oss << " (";
+      if (!it->func_name.empty()) {
+        oss << it->func_name;
+        if (it->func_offset != 0) {
+          oss << "+" << it->func_offset;
+        }
+        // Functions found using the gdb jit interface will be in an empty
+        // map that cannot be found using addr2line.
+        if (!it->map.name.empty()) {
+          try_addr2line = true;
+        }
+      } else {
+        oss << "???";
+      }
+      oss << ")";
+    }
+    std::cerr << oss.str() << std::endl;
+    if (try_addr2line && addr2line_path != nullptr) {
+      addr2line::Addr2line(*addr2line_path,
+                           it->map.name,
+                           it->rel_pc,
+                           std::cerr,
+                           prefix,
+                           &addr2line_state);
+    }
+  }
+
+  if (addr2line_state != nullptr) {
+    addr2line::Drain(0, prefix, &addr2line_state, std::cerr);
+  }
+}
+
+void DumpProcess(pid_t forked_pid, const std::atomic<bool>& saw_wif_stopped_for_main) {
+  CHECK_EQ(0, ::ptrace(PTRACE_ATTACH, forked_pid, 0, 0));
+  std::set<pid_t> tids = ptrace::PtraceSiblings(forked_pid);
+  tids.insert(forked_pid);
+
+  // Check whether we have and should use addr2line.
+  std::unique_ptr<std::string> addr2line_path = addr2line::FindAddr2line();
+  if (addr2line_path != nullptr) {
+    LOG(ERROR) << "Found addr2line at " << *addr2line_path;
+  } else {
+    LOG(ERROR) << "Did not find usable addr2line";
+  }
+  bool use_addr2line = kUseAddr2line && addr2line_path != nullptr;
+  LOG(ERROR) << (use_addr2line ? "U" : "Not u") << "sing addr2line";
+
+  if (!WaitForMainSigStop(saw_wif_stopped_for_main)) {
+    LOG(ERROR) << "Did not receive SIGSTOP for pid " << forked_pid;
+  }
+
+  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(forked_pid));
+  if (backtrace_map == nullptr) {
+    LOG(ERROR) << "Could not create BacktraceMap";
+    return;
+  }
+
+  for (pid_t tid : tids) {
+    DumpThread(forked_pid,
+               tid,
+               use_addr2line ? addr2line_path.get() : nullptr,
+               "  ",
+               backtrace_map.get());
+  }
+}
+
+[[noreturn]]
+void WaitMainLoop(pid_t forked_pid, std::atomic<bool>* saw_wif_stopped_for_main) {
+  for (;;) {
+    // Consider switching to waitid to not get woken up for WIFSTOPPED.
+    int status;
+    pid_t res = TEMP_FAILURE_RETRY(waitpid(forked_pid, &status, 0));
+    if (res == -1) {
+      PLOG(FATAL) << "Failure during waitpid";
+      __builtin_unreachable();
+    }
+
+    if (WIFEXITED(status)) {
+      _exit(WEXITSTATUS(status));
+      __builtin_unreachable();
+    }
+    if (WIFSIGNALED(status)) {
+      _exit(1);
+      __builtin_unreachable();
+    }
+    if (WIFSTOPPED(status)) {
+      *saw_wif_stopped_for_main = true;
+      continue;
+    }
+    if (WIFCONTINUED(status)) {
+      continue;
+    }
+
+    LOG(FATAL) << "Unknown status " << std::hex << status;
+  }
+}
+
+[[noreturn]]
+void SetupAndWait(pid_t forked_pid) {
+  timeout_signal::SignalSet signals;
+  signals.Add(timeout_signal::GetTimeoutSignal());
+  signals.Block();
+
+  std::atomic<bool> saw_wif_stopped_for_main(false);
+
+  std::thread signal_catcher([&]() {
+    signals.Block();
+    int sig = signals.Wait();
+    CHECK_EQ(sig, timeout_signal::GetTimeoutSignal());
+
+    DumpProcess(forked_pid, saw_wif_stopped_for_main);
+
+    // Don't clean up. Just kill the child and exit.
+    kill(forked_pid, SIGKILL);
+    _exit(1);
+  });
+
+  WaitMainLoop(forked_pid, &saw_wif_stopped_for_main);
+}
+
+}  // namespace
+}  // namespace art
+
+int main(int argc ATTRIBUTE_UNUSED, char** argv) {
+  pid_t orig_ppid = getpid();
+
+  pid_t pid = fork();
+  if (pid == 0) {
+    if (prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) {
+      _exit(1);
+    }
+
+    if (getppid() != orig_ppid) {
+      _exit(2);
+    }
+
+    execvp(argv[1], &argv[1]);
+
+    _exit(3);
+    __builtin_unreachable();
+  }
+
+  art::SetupAndWait(pid);
+  __builtin_unreachable();
+}