Merge ""Revert^4 "Make adbconnection start automatically for debuggable apps (on target)""
diff --git a/Android.mk b/Android.mk
index 08a1a10..1c94629 100644
--- a/Android.mk
+++ b/Android.mk
@@ -245,19 +245,6 @@
 test-art-host-dexdump: $(addprefix $(HOST_OUT_EXECUTABLES)/, dexdump2 dexlist)
 	ANDROID_HOST_OUT=$(realpath $(HOST_OUT)) art/test/dexdump/run-all-tests
 
-# Valgrind.
-.PHONY: valgrind-test-art-host
-valgrind-test-art-host: valgrind-test-art-host-gtest
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
-.PHONY: valgrind-test-art-host32
-valgrind-test-art-host32: valgrind-test-art-host-gtest32
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
-.PHONY: valgrind-test-art-host64
-valgrind-test-art-host64: valgrind-test-art-host-gtest64
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
 ########################################################################
 # target test rules
 
@@ -332,19 +319,6 @@
 	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
 endif
 
-# Valgrind.
-.PHONY: valgrind-test-art-target
-valgrind-test-art-target: valgrind-test-art-target-gtest
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
-.PHONY: valgrind-test-art-target32
-valgrind-test-art-target32: valgrind-test-art-target-gtest32
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
-.PHONY: valgrind-test-art-target64
-valgrind-test-art-target64: valgrind-test-art-target-gtest64
-	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-
 
 #######################
 # Fake packages for ART
diff --git a/build/Android.bp b/build/Android.bp
index 2a5598f..3a1d583 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -127,8 +127,6 @@
     },
 
     include_dirs: [
-        "external/valgrind/include",
-        "external/valgrind",
         "external/vixl/src",
     ],
 
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 7272661..9f42727 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -408,15 +408,9 @@
 ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST_RULES :=
 ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_GTEST_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST_RULES :=
 ART_TEST_HOST_GTEST_DEPENDENCIES :=
 
 ART_GTEST_TARGET_ANDROID_ROOT := '/system'
@@ -424,40 +418,6 @@
   ART_GTEST_TARGET_ANDROID_ROOT := $(ART_TEST_ANDROID_ROOT)
 endif
 
-ART_VALGRIND_TARGET_DEPENDENCIES :=
-
-# Has to match list in external/valgrind/Android.build_one.mk
-ART_VALGRIND_SUPPORTED_ARCH := arm arm64 x86_64
-
-# Valgrind is not supported for x86
-ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_ARCH)))
-art_vg_arch := $(if $(filter x86_64,$(TARGET_ARCH)),amd64,$(TARGET_ARCH))
-ART_VALGRIND_TARGET_DEPENDENCIES += \
-  $(TARGET_OUT_EXECUTABLES)/valgrind \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(art_vg_arch)-linux \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(art_vg_arch)-linux.so \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(art_vg_arch)-linux.so \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/default.supp
-art_vg_arch :=
-endif
-
-ifdef TARGET_2ND_ARCH
-ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_2ND_ARCH)))
-ART_VALGRIND_TARGET_DEPENDENCIES += \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(TARGET_2ND_ARCH)-linux \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(TARGET_2ND_ARCH)-linux.so \
-  $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(TARGET_2ND_ARCH)-linux.so
-endif
-endif
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := valgrind-target-suppressions.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := test/valgrind-target-suppressions.txt
-LOCAL_MODULE_PATH := $(ART_TARGET_TEST_OUT)
-include $(BUILD_PREBUILT)
-
 # Define a make rule for a target device gtest.
 # $(1): gtest name - the name of the test we're building such as leb128_test.
 # $(2): path relative to $OUT to the test binary
@@ -487,11 +447,10 @@
     $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so \
     $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libopenjdkd.so \
     $$(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar \
-    $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar \
-    $$(ART_TARGET_TEST_OUT)/valgrind-target-suppressions.txt
+    $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar
 
-$$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe)
-$$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_MAYBE_CHROOT_COMMAND := $$(maybe_chroot_command)
+$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe)
+$$(gtest_rule): PRIVATE_MAYBE_CHROOT_COMMAND := $$(maybe_chroot_command)
 
 # File witnessing the success of the gtest, the presence of which means the gtest's success.
 gtest_witness := \
@@ -516,37 +475,7 @@
   ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule)
   ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule)
 
-# File witnessing the success of the Valgrind gtest, the presence of which means the gtest's
-# success.
-valgrind_gtest_witness := \
-  $$(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID
-
-valgrind-$$(gtest_rule): PRIVATE_VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness)
-
-.PHONY: valgrind-$$(gtest_rule)
-valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync
-	$(hide) adb shell touch $$(PRIVATE_VALGRIND_GTEST_WITNESS)
-	$(hide) adb shell rm $$(PRIVATE_VALGRIND_GTEST_WITNESS)
-	$(hide) adb shell $$(PRIVATE_MAYBE_CHROOT_COMMAND) chmod 755 $$(PRIVATE_TARGET_EXE)
-	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  (adb shell "$$(PRIVATE_MAYBE_CHROOT_COMMAND) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
-	       ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
-	       $(ART_GTEST_TARGET_ANDROID_ROOT)/bin/valgrind \
-	       --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \
-	       --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \
-	       --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \
-	     && touch $$(PRIVATE_VALGRIND_GTEST_WITNESS)" \
-	   && (adb pull $$(PRIVATE_VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \
-	   || $$(call ART_TEST_FAILED,$$@))
-	$(hide) rm -f /tmp/$$@-$$$$PPID
-
-  ART_TEST_TARGET_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += \
-    valgrind-$$(gtest_rule)
-  ART_TEST_TARGET_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule)
-  ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule)
-
   # Clear locally defined variables.
-  valgrind_gtest_witness :=
   gtest_witness :=
   maybe_chroot_command :=
   maybe_art_test_chroot :=
@@ -555,16 +484,6 @@
   gtest_rule :=
 endef  # define-art-gtest-rule-target
 
-ART_VALGRIND_DEPENDENCIES := \
-  $(HOST_OUT_EXECUTABLES)/valgrind \
-  $(HOST_OUT)/lib64/valgrind/memcheck-amd64-linux \
-  $(HOST_OUT)/lib64/valgrind/memcheck-x86-linux \
-  $(HOST_OUT)/lib64/valgrind/default.supp \
-  $(HOST_OUT)/lib64/valgrind/vgpreload_core-amd64-linux.so \
-  $(HOST_OUT)/lib64/valgrind/vgpreload_core-x86-linux.so \
-  $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-amd64-linux.so \
-  $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-x86-linux.so
-
 # Define make rules for a host gtests.
 # $(1): gtest name - the name of the test we're building such as leb128_test.
 # $(2): path relative to $OUT to the test binary
@@ -616,19 +535,6 @@
   ART_TEST_HOST_GTEST_$(1)_RULES += $$(gtest_rule)
 
 
-.PHONY: valgrind-$$(gtest_rule)
-valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES)
-	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  VALGRIND_LIB=$(HOST_OUT)/lib64/valgrind \
-	  $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 \
-	    --suppressions=art/test/valgrind-suppressions.txt --num-callers=50 \
-	    $$< && \
-	    $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@)
-
-  ART_TEST_HOST_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_RULES += valgrind-$$(gtest_rule)
-  ART_TEST_HOST_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule)
-  ART_TEST_HOST_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule)
-
   # Clear locally defined variables.
   gtest_deps :=
   gtest_exe :=
@@ -661,7 +567,6 @@
 
   ifndef ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES
     ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES :=
-    ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
   endif
   $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),$$(art_gtest_filename),$(2),$$($(2)library_path)))
 
@@ -681,7 +586,6 @@
   art_gtest_name := $$(notdir $$(basename $$(art_gtest_filename)))
   ifndef ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES
     ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES :=
-    ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
   endif
   $$(eval $$(call define-art-gtest-rule-host,$$(art_gtest_name),$$(art_gtest_filename),$(2)))
 
@@ -700,13 +604,8 @@
 test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES)
 	$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
 
-.PHONY: valgrind-test-art-target-gtest-$$(art_gtest_name)
-valgrind-test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES)
-	$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
-
   # Clear now unused variables.
   ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES :=
-  ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
   art_gtest_name :=
 endef  # define-art-gtest-target-both
 
@@ -719,13 +618,8 @@
 test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES)
 	$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
 
-.PHONY: valgrind-test-art-host-gtest-$$(art_gtest_name)
-valgrind-test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES)
-	$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
-
   # Clear now unused variables.
   ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES :=
-  ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
   art_gtest_name :=
 endef  # define-art-gtest-host-both
 
@@ -751,12 +645,11 @@
 $(foreach file, $(ART_TARGET_GTEST_FILES), $(eval RUNTIME_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(patsubst %/,%,$$(dir $$(file))))_$$(notdir $$(basename $$(file)))))
 COMPILER_TARGET_GTEST_MAKE_TARGETS :=
 
-# Define all the combinations of host/target, valgrind and suffix such as:
-# test-art-host-gtest or valgrind-test-art-host-gtest64
+# Define all the combinations of host/target and suffix such as:
+# test-art-host-gtest or test-art-host-gtest64
 # $(1): host or target
 # $(2): HOST or TARGET
-# $(3): valgrind- or undefined
-# $(4): undefined, 32 or 64
+# $(3): undefined, 32 or 64
 define define-test-art-gtest-combination
   ifeq ($(1),host)
     ifneq ($(2),HOST)
@@ -771,12 +664,8 @@
     endif
   endif
 
-  rule_name := $(3)test-art-$(1)-gtest$(4)
-  ifeq ($(3),valgrind-)
-    dependencies := $$(ART_TEST_$(2)_VALGRIND_GTEST$(4)_RULES)
-  else
-    dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES)
-  endif
+  rule_name := test-art-$(1)-gtest$(3)
+  dependencies := $$(ART_TEST_$(2)_GTEST$(3)_RULES)
 
 .PHONY: $$(rule_name)
 $$(rule_name): $$(dependencies) dx d8-compat-dx desugar
@@ -787,21 +676,15 @@
   dependencies :=
 endef  # define-test-art-gtest-combination
 
-$(eval $(call define-test-art-gtest-combination,target,TARGET,,))
-$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,))
-$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(ART_PHONY_TEST_TARGET_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,target,TARGET,))
+$(eval $(call define-test-art-gtest-combination,target,TARGET,$(ART_PHONY_TEST_TARGET_SUFFIX)))
 ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX
-$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,target,TARGET,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
 endif
-$(eval $(call define-test-art-gtest-combination,host,HOST,,))
-$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,))
-$(eval $(call define-test-art-gtest-combination,host,HOST,,$(ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(ART_PHONY_TEST_HOST_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,host,HOST,))
+$(eval $(call define-test-art-gtest-combination,host,HOST,$(ART_PHONY_TEST_HOST_SUFFIX)))
 ifneq ($(HOST_PREFER_32_BIT),true)
-$(eval $(call define-test-art-gtest-combination,host,HOST,,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,host,HOST,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
 endif
 
 # Clear locally defined variables.
@@ -818,15 +701,9 @@
 ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_VALGRIND_GTEST_RULES :=
 ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_GTEST_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_VALGRIND_GTEST_RULES :=
 ART_GTEST_TARGET_ANDROID_ROOT :=
 ART_GTEST_class_linker_test_DEX_DEPS :=
 ART_GTEST_class_table_test_DEX_DEPS :=
@@ -865,8 +742,6 @@
 ART_GTEST_dex2oat_environment_tests_DEX_DEPS :=
 ART_GTEST_heap_verification_test_DEX_DEPS :=
 ART_GTEST_verifier_deps_test_DEX_DEPS :=
-ART_VALGRIND_DEPENDENCIES :=
-ART_VALGRIND_TARGET_DEPENDENCIES :=
 $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=))
 $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=))
 ART_TEST_HOST_GTEST_MainStripped_DEX :=
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 517ac5c..ba3ef05 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -37,11 +37,9 @@
 endif
 
 # Use dex2oat debug version for better error reporting
-# $(1): compiler - optimizing, interpreter or interpreter-access-checks.
+# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
 # $(2): 2ND_ or undefined, 2ND_ for 32-bit host builds.
-# $(3): wrapper, e.g., valgrind.
-# $(4): dex2oat suffix, e.g, valgrind requires 32 right now.
-# $(5): multi-image.
+# $(3): multi-image.
 # NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for
 # run-test --no-image
 define create-core-oat-host-rules
@@ -65,11 +63,11 @@
   endif
   ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),)
     #Technically this test is not precise, but hopefully good enough.
-    $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing)
+    $$(error found $(1) expected interpreter, interp-ac, or optimizing)
   endif
 
-  # If $(5) is true, generate a multi-image.
-  ifeq ($(5),true)
+  # If $(3) is true, generate a multi-image.
+  ifeq ($(3),true)
     core_multi_infix := -multi
     core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar
     core_multi_group := _multi
@@ -79,22 +77,18 @@
     core_multi_group :=
   endif
 
-  core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_IMG_SUFFIX)
-  core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_OAT_SUFFIX)
+  core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_IMG_SUFFIX)
+  core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_OAT_SUFFIX)
 
   # Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
   ifeq ($(2),)
-    $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name)
+    HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name)
   else
-    $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name)
+    HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name)
   endif
-  $(3)HOST_CORE_IMG_OUTS += $$(core_image_name)
-  $(3)HOST_CORE_OAT_OUTS += $$(core_oat_name)
+  HOST_CORE_IMG_OUTS += $$(core_image_name)
+  HOST_CORE_OAT_OUTS += $$(core_oat_name)
 
-  # If we have a wrapper, make the target phony.
-  ifneq ($(3),)
-.PHONY: $$(core_image_name)
-  endif
 $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
 $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
 $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
@@ -102,7 +96,7 @@
 $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
 	@echo "host dex2oat: $$@"
 	@mkdir -p $$(dir $$@)
-	$$(hide) $(3) $$(DEX2OAT)$(4) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+	$$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
 	  --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
 	  --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(HOST_CORE_DEX_FILES)) \
 	  $$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
@@ -124,35 +118,27 @@
   core_infix :=
 endef  # create-core-oat-host-rules
 
-# $(1): compiler - optimizing, interpreter or interpreter-access-checks.
-# $(2): wrapper.
-# $(3): dex2oat suffix.
-# $(4): multi-image.
+# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
+# $(2): multi-image.
 define create-core-oat-host-rule-combination
-  $(call create-core-oat-host-rules,$(1),,$(2),$(3),$(4))
+  $(call create-core-oat-host-rules,$(1),,$(2))
 
   ifneq ($(HOST_PREFER_32_BIT),true)
-    $(call create-core-oat-host-rules,$(1),2ND_,$(2),$(3),$(4))
+    $(call create-core-oat-host-rules,$(1),2ND_,$(2))
   endif
 endef
 
-$(eval $(call create-core-oat-host-rule-combination,optimizing,,,false))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,,,false))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,false))
-$(eval $(call create-core-oat-host-rule-combination,optimizing,,,true))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,,,true))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,true))
-
-valgrindHOST_CORE_IMG_OUTS :=
-valgrindHOST_CORE_OAT_OUTS :=
-$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32,false))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32,false))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32,false))
-
-valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS)
+$(eval $(call create-core-oat-host-rule-combination,optimizing,false))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,false))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac,false))
+$(eval $(call create-core-oat-host-rule-combination,optimizing,true))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,true))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac,true))
 
 test-art-host-dex2oat-host: $(HOST_CORE_IMG_OUTS)
 
+# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
+# $(2): 2ND_ or undefined
 define create-core-oat-target-rules
   core_compile_options :=
   core_image_name :=
@@ -176,36 +162,32 @@
   endif
   ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),)
     # Technically this test is not precise, but hopefully good enough.
-    $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing)
+    $$(error found $(1) expected interpreter, interp-ac, or optimizing)
   endif
 
-  core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(3)$(CORE_IMG_SUFFIX)
-  core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(3)$(CORE_OAT_SUFFIX)
+  core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX)
+  core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX)
 
   # Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
   ifeq ($(2),)
     ifdef TARGET_2ND_ARCH
-      $(3)TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name)
+      TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name)
     else
-      $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
+      TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
     endif
   else
-    $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
+    TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
   endif
-  $(3)TARGET_CORE_IMG_OUTS += $$(core_image_name)
-  $(3)TARGET_CORE_OAT_OUTS += $$(core_oat_name)
+  TARGET_CORE_IMG_OUTS += $$(core_image_name)
+  TARGET_CORE_OAT_OUTS += $$(core_oat_name)
 
-  # If we have a wrapper, make the target phony.
-  ifneq ($(3),)
-.PHONY: $$(core_image_name)
-  endif
 $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
 $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
 $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
 $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency)
 	@echo "target dex2oat: $$@"
 	@mkdir -p $$(dir $$@)
-	$$(hide) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+	$$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
 	  --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
 	  --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \
 	  $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
@@ -228,30 +210,18 @@
   core_infix :=
 endef  # create-core-oat-target-rules
 
-# $(1): compiler - optimizing, interpreter or interpreter-access-checks.
-# $(2): wrapper.
-# $(3): dex2oat suffix.
+# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
 define create-core-oat-target-rule-combination
-  $(call create-core-oat-target-rules,$(1),,$(2),$(3))
+  $(call create-core-oat-target-rules,$(1),)
 
   ifdef TARGET_2ND_ARCH
-    $(call create-core-oat-target-rules,$(1),2ND_,$(2),$(3))
+    $(call create-core-oat-target-rules,$(1),2ND_)
   endif
 endef
 
-$(eval $(call create-core-oat-target-rule-combination,optimizing,,))
-$(eval $(call create-core-oat-target-rule-combination,interpreter,,))
-$(eval $(call create-core-oat-target-rule-combination,interp-ac,,))
-
-valgrindTARGET_CORE_IMG_OUTS :=
-valgrindTARGET_CORE_OAT_OUTS :=
-$(eval $(call create-core-oat-target-rule-combination,optimizing,valgrind,32))
-$(eval $(call create-core-oat-target-rule-combination,interpreter,valgrind,32))
-$(eval $(call create-core-oat-target-rule-combination,interp-ac,valgrind,32))
-
-valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS)
-
-valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-test-art-host-dex2oat-target
+$(eval $(call create-core-oat-target-rule-combination,optimizing))
+$(eval $(call create-core-oat-target-rule-combination,interpreter))
+$(eval $(call create-core-oat-target-rule-combination,interp-ac))
 
 # Define a default core image that can be used for things like gtests that
 # need some image to run, but don't otherwise care which image is used.
diff --git a/cmdline/unit.h b/cmdline/unit.h
index ad6a03d..f73981f 100644
--- a/cmdline/unit.h
+++ b/cmdline/unit.h
@@ -21,8 +21,9 @@
 
 // Used for arguments that simply indicate presence (e.g. "-help") without any values.
 struct Unit {
-  // Avoid 'Conditional jump or move depends on uninitialised value(s)' errors
-  // when running valgrind by specifying a user-defined constructor.
+  // Historical note: We specified a user-defined constructor to avoid
+  // 'Conditional jump or move depends on uninitialised value(s)' errors
+  // when running Valgrind.
   Unit() {}
   Unit(const Unit&) = default;
   ~Unit() {}
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 1fe42ad..d4a9ba5 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -82,9 +82,8 @@
     ASSERT_NE(0, cmp);
 
     // Unquicken the dex file.
-    for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) {
+    for (ClassAccessor accessor : updated_dex_file->GetClasses()) {
       // Unquicken each method.
-      ClassAccessor accessor(*updated_dex_file, updated_dex_file->GetClassDef(i));
       for (const ClassAccessor::Method& method : accessor.GetMethods()) {
         CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(
             method.GetReference());
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index dc044c1..fe8b766 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -724,7 +724,8 @@
     return false;
   }
   DCHECK_GE(field->GetOffset().Int32Value(), 0);
-  // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451.
+  // Historical note: We made sure not to interleave function calls with bit field writes to
+  // placate Valgrind. Bug: 27552451.
   uint32_t field_offset = field->GetOffset().Uint32Value();
   bool is_volatile = field->IsVolatile();
   result->field_idx = field_idx;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 653e9ed..6cb3936 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1685,16 +1685,14 @@
 
 bool CompilerDriver::RequiresConstructorBarrier(const DexFile& dex_file,
                                                 uint16_t class_def_idx) const {
-  ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx));
-  bool has_is_final = false;
+  ClassAccessor accessor(dex_file, class_def_idx);
   // We require a constructor barrier if there are final instance fields.
-  accessor.VisitFields(/*static*/ VoidFunctor(),
-                       [&](const ClassAccessor::Field& field) {
+  for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) {
     if (field.IsFinal()) {
-      has_is_final = true;
+      return true;
     }
-  });
-  return has_is_final;
+  }
+  return false;
 }
 
 class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor {
@@ -1744,7 +1742,7 @@
     // fields are assigned within the lock held for class initialization.
     bool requires_constructor_barrier = false;
 
-    ClassAccessor accessor(dex_file, class_def);
+    ClassAccessor accessor(dex_file, class_def_index);
     // Optionally resolve fields and methods and figure out if we need a constructor barrier.
     auto method_visitor = [&](const ClassAccessor::Method& method)
         REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1926,13 +1924,12 @@
     // Fetch the list of unverified classes.
     const std::set<dex::TypeIndex>& unverified_classes =
         verifier_deps->GetUnverifiedClasses(*dex_file);
-    uint32_t class_def_idx = 0u;
     for (ClassAccessor accessor : dex_file->GetClasses()) {
       if (unverified_classes.find(accessor.GetClassIdx()) == unverified_classes.end()) {
         if (compiler_only_verifies) {
           // Just update the compiled_classes_ map. The compiler doesn't need to resolve
           // the type.
-          ClassReference ref(dex_file, class_def_idx);
+          ClassReference ref(dex_file, accessor.GetClassDefIndex());
           const ClassStatus existing = ClassStatus::kNotReady;
           ClassStateTable::InsertResult result =
              compiled_classes_.Insert(ref, existing, ClassStatus::kVerified);
@@ -1959,7 +1956,6 @@
                             class_loader,
                             soa.Self());
       }
-      ++class_def_idx;
     }
   }
   return true;
@@ -2700,7 +2696,7 @@
     jobject jclass_loader = context.GetClassLoader();
     ClassReference ref(&dex_file, class_def_index);
     const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-    ClassAccessor accessor(dex_file, class_def);
+    ClassAccessor accessor(dex_file, class_def_index);
     // Skip compiling classes with generic verifier failures since they will still fail at runtime
     if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) {
       return;
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 4791fa3..b3feb78 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -516,7 +516,7 @@
         locations->AddTemp(visitor->GetMethodLocation());
         break;
     }
-  } else {
+  } else if (!invoke->IsInvokePolymorphic()) {
     locations->AddTemp(visitor->GetMethodLocation());
   }
 }
@@ -544,6 +544,7 @@
     case kVirtual:
     case kInterface:
     case kPolymorphic:
+    case kCustom:
       LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
       UNREACHABLE();
   }
@@ -572,6 +573,7 @@
       entrypoint = kQuickInvokeInterfaceTrampolineWithAccessCheck;
       break;
     case kPolymorphic:
+    case kCustom:
       LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
       UNREACHABLE();
   }
@@ -579,11 +581,19 @@
 }
 
 void CodeGenerator::GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke) {
-  MoveConstant(invoke->GetLocations()->GetTemp(0), static_cast<int32_t>(invoke->GetType()));
+  // invoke-polymorphic does not use a temporary to convey any additional information (e.g. a
+  // method index) since it requires multiple info from the instruction (registers A, B, H). Not
+  // using the reservation has no effect on the registers used in the runtime call.
   QuickEntrypointEnum entrypoint = kQuickInvokePolymorphic;
   InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr);
 }
 
+void CodeGenerator::GenerateInvokeCustomCall(HInvokeCustom* invoke) {
+  MoveConstant(invoke->GetLocations()->GetTemp(0), invoke->GetCallSiteIndex());
+  QuickEntrypointEnum entrypoint = kQuickInvokeCustom;
+  InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr);
+}
+
 void CodeGenerator::CreateUnresolvedFieldLocationSummary(
     HInstruction* field_access,
     DataType::Type field_type,
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index a340446..b3c29aa 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -542,10 +542,13 @@
 
   void GenerateInvokeStaticOrDirectRuntimeCall(
       HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path);
+
   void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke);
 
   void GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke);
 
+  void GenerateInvokeCustomCall(HInvokeCustom* invoke);
+
   void CreateUnresolvedFieldLocationSummary(
       HInstruction* field_access,
       DataType::Type field_type,
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 6f173e1..5f0533c 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4695,6 +4695,15 @@
   codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
 }
 
+void LocationsBuilderARM64::VisitInvokeCustom(HInvokeCustom* invoke) {
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorARM64::VisitInvokeCustom(HInvokeCustom* invoke) {
+  codegen_->GenerateInvokeCustomCall(invoke);
+  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
+}
+
 vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageRelRoPatch(
     uint32_t boot_image_offset,
     vixl::aarch64::Label* adrp_label) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 859e159..91c1315 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -3742,6 +3742,15 @@
   codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 9);
 }
 
+void LocationsBuilderARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
+  codegen_->GenerateInvokeCustomCall(invoke);
+  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10);
+}
+
 void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
@@ -5493,7 +5502,7 @@
     codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
     CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
   }
-  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10);
+  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11);
 }
 
 void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
@@ -5513,7 +5522,7 @@
   codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
   CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
   DCHECK(!codegen_->IsLeafMethod());
-  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11);
+  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12);
 }
 
 void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
@@ -7084,7 +7093,7 @@
     return;
   }
   GenerateSuspendCheck(instruction, nullptr);
-  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12);
+  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13);
 }
 
 void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
@@ -7437,7 +7446,7 @@
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
     codegen_->GenerateLoadClassRuntimeCall(cls);
-    codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13);
+    codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14);
     return;
   }
   DCHECK(!cls->NeedsAccessCheck());
@@ -7523,7 +7532,7 @@
     } else {
       __ Bind(slow_path->GetExitLabel());
     }
-    codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14);
+    codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15);
   }
 }
 
@@ -7732,7 +7741,7 @@
       codegen_->AddSlowPath(slow_path);
       __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
       __ Bind(slow_path->GetExitLabel());
-      codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15);
+      codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16);
       return;
     }
     case HLoadString::LoadKind::kJitTableAddress: {
@@ -7754,7 +7763,7 @@
   __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
   codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
   CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
-  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16);
+  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17);
 }
 
 static int32_t GetExceptionTlsOffset() {
@@ -8384,7 +8393,7 @@
   } else {
     CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
   }
-  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17);
+  codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18);
 }
 
 void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
@@ -8883,7 +8892,7 @@
     // Note that GC roots are not affected by heap poisoning, thus we
     // do not have to unpoison `root_reg` here.
   }
-  MaybeGenerateMarkingRegisterCheck(/* code */ 18);
+  MaybeGenerateMarkingRegisterCheck(/* code */ 19);
 }
 
 void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -8963,7 +8972,7 @@
                 narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
                        : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
     }
-    MaybeGenerateMarkingRegisterCheck(/* code */ 19, /* temp_loc */ LocationFrom(ip));
+    MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
     return;
   }
 
@@ -9041,7 +9050,7 @@
       DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
                 BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
     }
-    MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
+    MaybeGenerateMarkingRegisterCheck(/* code */ 21, /* temp_loc */ LocationFrom(ip));
     return;
   }
 
@@ -9095,7 +9104,7 @@
   // Fast path: the GC is not marking: just load the reference.
   GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
   __ Bind(slow_path->GetExitLabel());
-  MaybeGenerateMarkingRegisterCheck(/* code */ 21);
+  MaybeGenerateMarkingRegisterCheck(/* code */ 22);
 }
 
 void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
@@ -9150,7 +9159,7 @@
   // Fast path: the GC is not marking: nothing to do (the field is
   // up-to-date, and we don't need to load the reference).
   __ Bind(slow_path->GetExitLabel());
-  MaybeGenerateMarkingRegisterCheck(/* code */ 22);
+  MaybeGenerateMarkingRegisterCheck(/* code */ 23);
 }
 
 void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction,
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 8be84a1..507db36 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -7795,6 +7795,14 @@
   codegen_->GenerateInvokePolymorphicCall(invoke);
 }
 
+void LocationsBuilderMIPS::VisitInvokeCustom(HInvokeCustom* invoke) {
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorMIPS::VisitInvokeCustom(HInvokeCustom* invoke) {
+  codegen_->GenerateInvokeCustomCall(invoke);
+}
+
 static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) {
   if (invoke->GetLocations()->Intrinsified()) {
     IntrinsicCodeGeneratorMIPS intrinsic(codegen);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index cd9e0e5..08a6512 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -5908,6 +5908,14 @@
   codegen_->GenerateInvokePolymorphicCall(invoke);
 }
 
+void LocationsBuilderMIPS64::VisitInvokeCustom(HInvokeCustom* invoke) {
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitInvokeCustom(HInvokeCustom* invoke) {
+  codegen_->GenerateInvokeCustomCall(invoke);
+}
+
 static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codegen) {
   if (invoke->GetLocations()->Intrinsified()) {
     IntrinsicCodeGeneratorMIPS64 intrinsic(codegen);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 9e31538..9f42ac7 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -2311,6 +2311,14 @@
   codegen_->GenerateInvokePolymorphicCall(invoke);
 }
 
+void LocationsBuilderX86::VisitInvokeCustom(HInvokeCustom* invoke) {
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86::VisitInvokeCustom(HInvokeCustom* invoke) {
+  codegen_->GenerateInvokeCustomCall(invoke);
+}
+
 void LocationsBuilderX86::VisitNeg(HNeg* neg) {
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index f739704..05194b1 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2501,6 +2501,14 @@
   codegen_->GenerateInvokePolymorphicCall(invoke);
 }
 
+void LocationsBuilderX86_64::VisitInvokeCustom(HInvokeCustom* invoke) {
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86_64::VisitInvokeCustom(HInvokeCustom* invoke) {
+  codegen_->GenerateInvokeCustomCall(invoke);
+}
+
 void LocationsBuilderX86_64::VisitNeg(HNeg* neg) {
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 6900cd8..7dd756e 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -460,9 +460,10 @@
 
 bool HInliner::TryInline(HInvoke* invoke_instruction) {
   if (invoke_instruction->IsInvokeUnresolved() ||
-      invoke_instruction->IsInvokePolymorphic()) {
-    return false;  // Don't bother to move further if we know the method is unresolved or an
-                   // invoke-polymorphic.
+      invoke_instruction->IsInvokePolymorphic() ||
+      invoke_instruction->IsInvokeCustom()) {
+    return false;  // Don't bother to move further if we know the method is unresolved or the
+                   // invocation is polymorphic (invoke-{polymorphic,custom}).
   }
 
   ScopedObjectAccess soa(Thread::Current());
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 24dc2ee..8a347df 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -449,11 +449,7 @@
       target_method,
       HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
   RangeInstructionOperands operands(graph_->GetNumberOfVRegs() - in_vregs, in_vregs);
-  HandleInvoke(invoke,
-               operands,
-               dex_file_->GetMethodShorty(method_idx),
-               /* clinit_check */ nullptr,
-               /* is_unresolved */ false);
+  HandleInvoke(invoke, operands, dex_file_->GetMethodShorty(method_idx), /* is_unresolved */ false);
 
   // Add the return instruction.
   if (return_type_ == DataType::Type::kVoid) {
@@ -916,11 +912,11 @@
                                       uint32_t method_idx,
                                       const InstructionOperands& operands) {
   InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode());
-  const char* descriptor = dex_file_->GetMethodShorty(method_idx);
-  DataType::Type return_type = DataType::FromShorty(descriptor[0]);
+  const char* shorty = dex_file_->GetMethodShorty(method_idx);
+  DataType::Type return_type = DataType::FromShorty(shorty[0]);
 
   // Remove the return type from the 'proto'.
-  size_t number_of_arguments = strlen(descriptor) - 1;
+  size_t number_of_arguments = strlen(shorty) - 1;
   if (invoke_type != kStatic) {  // instance call
     // One extra argument for 'this'.
     number_of_arguments++;
@@ -937,11 +933,7 @@
                                                          dex_pc,
                                                          method_idx,
                                                          invoke_type);
-    return HandleInvoke(invoke,
-                        operands,
-                        descriptor,
-                        nullptr /* clinit_check */,
-                        true /* is_unresolved */);
+    return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ true);
   }
 
   // Replace calls to String.<init> with StringFactory.
@@ -968,7 +960,7 @@
         invoke_type,
         target_method,
         HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
-    return HandleStringInit(invoke, operands, descriptor);
+    return HandleStringInit(invoke, operands, shorty);
   }
 
   // Potential class initialization check, in the case of a static method call.
@@ -1028,29 +1020,39 @@
                                                resolved_method,
                                                ImTable::GetImtIndex(resolved_method));
   }
-
-  return HandleInvoke(invoke, operands, descriptor, clinit_check, false /* is_unresolved */);
+  return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false, clinit_check);
 }
 
-bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED,
-                                                 uint32_t dex_pc,
+bool HInstructionBuilder::BuildInvokePolymorphic(uint32_t dex_pc,
                                                  uint32_t method_idx,
                                                  dex::ProtoIndex proto_idx,
                                                  const InstructionOperands& operands) {
-  const char* descriptor = dex_file_->GetShorty(proto_idx);
-  DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), operands.GetNumberOfOperands());
-  DataType::Type return_type = DataType::FromShorty(descriptor[0]);
-  size_t number_of_arguments = strlen(descriptor);
+  const char* shorty = dex_file_->GetShorty(proto_idx);
+  DCHECK_EQ(1 + ArtMethod::NumArgRegisters(shorty), operands.GetNumberOfOperands());
+  DataType::Type return_type = DataType::FromShorty(shorty[0]);
+  size_t number_of_arguments = strlen(shorty);
   HInvoke* invoke = new (allocator_) HInvokePolymorphic(allocator_,
                                                         number_of_arguments,
                                                         return_type,
                                                         dex_pc,
                                                         method_idx);
-  return HandleInvoke(invoke,
-                      operands,
-                      descriptor,
-                      nullptr /* clinit_check */,
-                      false /* is_unresolved */);
+  return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false);
+}
+
+
+bool HInstructionBuilder::BuildInvokeCustom(uint32_t dex_pc,
+                                            uint32_t call_site_idx,
+                                            const InstructionOperands& operands) {
+  dex::ProtoIndex proto_idx = dex_file_->GetProtoIndexForCallSite(call_site_idx);
+  const char* shorty = dex_file_->GetShorty(proto_idx);
+  DataType::Type return_type = DataType::FromShorty(shorty[0]);
+  size_t number_of_arguments = strlen(shorty) - 1;
+  HInvoke* invoke = new (allocator_) HInvokeCustom(allocator_,
+                                                   number_of_arguments,
+                                                   call_site_idx,
+                                                   return_type,
+                                                   dex_pc);
+  return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false);
 }
 
 HNewInstance* HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) {
@@ -1197,10 +1199,10 @@
 
 bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke,
                                                const InstructionOperands& operands,
-                                               const char* descriptor,
+                                               const char* shorty,
                                                size_t start_index,
                                                size_t* argument_index) {
-  uint32_t descriptor_index = 1;  // Skip the return type.
+  uint32_t shorty_index = 1;  // Skip the return type.
   const size_t number_of_operands = operands.GetNumberOfOperands();
   for (size_t i = start_index;
        // Make sure we don't go over the expected arguments or over the number of
@@ -1208,7 +1210,7 @@
        // it hasn't been properly checked.
        (i < number_of_operands) && (*argument_index < invoke->GetNumberOfArguments());
        i++, (*argument_index)++) {
-    DataType::Type type = DataType::FromShorty(descriptor[descriptor_index++]);
+    DataType::Type type = DataType::FromShorty(shorty[shorty_index++]);
     bool is_wide = (type == DataType::Type::kInt64) || (type == DataType::Type::kFloat64);
     if (is_wide && ((i + 1 == number_of_operands) ||
                     (operands.GetOperand(i) + 1 != operands.GetOperand(i + 1)))) {
@@ -1250,9 +1252,9 @@
 
 bool HInstructionBuilder::HandleInvoke(HInvoke* invoke,
                                        const InstructionOperands& operands,
-                                       const char* descriptor,
-                                       HClinitCheck* clinit_check,
-                                       bool is_unresolved) {
+                                       const char* shorty,
+                                       bool is_unresolved,
+                                       HClinitCheck* clinit_check) {
   DCHECK(!invoke->IsInvokeStaticOrDirect() || !invoke->AsInvokeStaticOrDirect()->IsStringInit());
 
   size_t start_index = 0;
@@ -1267,7 +1269,7 @@
     argument_index = 1;
   }
 
-  if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) {
+  if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) {
     return false;
   }
 
@@ -1288,13 +1290,13 @@
 
 bool HInstructionBuilder::HandleStringInit(HInvoke* invoke,
                                            const InstructionOperands& operands,
-                                           const char* descriptor) {
+                                           const char* shorty) {
   DCHECK(invoke->IsInvokeStaticOrDirect());
   DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit());
 
   size_t start_index = 1;
   size_t argument_index = 0;
-  if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) {
+  if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) {
     return false;
   }
 
@@ -1313,7 +1315,8 @@
     // The only reason a HPhi can flow in a String.<init> is when there is an
     // irreducible loop, which will create HPhi for all dex registers at loop entry.
     DCHECK(arg_this->IsPhi());
-    DCHECK(graph_->HasIrreducibleLoops());
+    // TODO(b/109666561): Re-enable.
+    // DCHECK(graph_->HasIrreducibleLoops());
     // Don't bother compiling a method in that situation. While we could look at all
     // phis related to the HNewInstance, it's not worth the trouble.
     MaybeRecordStat(compilation_stats_,
@@ -2144,14 +2147,28 @@
       uint32_t args[5];
       uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args);
       VarArgsInstructionOperands operands(args, number_of_vreg_arguments);
-      return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands);
+      return BuildInvokePolymorphic(dex_pc, method_idx, proto_idx, operands);
     }
 
     case Instruction::INVOKE_POLYMORPHIC_RANGE: {
       uint16_t method_idx = instruction.VRegB_4rcc();
       dex::ProtoIndex proto_idx(instruction.VRegH_4rcc());
       RangeInstructionOperands operands(instruction.VRegC_4rcc(), instruction.VRegA_4rcc());
-      return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands);
+      return BuildInvokePolymorphic(dex_pc, method_idx, proto_idx, operands);
+    }
+
+    case Instruction::INVOKE_CUSTOM: {
+      uint16_t call_site_idx = instruction.VRegB_35c();
+      uint32_t args[5];
+      uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args);
+      VarArgsInstructionOperands operands(args, number_of_vreg_arguments);
+      return BuildInvokeCustom(dex_pc, call_site_idx, operands);
+    }
+
+    case Instruction::INVOKE_CUSTOM_RANGE: {
+      uint16_t call_site_idx = instruction.VRegB_3rc();
+      RangeInstructionOperands operands(instruction.VRegC_3rc(), instruction.VRegA_3rc());
+      return BuildInvokeCustom(dex_pc, call_site_idx, operands);
     }
 
     case Instruction::NEG_INT: {
@@ -2933,7 +2950,21 @@
       break;
     }
 
-    default:
+    case Instruction::UNUSED_3E:
+    case Instruction::UNUSED_3F:
+    case Instruction::UNUSED_40:
+    case Instruction::UNUSED_41:
+    case Instruction::UNUSED_42:
+    case Instruction::UNUSED_43:
+    case Instruction::UNUSED_79:
+    case Instruction::UNUSED_7A:
+    case Instruction::UNUSED_F3:
+    case Instruction::UNUSED_F4:
+    case Instruction::UNUSED_F5:
+    case Instruction::UNUSED_F6:
+    case Instruction::UNUSED_F7:
+    case Instruction::UNUSED_F8:
+    case Instruction::UNUSED_F9: {
       VLOG(compiler) << "Did not compile "
                      << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex())
                      << " because of unhandled instruction "
@@ -2941,6 +2972,7 @@
       MaybeRecordStat(compilation_stats_,
                       MethodCompilationStat::kNotCompiledUnhandledInstruction);
       return false;
+    }
   }
   return true;
 }  // NOLINT(readability/fn_size)
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 2218a69..af7092a 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -173,12 +173,17 @@
 
   // Builds an invocation node for invoke-polymorphic and returns whether the
   // instruction is supported.
-  bool BuildInvokePolymorphic(const Instruction& instruction,
-                              uint32_t dex_pc,
+  bool BuildInvokePolymorphic(uint32_t dex_pc,
                               uint32_t method_idx,
                               dex::ProtoIndex proto_idx,
                               const InstructionOperands& operands);
 
+  // Builds an invocation node for invoke-custom and returns whether the
+  // instruction is supported.
+  bool BuildInvokeCustom(uint32_t dex_pc,
+                         uint32_t call_site_idx,
+                         const InstructionOperands& operands);
+
   // Builds a new array node and the instructions that fill it.
   HNewArray* BuildFilledNewArray(uint32_t dex_pc,
                                  dex::TypeIndex type_index,
@@ -253,19 +258,19 @@
 
   bool SetupInvokeArguments(HInvoke* invoke,
                             const InstructionOperands& operands,
-                            const char* descriptor,
+                            const char* shorty,
                             size_t start_index,
                             size_t* argument_index);
 
   bool HandleInvoke(HInvoke* invoke,
                     const InstructionOperands& operands,
-                    const char* descriptor,
-                    HClinitCheck* clinit_check,
-                    bool is_unresolved);
+                    const char* shorty,
+                    bool is_unresolved,
+                    HClinitCheck* clinit_check = nullptr);
 
   bool HandleStringInit(HInvoke* invoke,
                         const InstructionOperands& operands,
-                        const char* descriptor);
+                        const char* shorty);
   void HandleStringInitResult(HInvokeStaticOrDirect* invoke);
 
   HClinitCheck* ProcessClinitCheckForInvoke(
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 63704a4..d532eee 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -637,8 +637,8 @@
     return;
   }
 
-  // Note: The `outcome` is initialized to please valgrind - the compiler can reorder
-  // the return value check with the `outcome` check, b/27651442 .
+  // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder
+  // the return value check with the `outcome` check, b/27651442.
   bool outcome = false;
   if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) {
     if (outcome) {
@@ -683,8 +683,8 @@
     return;
   }
 
-  // Note: The `outcome` is initialized to please valgrind - the compiler can reorder
-  // the return value check with the `outcome` check, b/27651442 .
+  // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder
+  // the return value check with the `outcome` check, b/27651442.
   bool outcome = false;
   if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) {
     MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf);
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 056f533..f0c91f3 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -142,6 +142,7 @@
     case kSuper:
     case kInterface:
     case kPolymorphic:
+    case kCustom:
       return false;
   }
   LOG(FATAL) << "Unknown intrinsic invoke type: " << intrinsic_type;
@@ -272,34 +273,33 @@
   ClassLinker* class_linker = runtime->GetClassLinker();
   gc::Heap* heap = runtime->GetHeap();
   IntegerValueOfInfo info;
-  info.integer_cache =
-      class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;").Ptr();
-  if (info.integer_cache == nullptr) {
-    self->ClearException();
+  info.integer_cache = class_linker->LookupClass(self,
+                                                 "Ljava/lang/Integer$IntegerCache;",
+                                                 /* class_loader */ nullptr).Ptr();
+  if (info.integer_cache == nullptr || !info.integer_cache->IsInitialized()) {
+    // Optimization only works if the class is initialized.
     return info;
   }
-  if (!heap->ObjectIsInBootImageSpace(info.integer_cache) || !info.integer_cache->IsInitialized()) {
-    // Optimization only works if the class is initialized and in the boot image.
+  if (!heap->ObjectIsInBootImageSpace(info.integer_cache)) {
+    // Optimization only works if the class is in the boot image.
+    // TODO: Implement the intrinsic for boot image compilation.
     return info;
   }
-  info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;").Ptr();
-  if (info.integer == nullptr) {
-    self->ClearException();
-    return info;
-  }
-  if (!heap->ObjectIsInBootImageSpace(info.integer) || !info.integer->IsInitialized()) {
-    // Optimization only works if the class is initialized and in the boot image.
+  info.integer =
+      class_linker->LookupClass(self, "Ljava/lang/Integer;", /* class_loader */ nullptr).Ptr();
+  DCHECK(info.integer != nullptr);
+  DCHECK(info.integer->IsInitialized());  // Must be initialized since IntegerCache is initialized.
+  if (!heap->ObjectIsInBootImageSpace(info.integer)) {
+    // Optimization only works if the class is in the boot image.
     return info;
   }
 
   ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
-  if (field == nullptr) {
-    return info;
-  }
+  CHECK(field != nullptr);
   info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>(
       field->GetObject(info.integer_cache).Ptr());
   if (info.cache == nullptr) {
-    return info;
+    return info;  // Did someone mess up the IntegerCache using reflection?
   }
 
   if (!heap->ObjectIsInBootImageSpace(info.cache)) {
@@ -308,21 +308,15 @@
   }
 
   field = info.integer->FindDeclaredInstanceField("value", "I");
-  if (field == nullptr) {
-    return info;
-  }
+  CHECK(field != nullptr);
   info.value_offset = field->GetOffset().Int32Value();
 
   field = info.integer_cache->FindDeclaredStaticField("low", "I");
-  if (field == nullptr) {
-    return info;
-  }
+  CHECK(field != nullptr);
   info.low = field->GetInt(info.integer_cache);
 
   field = info.integer_cache->FindDeclaredStaticField("high", "I");
-  if (field == nullptr) {
-    return info;
-  }
+  CHECK(field != nullptr);
   info.high = field->GetInt(info.integer_cache);
 
   DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3fd5b6b..2037879 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1380,6 +1380,7 @@
   M(InvokeStaticOrDirect, Invoke)                                       \
   M(InvokeVirtual, Invoke)                                              \
   M(InvokePolymorphic, Invoke)                                          \
+  M(InvokeCustom, Invoke)                                               \
   M(LessThan, Condition)                                                \
   M(LessThanOrEqual, Condition)                                         \
   M(LoadClass, Instruction)                                             \
@@ -4382,6 +4383,38 @@
   DEFAULT_COPY_CONSTRUCTOR(InvokePolymorphic);
 };
 
+class HInvokeCustom FINAL : public HInvoke {
+ public:
+  HInvokeCustom(ArenaAllocator* allocator,
+                uint32_t number_of_arguments,
+                uint32_t call_site_index,
+                DataType::Type return_type,
+                uint32_t dex_pc)
+      : HInvoke(kInvokeCustom,
+                allocator,
+                number_of_arguments,
+                /* number_of_other_inputs */ 0u,
+                return_type,
+                dex_pc,
+                /* dex_method_index */ dex::kDexNoIndex,
+                /* resolved_method */ nullptr,
+                kStatic),
+      call_site_index_(call_site_index) {
+  }
+
+  uint32_t GetCallSiteIndex() const { return call_site_index_; }
+
+  bool IsClonable() const OVERRIDE { return true; }
+
+  DECLARE_INSTRUCTION(InvokeCustom);
+
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(InvokeCustom);
+
+ private:
+  uint32_t call_site_index_;
+};
+
 class HInvokeStaticOrDirect FINAL : public HInvoke {
  public:
   // Requirements of this method call regarding the class
@@ -6510,9 +6543,9 @@
 class HLoadMethodHandle FINAL : public HInstruction {
  public:
   HLoadMethodHandle(HCurrentMethod* current_method,
-                  uint16_t method_handle_idx,
-                  const DexFile& dex_file,
-                  uint32_t dex_pc)
+                    uint16_t method_handle_idx,
+                    const DexFile& dex_file,
+                    uint32_t dex_pc)
       : HInstruction(kLoadMethodHandle,
                      DataType::Type::kReference,
                      SideEffectsForArchRuntimeCalls(),
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 19c405e..e76e98a 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -153,7 +153,7 @@
   " 21c:	f8d9 8034 	ldr.w	r8, [r9, #52]	; 0x34\n",
   " 220:	4770      	bx	lr\n",
   " 222:	4660      	mov	r0, ip\n",
-  " 224:	f8d9 c2cc 	ldr.w	ip, [r9, #716]	; 0x2cc\n",
+  " 224:	f8d9 c2d0 	ldr.w	ip, [r9, #720]	; 0x2d0\n",
   " 228:	47e0      	blx	ip\n",
   nullptr
 };
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6b65aca..00c893a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -657,7 +657,7 @@
     // the runtime.
     LogCompletionTime();
 
-    if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+    if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
       // We want to just exit on non-debug builds, not bringing the runtime down
       // in an orderly fashion. So release the following fields.
       driver_.release();
@@ -3119,9 +3119,9 @@
 int main(int argc, char** argv) {
   int result = static_cast<int>(art::Dex2oat(argc, argv));
   // Everything was done, do an explicit exit here to avoid running Runtime destructors that take
-  // time (bug 10645725) unless we're a debug or instrumented build or running on valgrind. Note:
-  // The Dex2Oat class should not destruct the runtime in this case.
-  if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && (RUNNING_ON_MEMORY_TOOL == 0)) {
+  // time (bug 10645725) unless we're a debug or instrumented build or running on a memory tool.
+  // Note: The Dex2Oat class should not destruct the runtime in this case.
+  if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && !art::kRunningOnMemoryTool) {
     _exit(result);
   }
   return result;
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 96d7dba..a060fd2 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -472,8 +472,8 @@
 };
 
 TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) {
-  // Native memory usage isn't correctly tracked under sanitization.
-  TEST_DISABLED_FOR_MEMORY_TOOL_ASAN();
+  // Native memory usage isn't correctly tracked when running under ASan.
+  TEST_DISABLED_FOR_MEMORY_TOOL();
 
   // The `native_alloc_2_ >= native_alloc_1_` assertion below may not
   // hold true on some x86 systems; disable this test while we
@@ -1054,8 +1054,6 @@
 }
 
 TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) {
-  TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND();  // b/63052624
-
   // The watchdog is independent of dex2oat and will not delete intermediates. It is possible
   // that the compilation succeeds and the file is completely written by the time the watchdog
   // kills dex2oat (but the dex2oat threads must have been scheduled pretty badly).
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index dc07090..da69b83 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1261,15 +1261,8 @@
   return nullptr;
 }
 
-
-ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
-  Runtime* runtime = Runtime::Current();
-  ClassLinker* class_linker = runtime->GetClassLinker();
-  Thread* self = Thread::Current();
-  StackHandleScope<3> hs(self);
-  Handle<Class> object_array_class(hs.NewHandle(
-      class_linker->FindSystemClass(self, "[Ljava/lang/Object;")));
-
+ObjPtr<mirror::ObjectArray<mirror::Object>> ImageWriter::CollectDexCaches(Thread* self,
+                                                                          size_t oat_index) const {
   std::unordered_set<const DexFile*> image_dex_files;
   for (auto& pair : dex_file_oat_index_map_) {
     const DexFile* image_dex_file = pair.first;
@@ -1284,6 +1277,7 @@
   // ObjectArray, we lock the dex lock twice, first to get the number
   // of dex caches first and then lock it again to copy the dex
   // caches. We check that the number of dex caches does not change.
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   size_t dex_cache_count = 0;
   {
     ReaderMutexLock mu(self, *Locks::dex_lock_);
@@ -1300,8 +1294,8 @@
       }
     }
   }
-  Handle<ObjectArray<Object>> dex_caches(
-      hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), dex_cache_count)));
+  ObjPtr<ObjectArray<Object>> dex_caches = ObjectArray<Object>::Alloc(
+      self, GetClassRoot<ObjectArray<Object>>(class_linker), dex_cache_count);
   CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array.";
   {
     ReaderMutexLock mu(self, *Locks::dex_lock_);
@@ -1335,11 +1329,62 @@
       }
     }
   }
+  return dex_caches;
+}
+
+static ObjPtr<mirror::ObjectArray<mirror::Object>> LookupIntegerCache(Thread* self,
+                                                                      ClassLinker* class_linker)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::Class> integer_cache_class = class_linker->LookupClass(
+      self, "Ljava/lang/Integer$IntegerCache;", /* class_linker */ nullptr);
+  if (integer_cache_class == nullptr || !integer_cache_class->IsInitialized()) {
+    return nullptr;
+  }
+  ArtField* cache_field =
+      integer_cache_class->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
+  CHECK(cache_field != nullptr);
+  ObjPtr<ObjectArray<mirror::Object>> integer_cache =
+      ObjPtr<ObjectArray<mirror::Object>>::DownCast(cache_field->GetObject(integer_cache_class));
+  CHECK(integer_cache != nullptr);
+  return integer_cache;
+}
+
+static ObjPtr<mirror::ObjectArray<mirror::Object>> CollectBootImageLiveObjects(
+    Thread* self,
+    ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_) {
+  // The objects used for the Integer.valueOf() intrinsic must remain live even if references
+  // to them are removed using reflection. Image roots are not accessible through reflection,
+  // so the array we construct here shall keep them alive.
+  StackHandleScope<1> hs(self);
+  Handle<ObjectArray<mirror::Object>> integer_cache =
+      hs.NewHandle(LookupIntegerCache(self, class_linker));
+  size_t live_objects_size =
+      (integer_cache != nullptr) ? (/* cache */ 1u + integer_cache->GetLength()) : 0u;
+  ObjPtr<mirror::ObjectArray<mirror::Object>> live_objects = ObjectArray<Object>::Alloc(
+      self, GetClassRoot<ObjectArray<Object>>(class_linker), live_objects_size);
+  int32_t index = 0;
+  if (integer_cache != nullptr) {
+    live_objects->Set(index++, integer_cache.Get());
+    for (int32_t i = 0, length = integer_cache->GetLength(); i != length; ++i) {
+      live_objects->Set(index++, integer_cache->Get(i));
+    }
+  }
+  CHECK_EQ(index, live_objects->GetLength());
+  return live_objects;
+}
+
+ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
+  Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  Thread* self = Thread::Current();
+  StackHandleScope<2> hs(self);
+
+  Handle<ObjectArray<Object>> dex_caches(hs.NewHandle(CollectDexCaches(self, oat_index)));
 
   // build an Object[] of the roots needed to restore the runtime
   int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
-  auto image_roots(hs.NewHandle(
-      ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size)));
+  Handle<ObjectArray<Object>> image_roots(hs.NewHandle(ObjectArray<Object>::Alloc(
+      self, GetClassRoot<ObjectArray<Object>>(class_linker), image_roots_size)));
   image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
   image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots());
   image_roots->Set<false>(ImageHeader::kOomeWhenThrowingException,
@@ -1350,10 +1395,16 @@
                           runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
   image_roots->Set<false>(ImageHeader::kNoClassDefFoundError,
                           runtime->GetPreAllocatedNoClassDefFoundError());
-  // image_roots[ImageHeader::kClassLoader] will be set later for app image.
-  static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
-                "Class loader should be the last image root.");
-  for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) {
+  if (!compile_app_image_) {
+    ObjPtr<ObjectArray<Object>> boot_image_live_objects =
+        CollectBootImageLiveObjects(self, class_linker);
+    image_roots->Set<false>(ImageHeader::kBootImageLiveObjects, boot_image_live_objects);
+  }
+  for (int32_t i = 0, num = ImageHeader::NumberOfImageRoots(compile_app_image_); i != num; ++i) {
+    if (compile_app_image_ && i == ImageHeader::kAppImageClassLoader) {
+      // image_roots[ImageHeader::kAppImageClassLoader] will be set later for app image.
+      continue;
+    }
     CHECK(image_roots->Get(i) != nullptr);
   }
   return image_roots.Get();
@@ -1781,7 +1832,7 @@
     CHECK_EQ(class_loaders_.size(), 1u);
     CHECK_EQ(image_roots.size(), 1u);
     CHECK(*class_loaders_.begin() != nullptr);
-    image_roots[0]->Set<false>(ImageHeader::kClassLoader, *class_loaders_.begin());
+    image_roots[0]->Set<false>(ImageHeader::kAppImageClassLoader, *class_loaders_.begin());
   }
 
   // Verify that all objects have assigned image bin slots.
@@ -2094,7 +2145,8 @@
         size_t size = ArtMethod::Size(target_ptr_size_);
         size_t alignment = ArtMethod::Alignment(target_ptr_size_);
         memcpy(dest, pair.first, LengthPrefixedArray<ArtMethod>::ComputeSize(0, size, alignment));
-        // Clear padding to avoid non-deterministic data in the image (and placate valgrind).
+        // Clear padding to avoid non-deterministic data in the image.
+        // Historical note: We also did that to placate Valgrind.
         reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(dest)->ClearPadding(size, alignment);
         break;
       }
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 960d698..c282a2a 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -450,6 +450,8 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
   void CreateHeader(size_t oat_index)
       REQUIRES_SHARED(Locks::mutator_lock_);
+  ObjPtr<mirror::ObjectArray<mirror::Object>> CollectDexCaches(Thread* self, size_t oat_index) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
   mirror::ObjectArray<mirror::Object>* CreateImageRoots(size_t oat_index) const
       REQUIRES_SHARED(Locks::mutator_lock_);
   void CalculateObjectBinSlots(mirror::Object* obj)
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index d0a6eb9..2bc286a 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -497,7 +497,7 @@
   EXPECT_EQ(76U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
-  EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+  EXPECT_EQ(165 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
             sizeof(QuickEntryPoints));
 }
 
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 85778b6..8261035 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -612,7 +612,7 @@
           pClassDef.class_data_off_, pClassDef.class_data_off_);
 
   // Fields and methods.
-  ClassAccessor accessor(*pDexFile, pClassDef);
+  ClassAccessor accessor(*pDexFile, idx);
   fprintf(gOutFile, "static_fields_size  : %d\n", accessor.NumStaticFields());
   fprintf(gOutFile, "instance_fields_size: %d\n", accessor.NumInstanceFields());
   fprintf(gOutFile, "direct_methods_size : %d\n", accessor.NumDirectMethods());
diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h
index 4dccd03..a9ccae1 100644
--- a/libartbase/base/arena_allocator.h
+++ b/libartbase/base/arena_allocator.h
@@ -148,34 +148,9 @@
 
 typedef ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations> ArenaAllocatorStats;
 
-template <bool kAvailable, bool kValgrind>
-class ArenaAllocatorMemoryToolCheckImpl {
-  // This is the generic template but since there is a partial specialization
-  // for kValgrind == false, this can be instantiated only for kValgrind == true.
-  static_assert(kValgrind, "This template can be instantiated only for Valgrind.");
-  static_assert(kAvailable, "Valgrind implies memory tool availability.");
-
+class ArenaAllocatorMemoryTool {
  public:
-  ArenaAllocatorMemoryToolCheckImpl() : is_running_on_valgrind_(RUNNING_ON_MEMORY_TOOL) { }
-  bool IsRunningOnMemoryTool() { return is_running_on_valgrind_; }
-
- private:
-  const bool is_running_on_valgrind_;
-};
-
-template <bool kAvailable>
-class ArenaAllocatorMemoryToolCheckImpl<kAvailable, false> {
- public:
-  ArenaAllocatorMemoryToolCheckImpl() { }
-  bool IsRunningOnMemoryTool() { return kAvailable; }
-};
-
-typedef ArenaAllocatorMemoryToolCheckImpl<kMemoryToolIsAvailable, kMemoryToolIsValgrind>
-    ArenaAllocatorMemoryToolCheck;
-
-class ArenaAllocatorMemoryTool : private ArenaAllocatorMemoryToolCheck {
- public:
-  using ArenaAllocatorMemoryToolCheck::IsRunningOnMemoryTool;
+  bool IsRunningOnMemoryTool() { return kMemoryToolIsAvailable; }
 
   void MakeDefined(void* ptr, size_t size) {
     if (UNLIKELY(IsRunningOnMemoryTool())) {
diff --git a/libartbase/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc
index e358710..6323a2b 100644
--- a/libartbase/base/arena_allocator_test.cc
+++ b/libartbase/base/arena_allocator_test.cc
@@ -16,6 +16,7 @@
 
 #include "arena_allocator-inl.h"
 #include "arena_bit_vector.h"
+#include "base/common_art_test.h"
 #include "gtest/gtest.h"
 #include "malloc_arena_pool.h"
 #include "memory_tool.h"
@@ -146,11 +147,8 @@
 }
 
 TEST_F(ArenaAllocatorTest, ReallocReuse) {
-  // Realloc does not reuse arenas when running under sanitization. So we cannot do those
-  if (RUNNING_ON_MEMORY_TOOL != 0) {
-    printf("WARNING: TEST DISABLED FOR MEMORY_TOOL\n");
-    return;
-  }
+  // Realloc does not reuse arenas when running under sanitization.
+  TEST_DISABLED_FOR_MEMORY_TOOL();
 
   {
     // Case 1: small aligned allocation, aligned extend inside arena.
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index 3998be5..d9bea3d 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -210,23 +210,11 @@
   }
 
 #define TEST_DISABLED_FOR_MEMORY_TOOL() \
-  if (RUNNING_ON_MEMORY_TOOL > 0) { \
+  if (kRunningOnMemoryTool) { \
     printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \
     return; \
   }
 
-#define TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND() \
-  if (RUNNING_ON_MEMORY_TOOL > 0 && kMemoryToolIsValgrind) { \
-    printf("WARNING: TEST DISABLED FOR MEMORY TOOL VALGRIND\n"); \
-    return; \
-  }
-
-#define TEST_DISABLED_FOR_MEMORY_TOOL_ASAN() \
-  if (RUNNING_ON_MEMORY_TOOL > 0 && !kMemoryToolIsValgrind) { \
-    printf("WARNING: TEST DISABLED FOR MEMORY TOOL ASAN\n"); \
-    return; \
-  }
-
 #define TEST_DISABLED_FOR_HEAP_POISONING() \
   if (kPoisonHeapReferences) { \
     printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \
diff --git a/libartbase/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc
index 144b06c..15a5d71 100644
--- a/libartbase/base/malloc_arena_pool.cc
+++ b/libartbase/base/malloc_arena_pool.cc
@@ -53,7 +53,7 @@
     memory_ = unaligned_memory_;
   } else {
     memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment);
-    if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+    if (kRunningOnMemoryTool) {
       size_t head = memory_ - unaligned_memory_;
       size_t tail = overallocation - head;
       MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head);
@@ -66,7 +66,7 @@
 
 MallocArena::~MallocArena() {
   constexpr size_t overallocation = RequiredOverallocation();
-  if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+  if (overallocation != 0u && kRunningOnMemoryTool) {
     size_t head = memory_ - unaligned_memory_;
     size_t tail = overallocation - head;
     MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head);
@@ -132,7 +132,7 @@
 }
 
 void MallocArenaPool::FreeArenaChain(Arena* first) {
-  if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+  if (kRunningOnMemoryTool) {
     for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
       MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
     }
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index c455fed..9ba1d6c 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -460,7 +460,7 @@
       (expected_ptr == nullptr) ? nullptr : (expected_ptr - page_offset);
 
   size_t redzone_size = 0;
-  if (RUNNING_ON_MEMORY_TOOL && kMemoryToolAddsRedzones && expected_ptr == nullptr) {
+  if (kRunningOnMemoryTool && kMemoryToolAddsRedzones && expected_ptr == nullptr) {
     redzone_size = kPageSize;
     page_aligned_byte_count += redzone_size;
   }
@@ -649,9 +649,11 @@
 bool MemMap::Sync() {
   bool result;
   if (redzone_size_ != 0) {
-    // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing
-    // it to msync() as it only accepts page-aligned base address, and exclude the higher-end
-    // noaccess protection from the msync range. b/27552451.
+    // To avoid errors when running on a memory tool, temporarily lift the lower-end noaccess
+    // protection before passing it to msync() as it only accepts page-aligned base address,
+    // and exclude the higher-end noaccess protection from the msync range. b/27552451.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether this special case is needed for ASan.
     uint8_t* base_begin = reinterpret_cast<uint8_t*>(base_begin_);
     MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin);
     result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0;
diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc
index d956126..4a78bdc 100644
--- a/libartbase/base/mem_map_test.cc
+++ b/libartbase/base/mem_map_test.cc
@@ -471,31 +471,33 @@
   // cannot allocate in the 2GB-4GB region.
   TEST_DISABLED_FOR_MIPS();
 
+  // This test may not work under Valgrind.
+  // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+  // check whether this test works with ASan.
+  TEST_DISABLED_FOR_MEMORY_TOOL();
+
   CommonInit();
-  // This test may not work under valgrind.
-  if (RUNNING_ON_MEMORY_TOOL == 0) {
-    constexpr size_t size = 0x100000;
-    // Try all addresses starting from 2GB to 4GB.
-    size_t start_addr = 2 * GB;
-    std::string error_msg;
-    std::unique_ptr<MemMap> map;
-    for (; start_addr <= std::numeric_limits<uint32_t>::max() - size; start_addr += size) {
-      map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
-                                     reinterpret_cast<uint8_t*>(start_addr),
-                                     size,
-                                     PROT_READ | PROT_WRITE,
-                                     /*low_4gb*/true,
-                                     false,
-                                     &error_msg));
-      if (map != nullptr) {
-        break;
-      }
+  constexpr size_t size = 0x100000;
+  // Try all addresses starting from 2GB to 4GB.
+  size_t start_addr = 2 * GB;
+  std::string error_msg;
+  std::unique_ptr<MemMap> map;
+  for (; start_addr <= std::numeric_limits<uint32_t>::max() - size; start_addr += size) {
+    map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
+                                   reinterpret_cast<uint8_t*>(start_addr),
+                                   size,
+                                   PROT_READ | PROT_WRITE,
+                                   /*low_4gb*/true,
+                                   false,
+                                   &error_msg));
+    if (map != nullptr) {
+      break;
     }
-    ASSERT_TRUE(map.get() != nullptr) << error_msg;
-    ASSERT_GE(reinterpret_cast<uintptr_t>(map->End()), 2u * GB);
-    ASSERT_TRUE(error_msg.empty());
-    ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast<void*>(start_addr));
   }
+  ASSERT_TRUE(map.get() != nullptr) << error_msg;
+  ASSERT_GE(reinterpret_cast<uintptr_t>(map->End()), 2u * GB);
+  ASSERT_TRUE(error_msg.empty());
+  ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast<void*>(start_addr));
 }
 
 TEST_F(MemMapTest, MapAnonymousOverflow) {
diff --git a/libartbase/base/memory_tool.h b/libartbase/base/memory_tool.h
index e1df99f..d381f01 100644
--- a/libartbase/base/memory_tool.h
+++ b/libartbase/base/memory_tool.h
@@ -19,53 +19,53 @@
 
 #include <stddef.h>
 
+namespace art {
+
 #if !defined(__has_feature)
-#define __has_feature(x) 0
+# define __has_feature(x) 0
 #endif
 
 #if __has_feature(address_sanitizer)
 
-#include <sanitizer/asan_interface.h>
-#define ADDRESS_SANITIZER
+# include <sanitizer/asan_interface.h>
+# define ADDRESS_SANITIZER
 
-#ifdef ART_ENABLE_ADDRESS_SANITIZER
-#define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s)
-#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s)
-#define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s)
+# ifdef ART_ENABLE_ADDRESS_SANITIZER
+#  define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s)
+#  define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s)
+#  define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s)
 constexpr bool kMemoryToolIsAvailable = true;
-#else
-#define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0)
-#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0)
-#define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+# else
+#  define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0)
+#  define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+#  define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0)
 constexpr bool kMemoryToolIsAvailable = false;
-#endif
+# endif
 
 extern "C" void __asan_handle_no_return();
 
-#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
-#define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return()
-#define RUNNING_ON_MEMORY_TOOL 1U
-constexpr bool kMemoryToolIsValgrind = false;
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
+# define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return()
+constexpr bool kRunningOnMemoryTool = true;
 constexpr bool kMemoryToolDetectsLeaks = true;
 constexpr bool kMemoryToolAddsRedzones = true;
 constexpr size_t kMemoryToolStackGuardSizeScale = 2;
 
 #else
 
-#include <memcheck/memcheck.h>
-#include <valgrind.h>
-#define MEMORY_TOOL_MAKE_NOACCESS(p, s) VALGRIND_MAKE_MEM_NOACCESS(p, s)
-#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s)
-#define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s)
-#define ATTRIBUTE_NO_SANITIZE_ADDRESS
-#define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0)
-#define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND
-constexpr bool kMemoryToolIsAvailable = true;
-constexpr bool kMemoryToolIsValgrind = true;
-constexpr bool kMemoryToolDetectsLeaks = true;
-constexpr bool kMemoryToolAddsRedzones = true;
+# define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0)
+# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+# define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS
+# define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0)
+constexpr bool kRunningOnMemoryTool = false;
+constexpr bool kMemoryToolIsAvailable = false;
+constexpr bool kMemoryToolDetectsLeaks = false;
+constexpr bool kMemoryToolAddsRedzones = false;
 constexpr size_t kMemoryToolStackGuardSizeScale = 1;
 
 #endif
 
+}  // namespace art
+
 #endif  // ART_LIBARTBASE_BASE_MEMORY_TOOL_H_
diff --git a/libartbase/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h
index 44d7ebb..6c78bad 100644
--- a/libartbase/base/scoped_arena_containers.h
+++ b/libartbase/base/scoped_arena_containers.h
@@ -236,7 +236,7 @@
  protected:
   // Used for variable sized objects such as RegisterLine.
   ALWAYS_INLINE void ProtectMemory(T* ptr, size_t size) const {
-    if (RUNNING_ON_MEMORY_TOOL > 0) {
+    if (kRunningOnMemoryTool) {
       // Writing to the memory will fail ift we already destroyed the pointer with
       // DestroyOnlyDelete since we make it no access.
       memset(ptr, kMagicFill, size);
diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h
index 3bb9e93..a335f08 100644
--- a/libdexfile/dex/class_accessor-inl.h
+++ b/libdexfile/dex/class_accessor-inl.h
@@ -26,12 +26,15 @@
 namespace art {
 
 inline ClassAccessor::ClassAccessor(const ClassIteratorData& data)
-    : ClassAccessor(data.dex_file_, data.dex_file_.GetClassDef(data.class_def_idx_)) {}
+    : ClassAccessor(data.dex_file_, data.class_def_idx_) {}
 
 inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def)
+    : ClassAccessor(dex_file, dex_file.GetIndexForClassDef(class_def)) {}
+
+inline ClassAccessor::ClassAccessor(const DexFile& dex_file, uint32_t class_def_index)
     : dex_file_(dex_file),
-      descriptor_index_(class_def.class_idx_),
-      ptr_pos_(dex_file.GetClassData(class_def)),
+      class_def_index_(class_def_index),
+      ptr_pos_(dex_file.GetClassData(dex_file.GetClassDef(class_def_index))),
       num_static_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
       num_instance_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
       num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
@@ -108,7 +111,7 @@
 }
 
 inline const char* ClassAccessor::GetDescriptor() const {
-  return dex_file_.StringByTypeIdx(descriptor_index_);
+  return dex_file_.StringByTypeIdx(GetClassIdx());
 }
 
 inline const DexFile::CodeItem* ClassAccessor::Method::GetCodeItem() const {
@@ -175,6 +178,10 @@
   DexFile::UnHideAccessFlags(const_cast<uint8_t*>(ptr_pos_), GetAccessFlags(), /*is_method*/ true);
 }
 
+inline dex::TypeIndex ClassAccessor::GetClassIdx() const {
+  return dex_file_.GetClassDef(class_def_index_).class_idx_;
+}
+
 }  // namespace art
 
 #endif  // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_
diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h
index 4f0fd32..34c7e07 100644
--- a/libdexfile/dex/class_accessor.h
+++ b/libdexfile/dex/class_accessor.h
@@ -248,6 +248,8 @@
 
   ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def);
 
+  ClassAccessor(const DexFile& dex_file, uint32_t class_def_index);
+
   // Return the code item for a method.
   const DexFile::CodeItem* GetCodeItem(const Method& method) const;
 
@@ -315,9 +317,7 @@
 
   const char* GetDescriptor() const;
 
-  dex::TypeIndex GetClassIdx() const {
-    return descriptor_index_;
-  }
+  dex::TypeIndex GetClassIdx() const;
 
   const DexFile& GetDexFile() const {
     return dex_file_;
@@ -327,6 +327,10 @@
     return ptr_pos_ != nullptr;
   }
 
+  uint32_t GetClassDefIndex() const {
+    return class_def_index_;
+  }
+
  protected:
   // Template visitor to reduce copy paste for visiting elements.
   // No thread safety analysis since the visitor may require capabilities.
@@ -341,7 +345,7 @@
   IterationRange<DataIterator<Method>> GetMethodsInternal(size_t count) const;
 
   const DexFile& dex_file_;
-  const dex::TypeIndex descriptor_index_ = {};
+  const uint32_t class_def_index_;
   const uint8_t* ptr_pos_ = nullptr;  // Pointer into stream of class_data_item.
   const uint32_t num_static_fields_ = 0u;
   const uint32_t num_instance_fields_ = 0u;
diff --git a/libdexfile/dex/class_accessor_test.cc b/libdexfile/dex/class_accessor_test.cc
index d0533c1..1f30ae5 100644
--- a/libdexfile/dex/class_accessor_test.cc
+++ b/libdexfile/dex/class_accessor_test.cc
@@ -30,8 +30,9 @@
     uint32_t class_def_idx = 0u;
     ASSERT_GT(dex_file->NumClassDefs(), 0u);
     for (ClassAccessor accessor : dex_file->GetClasses()) {
-      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(accessor.GetClassDefIndex());
       EXPECT_EQ(accessor.GetDescriptor(), dex_file->StringByTypeIdx(class_def.class_idx_));
+      EXPECT_EQ(class_def_idx, accessor.GetClassDefIndex());
       ++class_def_idx;
       // Check iterators against visitors.
       auto methods = accessor.GetMethods();
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index f570158..f1f8960 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -605,6 +605,15 @@
   return PrettyDescriptor(GetTypeDescriptor(type_id));
 }
 
+dex::ProtoIndex DexFile::GetProtoIndexForCallSite(uint32_t call_site_idx) const {
+  const DexFile::CallSiteIdItem& csi = GetCallSiteId(call_site_idx);
+  CallSiteArrayValueIterator it(*this, csi);
+  it.Next();
+  it.Next();
+  DCHECK_EQ(EncodedArrayValueIterator::ValueType::kMethodType, it.GetValueType());
+  return dex::ProtoIndex(it.GetJavaValue().i);
+}
+
 // Checks that visibility is as expected. Includes special behavior for M and
 // before to allow runtime and build visibility when expecting runtime.
 std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) {
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index ed21980..a8cd187 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -737,6 +737,8 @@
     return DataBegin() + call_site_id.data_off_;
   }
 
+  dex::ProtoIndex GetProtoIndexForCallSite(uint32_t call_site_idx) const;
+
   static const TryItem* GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset);
 
   // Get the base of the encoded data for the given DexCode.
diff --git a/libdexfile/dex/dex_file_tracking_registrar.cc b/libdexfile/dex/dex_file_tracking_registrar.cc
index 78ea9c1..551bea1 100644
--- a/libdexfile/dex/dex_file_tracking_registrar.cc
+++ b/libdexfile/dex/dex_file_tracking_registrar.cc
@@ -130,7 +130,8 @@
     MEMORY_TOOL_MAKE_NOACCESS(begin, size);
   } else {
     // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address
-    // Sanitizer. The difference has not been tested with Valgrind
+    // Sanitizer.
+    // Historical note: The difference has not been tested with Valgrind.
     MEMORY_TOOL_MAKE_DEFINED(begin, size);
   }
 }
diff --git a/libdexfile/dex/invoke_type.h b/libdexfile/dex/invoke_type.h
index 9b3af67..1740c07 100644
--- a/libdexfile/dex/invoke_type.h
+++ b/libdexfile/dex/invoke_type.h
@@ -28,7 +28,8 @@
   kSuper,        // <<super>>
   kInterface,    // <<interface>>
   kPolymorphic,  // <<polymorphic>>
-  kMaxInvokeType = kPolymorphic
+  kCustom,       // <<custom>>
+  kMaxInvokeType = kCustom
 };
 
 std::ostream& operator<<(std::ostream& os, const InvokeType& rhs);
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 5012e55..453e9da 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -274,7 +274,7 @@
   void WalkOatClass(const OatFile::OatClass& oat_class,
                     const DexFile& dex_file,
                     uint32_t class_def_index) {
-    ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_index));
+    ClassAccessor accessor(dex_file, class_def_index);
     // Note: even if this is an interface or a native class, we still have to walk it, as there
     //       might be a static initializer.
     uint32_t class_method_idx = 0;
@@ -905,15 +905,13 @@
         continue;
       }
       offsets_.insert(reinterpret_cast<uintptr_t>(&dex_file->GetHeader()));
-      uint32_t class_def_index = 0u;
       for (ClassAccessor accessor : dex_file->GetClasses()) {
-        const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
+        const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(accessor.GetClassDefIndex());
         for (uint32_t class_method_index = 0;
             class_method_index < accessor.NumMethods();
             ++class_method_index) {
           AddOffsets(oat_class.GetOatMethod(class_method_index));
         }
-        ++class_def_index;
       }
     }
 
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 01418b0..0beca1f 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -615,7 +615,7 @@
     }
   }
 
-  if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+  if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
     // We want to just exit on non-debug builds, not bringing the runtime down
     // in an orderly fashion. So release the following fields.
     runtime.release();
@@ -695,7 +695,7 @@
     }
   }
 
-  if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+  if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
     // We want to just exit on non-debug builds, not bringing the runtime down
     // in an orderly fashion. So release the following fields.
     runtime.release();
diff --git a/profman/profman.cc b/profman/profman.cc
index 096e5dc..5fbce66 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -930,7 +930,9 @@
       dex_resolved_classes.first->AddClass(class_ref.TypeIndex());
       std::vector<ProfileMethodInfo> methods;
       if (method_str == kClassAllMethods) {
-        ClassAccessor accessor(*dex_file, *dex_file->FindClassDef(class_ref.TypeIndex()));
+        ClassAccessor accessor(
+            *dex_file,
+            dex_file->GetIndexForClassDef(*dex_file->FindClassDef(class_ref.TypeIndex())));
         for (const ClassAccessor::Method& method : accessor.GetMethods()) {
           if (method.GetCodeItemOffset() != 0) {
             // Add all of the methods that have code to the profile.
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 311e838..ccff9f6 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -2686,84 +2686,31 @@
 .extern artInvokePolymorphic
 ENTRY art_quick_invoke_polymorphic
     SETUP_SAVE_REFS_AND_ARGS_FRAME r2
-    mov     r2, rSELF              @ pass Thread::Current
-    mov     r3, sp                 @ pass SP
-    mov     r0, #0                 @ initialize 64-bit JValue as zero.
-    str     r0, [sp, #-4]!
-    .cfi_adjust_cfa_offset 4
-    str     r0, [sp, #-4]!
-    .cfi_adjust_cfa_offset 4
-    mov     r0, sp                 @ pass JValue for return result as first argument.
-    bl      artInvokePolymorphic   @ artInvokePolymorphic(JValue, receiver, Thread*, SP)
-    sub     r0, 'A'                @ return value is descriptor of handle's return type.
-    cmp     r0, 'Z' - 'A'          @ check if value is in bounds of handler table
-    bgt     .Lcleanup_and_return   @ and clean-up if not.
-    adr     r1, .Lhandler_table
-    tbb     [r0, r1]               @ branch to handler for return value based on return type.
-
-.Lstart_of_handlers:
-.Lstore_boolean_result:
-    ldrb    r0, [sp]               @ Copy boolean value to return value of this function.
-    b       .Lcleanup_and_return
-.Lstore_char_result:
-    ldrh    r0, [sp]               @ Copy char value to return value of this function.
-    b       .Lcleanup_and_return
-.Lstore_float_result:
-    vldr    s0, [sp]               @ Copy float value from JValue result to the context restored by
-    vstr    s0, [sp, #16]          @ RESTORE_SAVE_REFS_AND_ARGS_FRAME.
-    b       .Lcleanup_and_return
-.Lstore_double_result:
-    vldr    d0, [sp]               @ Copy double value from JValue result to the context restored by
-    vstr    d0, [sp, #16]          @ RESTORE_SAVE_REFS_AND_ARGS_FRAME.
-    b       .Lcleanup_and_return
-.Lstore_long_result:
-    ldr     r1, [sp, #4]           @ Copy the upper bits from JValue result to the context restored by
-    str     r1, [sp, #80]          @ RESTORE_SAVE_REFS_AND_ARGS_FRAME.
-    // Fall-through for lower bits.
-.Lstore_int_result:
-    ldr     r0, [sp]               @ Copy int value to return value of this function.
-    // Fall-through to clean up and return.
-.Lcleanup_and_return:
-    add     sp, #8
-    .cfi_adjust_cfa_offset -8
+    mov     r0, r1                 @ r0 := receiver
+    mov     r1, rSELF              @ r1 := Thread::Current
+    mov     r2, sp                 @ r2 := SP
+    bl      artInvokePolymorphic   @ artInvokePolymorphic(receiver, Thread*, SP)
+    str     r1, [sp, 72]           @ r0:r1 := Result. Copy r1 to context.
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
     REFRESH_MARKING_REGISTER
+    vmov    d0, r0, r1             @ Put result r0:r1 into floating point return register.
     RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2
-
-.macro HANDLER_TABLE_OFFSET handler_label
-    .byte (\handler_label - .Lstart_of_handlers) / 2
-.endm
-
-.Lhandler_table:
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // A
-    HANDLER_TABLE_OFFSET(.Lstore_int_result)      // B (byte)
-    HANDLER_TABLE_OFFSET(.Lstore_char_result)     // C (char)
-    HANDLER_TABLE_OFFSET(.Lstore_double_result)   // D (double)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // E
-    HANDLER_TABLE_OFFSET(.Lstore_float_result)    // F (float)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // G
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // H
-    HANDLER_TABLE_OFFSET(.Lstore_int_result)      // I (int)
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // J (long)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // K
-    HANDLER_TABLE_OFFSET(.Lstore_int_result)      // L (object)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // M
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // N
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // O
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // P
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // Q
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // R
-    HANDLER_TABLE_OFFSET(.Lstore_int_result)      // S (short)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // T
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // U
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // V (void)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // W
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // X
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // Y
-    HANDLER_TABLE_OFFSET(.Lstore_boolean_result)  // Z (boolean)
-.purgem HANDLER_TABLE_OFFSET
 END art_quick_invoke_polymorphic
 
+.extern artInvokeCustom
+ENTRY art_quick_invoke_custom
+    SETUP_SAVE_REFS_AND_ARGS_FRAME r1
+                                   @ r0 := call_site_idx
+    mov     r1, rSELF              @ r1 := Thread::Current
+    mov     r2, sp                 @ r2 := SP
+    bl      artInvokeCustom        @ artInvokeCustom(call_site_idx, Thread*, SP)
+    str     r1, [sp, #72]          @ Save r1 to context (r0:r1 = result)
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
+    vmov    d0, r0, r1             @ Put result r0:r1 into floating point return register.
+    RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2
+END art_quick_invoke_custom
+
 // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
 //  Argument 0: r0: The context pointer for ExecuteSwitchImpl.
 //  Argument 1: r1: Pointer to the templated ExecuteSwitchImpl to call.
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 14d0cc7..80d5fce 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2844,84 +2844,30 @@
 
 .extern artInvokePolymorphic
 ENTRY art_quick_invoke_polymorphic
-    SETUP_SAVE_REFS_AND_ARGS_FRAME                // Save callee saves in case allocation triggers GC.
-    mov     x2, xSELF
-    mov     x3, sp
-    INCREASE_FRAME 16                             // Reserve space for JValue result.
-    str     xzr, [sp, #0]                         // Initialize result to zero.
-    mov     x0, sp                                // Set r0 to point to result.
-    bl      artInvokePolymorphic                  // artInvokePolymorphic(result, receiver, thread, save_area)
-    uxtb    w0, w0                                // Result is the return type descriptor as a char.
-    sub     w0, w0, 'A'                           // Convert to zero based index.
-    cmp     w0, 'Z' - 'A'
-    bhi     .Lcleanup_and_return                  // Clean-up if out-of-bounds.
-    adrp    x1, .Lhandler_table                   // Compute address of handler table.
-    add     x1, x1, :lo12:.Lhandler_table
-    ldrb    w0, [x1, w0, uxtw]                    // Lookup handler offset in handler table.
-    adr     x1, .Lstart_of_handlers
-    add     x0, x1, w0, sxtb #2                   // Convert relative offset to absolute address.
-    br      x0                                    // Branch to handler.
-
-.Lstart_of_handlers:
-.Lstore_boolean_result:
-    ldrb    w0, [sp]
-    b       .Lcleanup_and_return
-.Lstore_char_result:
-    ldrh    w0, [sp]
-    b       .Lcleanup_and_return
-.Lstore_float_result:
-    ldr     s0, [sp]
-    str     s0, [sp, #32]
-    b       .Lcleanup_and_return
-.Lstore_double_result:
-    ldr     d0, [sp]
-    str     d0, [sp, #32]
-    b       .Lcleanup_and_return
-.Lstore_long_result:
-    ldr     x0, [sp]
-    // Fall-through
-.Lcleanup_and_return:
-    DECREASE_FRAME 16
+    SETUP_SAVE_REFS_AND_ARGS_FRAME      // Save callee saves in case allocation triggers GC.
+    mov     x0, x1                      // x0 := receiver
+    mov     x1, xSELF                   // x1 := Thread::Current()
+    mov     x2, sp                      // x2 := SP
+    bl      artInvokePolymorphic        // artInvokePolymorphic(receiver, thread, save_area)
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
     REFRESH_MARKING_REGISTER
-    RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-
-    .section    .rodata                           // Place handler table in read-only section away from text.
-    .align  2
-.macro HANDLER_TABLE_OFFSET handler_label
-    .byte (\handler_label - .Lstart_of_handlers) / 4
-.endm
-.Lhandler_table:
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // A
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // B (byte)
-    HANDLER_TABLE_OFFSET(.Lstore_char_result)     // C (char)
-    HANDLER_TABLE_OFFSET(.Lstore_double_result)   // D (double)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // E
-    HANDLER_TABLE_OFFSET(.Lstore_float_result)    // F (float)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // G
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // H
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // I (int)
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // J (long)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // K
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // L (object - references are compressed and only 32-bits)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // M
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // N
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // O
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // P
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // Q
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // R
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // S (short)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // T
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // U
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // V (void)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // W
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // X
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // Y
-    HANDLER_TABLE_OFFSET(.Lstore_boolean_result)  // Z (boolean)
-    .text
-
+    fmov    d0, x0                      // Result is in x0. Copy to floating return register.
+    RETURN_OR_DELIVER_PENDING_EXCEPTION
 END  art_quick_invoke_polymorphic
 
+.extern artInvokeCustom
+ENTRY art_quick_invoke_custom
+    SETUP_SAVE_REFS_AND_ARGS_FRAME    // Save callee saves in case allocation triggers GC.
+                                      // x0 := call_site_idx
+    mov     x1, xSELF                 // x1 := Thread::Current()
+    mov     x2, sp                    // x2 := SP
+    bl      artInvokeCustom           // artInvokeCustom(call_site_idx, thread, save_area)
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
+    fmov    d0, x0                    // Copy result to double result register.
+    RETURN_OR_DELIVER_PENDING_EXCEPTION
+END  art_quick_invoke_custom
+
 // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
 //  Argument 0: x0: The context pointer for ExecuteSwitchImpl.
 //  Argument 1: x1: Pointer to the templated ExecuteSwitchImpl to call.
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 5d6e410..2b69c17 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -410,6 +410,9 @@
   static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck),
                 "Non-direct C stub marked direct.");
   qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic;
+  static_assert(!IsDirectEntrypoint(kQuickInvokePolymorphic), "Non-direct C stub marked direct.");
+  qpoints->pInvokeCustom = art_quick_invoke_custom;
+  static_assert(!IsDirectEntrypoint(kQuickInvokeCustom), "Non-direct C stub marked direct.");
 
   // Thread
   qpoints->pTestSuspend = art_quick_test_suspend;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index c367ea6..508a201 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -3246,59 +3246,48 @@
     BRB_FIELD_EXIT_BREAK
 END art_quick_read_barrier_mark_introspection
 
+    /*
+     * Polymorphic method invocation.
+     * On entry:
+     *   a0 = unused
+     *   a1 = receiver
+     */
 .extern artInvokePolymorphic
 ENTRY art_quick_invoke_polymorphic
     SETUP_SAVE_REFS_AND_ARGS_FRAME
-    move  $a2, rSELF                          # Make $a2 an alias for the current Thread.
-    addiu $a3, $sp, ARG_SLOT_SIZE             # Make $a3 a pointer to the saved frame context.
-    sw    $zero, 20($sp)                      # Initialize JValue result.
-    sw    $zero, 16($sp)
-    la    $t9, artInvokePolymorphic
-    jalr  $t9                                 # artInvokePolymorphic(result, receiver, Thread*, context)
-    addiu $a0, $sp, 16                        # Make $a0 a pointer to the JValue result
-.macro MATCH_RETURN_TYPE c, handler
-    li    $t0, \c
-    beq   $v0, $t0, \handler
-.endm
-    MATCH_RETURN_TYPE 'V', .Lcleanup_and_return
-    MATCH_RETURN_TYPE 'L', .Lstore_int_result
-    MATCH_RETURN_TYPE 'I', .Lstore_int_result
-    MATCH_RETURN_TYPE 'J', .Lstore_long_result
-    MATCH_RETURN_TYPE 'B', .Lstore_int_result
-    MATCH_RETURN_TYPE 'C', .Lstore_char_result
-    MATCH_RETURN_TYPE 'D', .Lstore_double_result
-    MATCH_RETURN_TYPE 'F', .Lstore_float_result
-    MATCH_RETURN_TYPE 'S', .Lstore_int_result
-    MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
-.purgem MATCH_RETURN_TYPE
-    nop
-    b .Lcleanup_and_return
-    nop
-.Lstore_boolean_result:
-    b .Lcleanup_and_return
-    lbu   $v0, 16($sp)                        # Move byte from JValue result to return value register.
-.Lstore_char_result:
-    b .Lcleanup_and_return
-    lhu   $v0, 16($sp)                        # Move char from JValue result to return value register.
-.Lstore_double_result:
-.Lstore_float_result:
-    CHECK_ALIGNMENT $sp, $t0
-    ldc1  $f0, 16($sp)                        # Move double/float from JValue result to return value register.
-    b .Lcleanup_and_return
-    nop
-.Lstore_long_result:
-    lw    $v1, 20($sp)                        # Move upper bits from JValue result to return value register.
-    // Fall-through for lower bits.
-.Lstore_int_result:
-    lw    $v0, 16($sp)                        # Move lower bits from JValue result to return value register.
-    // Fall-through to clean up and return.
-.Lcleanup_and_return:
-    lw    $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
+    move    $a0, $a1                            # Make $a0 the receiver.
+    move    $a1, rSELF                          # Make $a1 an alias for the current Thread.
+    la      $t9, artInvokePolymorphic           # Invoke artInvokePolymorphic
+    jalr    $t9                                 # with args (receiver, Thread*, context).
+    addiu   $a2, $sp, ARG_SLOT_SIZE             # Make $a2 a pointer to the saved frame context.
+    lw      $t7, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
-    bnez  $t7, 1f                             # Success if no exception is pending.
-    nop
-    jalr  $zero, $ra
+    bnez    $t7, 1f
+    # don't care if $v0 and/or $v1 are modified, when exception branch taken
+    MTD     $v0, $v1, $f0, $f1                  # move float value to return value
+    jalr    $zero, $ra
     nop
 1:
     DELIVER_PENDING_EXCEPTION
 END art_quick_invoke_polymorphic
+
+    /*
+     * InvokeCustom invocation.
+     * On entry:
+     *   a0 = call_site_idx
+     */
+.extern artInvokeCustom
+ENTRY art_quick_invoke_custom
+    SETUP_SAVE_REFS_AND_ARGS_FRAME
+    move    $a1, rSELF                          # Make $a1 an alias for the current Thread.
+    la      $t9, artInvokeCustom                # Invoke artInvokeCustom
+    jalr    $t9                                 # with args (call_site_idx, Thread*, context).
+    addiu   $a2, $sp, ARG_SLOT_SIZE             # Make $a2 a pointer to the saved frame context.
+    lw      $t7, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    bnez    $t7, 1f
+    # don't care if $v0 and/or $v1 are modified, when exception branch taken
+    MTD     $v0, $v1, $f0, $f1                  # move float value to return value
+    jalr    $zero, $ra
+    nop
+END art_quick_invoke_custom
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 1f4f174..258acdd 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -3046,61 +3046,49 @@
     BRB_FIELD_EXIT_BREAK
 END art_quick_read_barrier_mark_introspection
 
+    /*
+     * Polymorphic method invocation.
+     * On entry:
+     *   a0 = unused
+     *   a1 = receiver
+     */
 .extern artInvokePolymorphic
 ENTRY art_quick_invoke_polymorphic
     SETUP_SAVE_REFS_AND_ARGS_FRAME
-    move   $a2, rSELF                          # Make $a2 an alias for the current Thread.
-    move   $a3, $sp                            # Make $a3 a pointer to the saved frame context.
-    daddiu $sp, $sp, -8                        # Reserve space for JValue result.
-    .cfi_adjust_cfa_offset 8
-    sd     $zero, 0($sp)                       # Initialize JValue result.
-    jal    artInvokePolymorphic                # artInvokePolymorphic(result, receiver, Thread*, context)
-    move   $a0, $sp                            # Make $a0 a pointer to the JValue result
-.macro MATCH_RETURN_TYPE c, handler
-    li     $t0, \c
-    beq    $v0, $t0, \handler
-.endm
-    MATCH_RETURN_TYPE 'V', .Lcleanup_and_return
-    MATCH_RETURN_TYPE 'L', .Lstore_ref_result
-    MATCH_RETURN_TYPE 'I', .Lstore_long_result
-    MATCH_RETURN_TYPE 'J', .Lstore_long_result
-    MATCH_RETURN_TYPE 'B', .Lstore_long_result
-    MATCH_RETURN_TYPE 'C', .Lstore_char_result
-    MATCH_RETURN_TYPE 'D', .Lstore_double_result
-    MATCH_RETURN_TYPE 'F', .Lstore_float_result
-    MATCH_RETURN_TYPE 'S', .Lstore_long_result
-    MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
-.purgem MATCH_RETURN_TYPE
-    nop
-    b .Lcleanup_and_return
-    nop
-.Lstore_boolean_result:
-    b      .Lcleanup_and_return
-    lbu    $v0, 0($sp)                         # Move byte from JValue result to return value register.
-.Lstore_char_result:
-    b      .Lcleanup_and_return
-    lhu    $v0, 0($sp)                         # Move char from JValue result to return value register.
-.Lstore_double_result:
-.Lstore_float_result:
-    b      .Lcleanup_and_return
-    l.d    $f0, 0($sp)                         # Move double/float from JValue result to return value register.
-.Lstore_ref_result:
-    b      .Lcleanup_and_return
-    lwu    $v0, 0($sp)                         # Move zero extended lower 32-bits to return value register.
-.Lstore_long_result:
-    ld     $v0, 0($sp)                         # Move long from JValue result to return value register.
-    // Fall-through to clean up and return.
-.Lcleanup_and_return:
-    daddiu $sp, $sp, 8                         # Remove space for JValue result.
-    .cfi_adjust_cfa_offset -8
-    ld     $t0, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
-    RESTORE_SAVE_REFS_AND_ARGS_FRAME
-    bnez   $t0, 1f                             # Success if no exception is pending.
-    nop
-    jalr   $zero, $ra
-    nop
+    move    $a0, $a1               # Make $a0 the receiver
+    move    $a1, rSELF             # Make $a1 an alias for the current Thread.
+    jal     artInvokePolymorphic   # artInvokePolymorphic(receiver, Thread*, context)
+    move    $a2, $sp               # Make $a3 a pointer to the saved frame context.
+    ld      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+    daddiu  $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE  # skip a0-a7 and f12-f19
+    RESTORE_SAVE_REFS_ONLY_FRAME
+    bne     $t0, $zero, 1f
+    dmtc1   $v0, $f0               # place return value to FP return value
+    jalr    $zero, $ra
+    dmtc1   $v1, $f1               # place return value to FP return value
 1:
     DELIVER_PENDING_EXCEPTION
 END art_quick_invoke_polymorphic
 
+    /*
+     * InvokeCustom invocation.
+     * On entry:
+     *   a0 = call_site_idx
+     */
+.extern artInvokeCustom
+ENTRY art_quick_invoke_custom
+    SETUP_SAVE_REFS_AND_ARGS_FRAME
+    move    $a1, rSELF             # Make $a1 an alias for the current Thread.
+    jal     artInvokeCustom        # Call artInvokeCustom(call_site_idx, Thread*, context).
+    move    $a2, $sp               # Make $a1 a pointer to the saved frame context.
+    ld      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+    daddiu  $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE  # skip a0-a7 and f12-f19
+    RESTORE_SAVE_REFS_ONLY_FRAME
+    bne     $t0, $zero, 1f
+    dmtc1   $v0, $f0               # place return value to FP return value
+    jalr    $zero, $ra
+    dmtc1   $v1, $f1               # place return value to FP return value
+1:
+    DELIVER_PENDING_EXCEPTION
+END art_quick_invoke_polymorphic
   .set pop
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index b89d45f..e1b3df8 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -2434,99 +2434,49 @@
 END_FUNCTION art_quick_osr_stub
 
 DEFINE_FUNCTION art_quick_invoke_polymorphic
-    SETUP_SAVE_REFS_AND_ARGS_FRAME  ebx, ebx       // Save frame.
-    mov %esp, %edx                                 // Remember SP.
-    subl LITERAL(16), %esp                         // Make space for JValue result.
-    CFI_ADJUST_CFA_OFFSET(16)
-    movl LITERAL(0), (%esp)                        // Initialize result to zero.
-    movl LITERAL(0), 4(%esp)
-    mov %esp, %eax                                 // Store pointer to JValue result in eax.
-    PUSH edx                                       // pass SP
-    pushl %fs:THREAD_SELF_OFFSET                   // pass Thread::Current()
+                                                   // On entry: EAX := unused, ECX := receiver
+    SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx        // Save frame.
+    mov %esp, %edx                                 // Remember SP
+    sub LITERAL(4), %esp                           // Alignment padding
     CFI_ADJUST_CFA_OFFSET(4)
-    PUSH ecx                                       // pass receiver (method handle)
-    PUSH eax                                       // pass JResult
-    call SYMBOL(artInvokePolymorphic)              // artInvokePolymorphic(result, receiver, Thread*, SP)
-    subl LITERAL('A'), %eax                        // Eliminate out of bounds options
-    cmpb LITERAL('Z' - 'A'), %al
-    ja .Lcleanup_and_return
-    movzbl %al, %eax
-    call .Lput_eip_in_ecx
-.Lbranch_start:
-    movl %ecx, %edx
-    add $(.Lhandler_table - .Lbranch_start), %edx  // Make EDX point to handler_table.
-    leal (%edx, %eax, 2), %eax                     // Calculate address of entry in table.
-    movzwl (%eax), %eax                            // Lookup relative branch in table.
-    addl %ecx, %eax                                // Add EIP relative offset.
-    jmp *%eax                                      // Branch to handler.
-
-    // Handlers for different return types.
-.Lstore_boolean_result:
-    movzbl 16(%esp), %eax                          // Copy boolean result to the accumulator.
-    jmp .Lcleanup_and_return
-.Lstore_char_result:
-    movzwl 16(%esp), %eax                          // Copy char result to the accumulator.
-    jmp .Lcleanup_and_return
-.Lstore_float_result:
-    movd 16(%esp), %xmm0                           // Copy float result to the context restored by
-    movd %xmm0, 36(%esp)                           // RESTORE_SAVE_REFS_ONLY_FRAME.
-    jmp .Lcleanup_and_return
-.Lstore_double_result:
-    movsd 16(%esp), %xmm0                          // Copy double result to the context restored by
-    movsd %xmm0, 36(%esp)                          // RESTORE_SAVE_REFS_ONLY_FRAME.
-    jmp .Lcleanup_and_return
-.Lstore_long_result:
-    movl 20(%esp), %edx                            // Copy upper-word of result to the context restored by
-    movl %edx, 72(%esp)                            // RESTORE_SAVE_REFS_ONLY_FRAME.
-    // Fall-through for lower bits.
-.Lstore_int_result:
-    movl 16(%esp), %eax                            // Copy int result to the accumulator.
-    // Fall-through to clean up and return.
-.Lcleanup_and_return:
-    addl LITERAL(32), %esp                         // Pop arguments and stack allocated JValue result.
-    CFI_ADJUST_CFA_OFFSET(-32)
+    push %edx                                      // Push SP
+    CFI_ADJUST_CFA_OFFSET(4)
+    pushl %fs:THREAD_SELF_OFFSET                   // Push Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    push %ecx                                      // Push receiver (method handle)
+    CFI_ADJUST_CFA_OFFSET(4)
+    call SYMBOL(artInvokePolymorphic)              // invoke with (receiver, thread, SP)
+    addl LITERAL(16), %esp                         // Pop arguments.
+    CFI_ADJUST_CFA_OFFSET(-16)
+    mov %eax, 4(%esp)                              // Result is in EAX:EDX. Copy to saved FP state.
+    mov %edx, 8(%esp)
+    mov %edx, 40(%esp)                             // Copy EDX to saved context
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
     RETURN_OR_DELIVER_PENDING_EXCEPTION
-
-.Lput_eip_in_ecx:                                  // Internal function that puts address of
-    movl 0(%esp), %ecx                             // next instruction into ECX when CALL
-    ret
-
-    // Handler table to handlers for given type.
-.Lhandler_table:
-MACRO1(HANDLER_TABLE_ENTRY, handler_label)
-    // NB some tools require 16-bits for relocations. Shouldn't need adjusting.
-    .word RAW_VAR(handler_label) - .Lbranch_start
-END_MACRO
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // A
-    HANDLER_TABLE_ENTRY(.Lstore_int_result)        // B (byte)
-    HANDLER_TABLE_ENTRY(.Lstore_char_result)       // C (char)
-    HANDLER_TABLE_ENTRY(.Lstore_double_result)     // D (double)
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // E
-    HANDLER_TABLE_ENTRY(.Lstore_float_result)      // F (float)
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // G
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // H
-    HANDLER_TABLE_ENTRY(.Lstore_int_result)        // I (int)
-    HANDLER_TABLE_ENTRY(.Lstore_long_result)       // J (long)
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // K
-    HANDLER_TABLE_ENTRY(.Lstore_int_result)        // L (object)
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // M
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // N
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // O
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // P
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // Q
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // R
-    HANDLER_TABLE_ENTRY(.Lstore_int_result)        // S (short)
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // T
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // U
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // V (void)
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // W
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // X
-    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // Y
-    HANDLER_TABLE_ENTRY(.Lstore_boolean_result)    // Z (boolean)
-
 END_FUNCTION art_quick_invoke_polymorphic
 
+DEFINE_FUNCTION art_quick_invoke_custom
+    SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx        // Save frame.
+                                                   // EAX := call_site_index
+    mov %esp, %ecx                                 // Remember SP.
+    subl LITERAL(4), %esp                          // Alignment padding.
+    CFI_ADJUST_CFA_OFFSET(4)
+    push %ecx                                      // pass SP
+    CFI_ADJUST_CFA_OFFSET(4)
+    pushl %fs:THREAD_SELF_OFFSET                   // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    push %eax                                      // pass call_site_index
+    CFI_ADJUST_CFA_OFFSET(4)
+    call SYMBOL(artInvokeCustom)                   // artInvokeCustom(call_site_index, Thread*, SP)
+    addl LITERAL(16), %esp                         // Pop arguments.
+    CFI_ADJUST_CFA_OFFSET(-16)
+    mov %eax, 4(%esp)                              // Result is in EAX:EDX. Copy to saved FP state.
+    mov %edx, 8(%esp)
+    mov %edx, 40(%esp)                             // Copy EDX to saved context
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    RETURN_OR_DELIVER_PENDING_EXCEPTION
+END_FUNCTION art_quick_invoke_custom
+
 // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
 //  Argument 0: ESP+4: The context pointer for ExecuteSwitchImpl.
 //  Argument 1: ESP+8: Pointer to the templated ExecuteSwitchImpl to call.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index c179033..9980966 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -2418,81 +2418,29 @@
 END_FUNCTION art_quick_osr_stub
 
 DEFINE_FUNCTION art_quick_invoke_polymorphic
+                                                   // On entry: RDI := unused, RSI := receiver
     SETUP_SAVE_REFS_AND_ARGS_FRAME                 // save callee saves
-    movq %gs:THREAD_SELF_OFFSET, %rdx              // pass Thread
-    movq %rsp, %rcx                                // pass SP
-    subq LITERAL(16), %rsp                         // make space for JValue result
-    CFI_ADJUST_CFA_OFFSET(16)
-    movq LITERAL(0), (%rsp)                        // initialize result
-    movq %rsp, %rdi                                // store pointer to JValue result
-    call SYMBOL(artInvokePolymorphic)              // artInvokePolymorphic(result, receiver, Thread*, SP)
+    movq %rsi, %rdi                                // RDI := receiver
+    movq %gs:THREAD_SELF_OFFSET, %rsi              // RSI := Thread (self)
+    movq %rsp, %rdx                                // RDX := pass SP
+    call SYMBOL(artInvokePolymorphic)              // invoke with (receiver, self, SP)
                                                    // save the code pointer
-    subq LITERAL('A'), %rax                        // Convert type descriptor character value to a zero based index.
-    cmpb LITERAL('Z' - 'A'), %al                   // Eliminate out of bounds options
-    ja .Lcleanup_and_return
-    movzbq %al, %rax
-    leaq .Lhandler_table(%rip), %rcx               // Get the address of the handler table
-    movslq (%rcx, %rax, 4), %rax                   // Lookup handler offset relative to table
-    addq %rcx, %rax                                // Add table address to yield handler address.
-    jmpq *%rax                                     // Jump to handler.
-
-.align 4
-.Lhandler_table:                                   // Table of type descriptor to handlers.
-MACRO1(HANDLER_TABLE_OFFSET, handle_label)
-    // NB some tools require 32-bits for relocations. Shouldn't need adjusting.
-    .long RAW_VAR(handle_label) - .Lhandler_table
-END_MACRO
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // A
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // B (byte)
-    HANDLER_TABLE_OFFSET(.Lstore_char_result)      // C (char)
-    HANDLER_TABLE_OFFSET(.Lstore_double_result)    // D (double)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // E
-    HANDLER_TABLE_OFFSET(.Lstore_float_result)     // F (float)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // G
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // H
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // I (int)
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // J (long)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // K
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // L (object - references are compressed and only 32-bits)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // M
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // N
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // O
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // P
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // Q
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // R
-    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // S (short)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // T
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // U
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // V (void)
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // W
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // X
-    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // Y
-    HANDLER_TABLE_OFFSET(.Lstore_boolean_result)   // Z (boolean)
-
-.Lstore_boolean_result:
-    movzbq (%rsp), %rax                            // Copy boolean result to the accumulator
-    jmp .Lcleanup_and_return
-.Lstore_char_result:
-    movzwq (%rsp), %rax                            // Copy char result to the accumulator
-    jmp .Lcleanup_and_return
-.Lstore_float_result:
-    movd (%rsp), %xmm0                             // Copy float result to the context restored by
-    movd %xmm0, 32(%rsp)                           // RESTORE_SAVE_REFS_AND_ARGS_FRAME.
-    jmp .Lcleanup_and_return
-.Lstore_double_result:
-    movsd (%rsp), %xmm0                            // Copy double result to the context restored by
-    movsd %xmm0, 32(%rsp)                          // RESTORE_SAVE_REFS_AND_ARGS_FRAME.
-    jmp .Lcleanup_and_return
-.Lstore_long_result:
-    movq (%rsp), %rax                              // Copy long result to the accumulator.
-     // Fall-through
-.Lcleanup_and_return:
-    addq LITERAL(16), %rsp                         // Pop space for JValue result.
-    CFI_ADJUST_CFA_OFFSET(16)
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    movq %rax, %xmm0                               // Result is in RAX. Copy to FP result register.
     RETURN_OR_DELIVER_PENDING_EXCEPTION
 END_FUNCTION art_quick_invoke_polymorphic
 
+DEFINE_FUNCTION art_quick_invoke_custom
+    SETUP_SAVE_REFS_AND_ARGS_FRAME                 // save callee saves
+                                                   // RDI := call_site_index
+    movq %gs:THREAD_SELF_OFFSET, %rsi              // RSI := Thread::Current()
+    movq %rsp, %rdx                                // RDX := SP
+    call SYMBOL(artInvokeCustom)                   // artInvokeCustom(Thread*, SP)
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    movq %rax, %xmm0                               // Result is in RAX. Copy to FP result register.
+    RETURN_OR_DELIVER_PENDING_EXCEPTION
+END_FUNCTION art_quick_invoke_custom
+
 // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
 //  Argument 0: RDI: The context pointer for ExecuteSwitchImpl.
 //  Argument 1: RSI: Pointer to the templated ExecuteSwitchImpl to call.
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 45bf664..5b4dcb7 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -425,7 +425,7 @@
 static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file,
                                                  uint16_t class_def_idx,
                                                  uint32_t method_idx) {
-  ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx));
+  ClassAccessor accessor(dex_file, class_def_idx);
   uint32_t class_def_method_index = 0u;
   for (const ClassAccessor::Method& method : accessor.GetMethods()) {
     if (method.GetIndex() == method_idx) {
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 70ff40d..ffbff88 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -73,7 +73,7 @@
 
 // Offset of field Thread::tlsPtr_.mterp_current_ibase.
 #define THREAD_CURRENT_IBASE_OFFSET \
-    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__)
+    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 165) * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
             art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.mterp_default_ibase.
diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc
index 9ac7886..702f0e4 100644
--- a/runtime/base/mem_map_arena_pool.cc
+++ b/runtime/base/mem_map_arena_pool.cc
@@ -125,7 +125,7 @@
 }
 
 void MemMapArenaPool::FreeArenaChain(Arena* first) {
-  if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+  if (kRunningOnMemoryTool) {
     for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
       MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
     }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index be636d8..1710e78 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -990,8 +990,7 @@
   class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(
       ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(MakeObjPtr(
           spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))));
-  DCHECK_EQ(GetClassRoot(ClassRoot::kJavaLangClass, this)->GetClassFlags(),
-            mirror::kClassFlagClass);
+  DCHECK_EQ(GetClassRoot<mirror::Class>(this)->GetClassFlags(), mirror::kClassFlagClass);
 
   ObjPtr<mirror::Class> java_lang_Object = GetClassRoot<mirror::Object>(this);
   java_lang_Object->SetObjectSize(sizeof(mirror::Object));
@@ -1610,10 +1609,9 @@
       hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
   Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
       header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
-  static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
-                "Class loader should be the last image root.");
   MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
-      app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr));
+      app_image ? header.GetImageRoot(ImageHeader::kAppImageClassLoader)->AsClassLoader()
+                : nullptr));
   DCHECK(class_roots != nullptr);
   if (class_roots->GetLength() != static_cast<int32_t>(ClassRoot::kMax)) {
     *error_msg = StringPrintf("Expected %d class roots but got %d",
@@ -2863,9 +2861,9 @@
   }
 
   const DexFile& dex_file = klass->GetDexFile();
-  const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
-  CHECK(dex_class_def != nullptr);
-  ClassAccessor accessor(dex_file, *dex_class_def);
+  const uint16_t class_def_idx = klass->GetDexClassDefIndex();
+  CHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
+  ClassAccessor accessor(dex_file, class_def_idx);
   // There should always be class data if there were direct methods.
   CHECK(accessor.HasClassData()) << klass->PrettyDescriptor();
   bool has_oat_class;
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 2ffadb3..657a78b 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -787,7 +787,7 @@
     //   Object stackState;
     //   StackTraceElement[] stackTrace;
     // Only Throwable has a non-empty constructor:
-    //   this.stackTrace = Throwable.UNASSIGNED_STACK;
+    //   this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT;
     //   fillInStackTrace();
 
     // detailMessage.
@@ -822,8 +822,8 @@
 
         // stackTrace.
         ScopedLocalRef<jobject> stack_trace_elem(env, env->GetStaticObjectField(
-            WellKnownClasses::java_lang_Throwable,
-            WellKnownClasses::java_lang_Throwable_UNASSIGNED_STACK));
+            WellKnownClasses::libcore_util_EmptyArray,
+            WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT));
         env->SetObjectField(exc.get(),
                             WellKnownClasses::java_lang_Throwable_stackTrace,
                             stack_trace_elem.get());
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 1804d9e..938489b 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -114,9 +114,9 @@
 
 extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
 
-// Invoke polymorphic entrypoint. Return type is dynamic and may be void, a primitive value, or
-// reference return type.
+// Polymorphic invoke entrypoints.
 extern "C" void art_quick_invoke_polymorphic(uint32_t, void*);
+extern "C" void art_quick_invoke_custom(uint32_t, void*);
 
 // Thread entrypoints.
 extern "C" void art_quick_test_suspend();
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 3f66045..5dcece4 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -106,6 +106,7 @@
   qpoints->pInvokeVirtualTrampolineWithAccessCheck =
       art_quick_invoke_virtual_trampoline_with_access_check;
   qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic;
+  qpoints->pInvokeCustom = art_quick_invoke_custom;
 
   // Thread
   qpoints->pTestSuspend = art_quick_test_suspend;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 3a8faca..415a158 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -134,6 +134,7 @@
   V(InvokeSuperTrampolineWithAccessCheck, void, uint32_t, void*) \
   V(InvokeVirtualTrampolineWithAccessCheck, void, uint32_t, void*) \
   V(InvokePolymorphic, void, uint32_t, void*) \
+  V(InvokeCustom, void, uint32_t, void*) \
 \
   V(TestSuspend, void, void) \
 \
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index af6a936..2b1623a 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -35,6 +35,7 @@
 #include "index_bss_mapping.h"
 #include "instrumentation.h"
 #include "interpreter/interpreter.h"
+#include "interpreter/interpreter_common.h"
 #include "interpreter/shadow_frame-inl.h"
 #include "jit/jit.h"
 #include "linear_alloc.h"
@@ -2722,18 +2723,11 @@
                                 reinterpret_cast<uintptr_t>(method));
 }
 
-// Returns shorty type so the caller can determine how to put |result|
-// into expected registers. The shorty type is static so the compiler
-// could call different flavors of this code path depending on the
-// shorty type though this would require different entry points for
-// each type.
-extern "C" uintptr_t artInvokePolymorphic(
-    JValue* result,
-    mirror::Object* raw_receiver,
-    Thread* self,
-    ArtMethod** sp)
+// Returns uint64_t representing raw bits from JValue.
+extern "C" uint64_t artInvokePolymorphic(mirror::Object* raw_receiver, Thread* self, ArtMethod** sp)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
+  DCHECK(raw_receiver != nullptr);
   DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
 
   // Start new JNI local reference state
@@ -2766,18 +2760,12 @@
   ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
       self, inst.VRegB(), caller_method, kVirtual);
 
-  if (UNLIKELY(receiver_handle.IsNull())) {
-    ThrowNullPointerExceptionForMethodAccess(resolved_method, InvokeType::kVirtual);
-    return static_cast<uintptr_t>('V');
-  }
-
   Handle<mirror::MethodType> method_type(
       hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method)));
-
-  // This implies we couldn't resolve one or more types in this method handle.
   if (UNLIKELY(method_type.IsNull())) {
+    // This implies we couldn't resolve one or more types in this method handle.
     CHECK(self->IsExceptionPending());
-    return static_cast<uintptr_t>('V');
+    return 0UL;
   }
 
   DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst.VRegA());
@@ -2811,6 +2799,7 @@
   // consecutive order.
   RangeInstructionOperands operands(first_arg + 1, num_vregs - 1);
   Intrinsics intrinsic = static_cast<Intrinsics>(resolved_method->GetIntrinsic());
+  JValue result;
   bool success = false;
   if (resolved_method->GetDeclaringClass() == GetClassRoot<mirror::MethodHandle>(linker)) {
     Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
@@ -2821,7 +2810,7 @@
                                         method_handle,
                                         method_type,
                                         &operands,
-                                        result);
+                                        &result);
     } else {
       DCHECK_EQ(static_cast<uint32_t>(intrinsic),
                 static_cast<uint32_t>(Intrinsics::kMethodHandleInvoke));
@@ -2830,7 +2819,7 @@
                                    method_handle,
                                    method_type,
                                    &operands,
-                                   result);
+                                   &result);
     }
   } else {
     DCHECK_EQ(GetClassRoot<mirror::VarHandle>(linker), resolved_method->GetDeclaringClass());
@@ -2844,7 +2833,7 @@
                                       method_type,
                                       access_mode,
                                       &operands,
-                                      result);
+                                      &result);
   }
 
   DCHECK(success || self->IsExceptionPending());
@@ -2852,7 +2841,65 @@
   // Pop transition record.
   self->PopManagedStackFragment(fragment);
 
-  return static_cast<uintptr_t>(shorty[0]);
+  return result.GetJ();
+}
+
+// Returns uint64_t representing raw bits from JValue.
+extern "C" uint64_t artInvokeCustom(uint32_t call_site_idx, Thread* self, ArtMethod** sp)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
+
+  // invoke-custom is effectively a static call (no receiver).
+  static constexpr bool kMethodIsStatic = true;
+
+  // Start new JNI local reference state
+  JNIEnvExt* env = self->GetJniEnv();
+  ScopedObjectAccessUnchecked soa(env);
+  ScopedJniEnvLocalRefState env_state(env);
+
+  const char* old_cause = self->StartAssertNoThreadSuspension("Making stack arguments safe.");
+
+  // From the instruction, get the |callsite_shorty| and expose arguments on the stack to the GC.
+  ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
+  uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+  const DexFile* dex_file = caller_method->GetDexFile();
+  const dex::ProtoIndex proto_idx(dex_file->GetProtoIndexForCallSite(call_site_idx));
+  const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx);
+  const uint32_t shorty_len = strlen(shorty);
+
+  // Construct the shadow frame placing arguments consecutively from |first_arg|.
+  const size_t first_arg = 0;
+  const size_t num_vregs = ArtMethod::NumArgRegisters(shorty);
+  ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
+      CREATE_SHADOW_FRAME(num_vregs, /* link */ nullptr, caller_method, dex_pc);
+  ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
+  ScopedStackedShadowFramePusher
+      frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+  BuildQuickShadowFrameVisitor shadow_frame_builder(sp,
+                                                    kMethodIsStatic,
+                                                    shorty,
+                                                    shorty_len,
+                                                    shadow_frame,
+                                                    first_arg);
+  shadow_frame_builder.VisitArguments();
+
+  // Push a transition back into managed code onto the linked list in thread.
+  ManagedStack fragment;
+  self->PushManagedStackFragment(&fragment);
+  self->EndAssertNoThreadSuspension(old_cause);
+
+  // Perform the invoke-custom operation.
+  RangeInstructionOperands operands(first_arg, num_vregs);
+  JValue result;
+  bool success =
+      interpreter::DoInvokeCustom(self, *shadow_frame, call_site_idx, &operands, &result);
+  DCHECK(success || self->IsExceptionPending());
+
+  // Pop transition record.
+  self->PopManagedStackFragment(fragment);
+
+  return result.GetJ();
 }
 
 }  // namespace art
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 1337cd5..dda3dde 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -287,8 +287,8 @@
                          pInvokeVirtualTrampolineWithAccessCheck, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeVirtualTrampolineWithAccessCheck,
                          pInvokePolymorphic, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic,
-                         pTestSuspend, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic, pInvokeCustom, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeCustom, pTestSuspend, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTestSuspend, pDeliverException, sizeof(void*));
 
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeliverException, pThrowArrayBounds, sizeof(void*));
diff --git a/runtime/exec_utils_test.cc b/runtime/exec_utils_test.cc
index 68edfa8..a9c1ea2 100644
--- a/runtime/exec_utils_test.cc
+++ b/runtime/exec_utils_test.cc
@@ -36,8 +36,10 @@
     command.push_back("/usr/bin/id");
   }
   std::string error_msg;
-  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
-    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+  if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
+    // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether the following code works with ASan.
     EXPECT_TRUE(Exec(command, &error_msg));
   }
   EXPECT_EQ(0U, error_msg.size()) << error_msg;
@@ -50,8 +52,10 @@
   std::vector<std::string> command;
   command.push_back("bogus");
   std::string error_msg;
-  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
-    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+  if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
+    // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether the following code works with ASan.
     EXPECT_FALSE(Exec(command, &error_msg));
     EXPECT_FALSE(error_msg.empty());
   }
@@ -72,8 +76,10 @@
   }
   command.push_back(kModifiedVariable);
   std::string error_msg;
-  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
-    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+  if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
+    // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether the following code works with ASan.
     EXPECT_FALSE(Exec(command, &error_msg));
     EXPECT_NE(0U, error_msg.size()) << error_msg;
   }
@@ -97,8 +103,10 @@
   }
   command.push_back(kDeletedVariable);
   std::string error_msg;
-  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
-    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+  if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) {
+    // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+    // check whether the following code works with ASan.
     EXPECT_TRUE(Exec(command, &error_msg));
     EXPECT_EQ(0U, error_msg.size()) << error_msg;
   }
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 150fe95..30213d5 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -625,7 +625,7 @@
 
   // If true, check that the returned memory is actually zero.
   static constexpr bool kCheckZeroMemory = kIsDebugBuild;
-  // Valgrind protects memory, so do not check memory when running under valgrind. In a normal
+  // Do not check memory when running under a memory tool. In a normal
   // build with kCheckZeroMemory the whole test should be optimized away.
   // TODO: Unprotect before checks.
   ALWAYS_INLINE bool ShouldCheckZeroMemory();
@@ -768,7 +768,7 @@
   // greater than or equal to this value, release pages.
   const size_t page_release_size_threshold_;
 
-  // Whether this allocator is running under Valgrind.
+  // Whether this allocator is running on a memory tool.
   bool is_running_on_memory_tool_;
 
   // The base address of the memory region that's managed by this allocator.
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 948d233..6756868 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -272,7 +272,7 @@
     }
     case kAllocatorTypeRosAlloc: {
       if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) {
-        // If running on valgrind or asan, we should be using the instrumented path.
+        // If running on ASan, we should be using the instrumented path.
         size_t max_bytes_tl_bulk_allocated = rosalloc_space_->MaxBytesBulkAllocatedFor(alloc_size);
         if (UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type,
                                                max_bytes_tl_bulk_allocated,
@@ -303,7 +303,7 @@
     }
     case kAllocatorTypeDlMalloc: {
       if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) {
-        // If running on valgrind, we should be using the instrumented path.
+        // If running on ASan, we should be using the instrumented path.
         ret = dlmalloc_space_->Alloc(self,
                                      alloc_size,
                                      bytes_allocated,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 25ed652..8e3bbde 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2248,7 +2248,8 @@
       // Add a new bin with the remaining space.
       AddBin(size - alloc_size, pos + alloc_size);
     }
-    // Copy the object over to its new location. Don't use alloc_size to avoid valgrind error.
+    // Copy the object over to its new location.
+    // Historical note: We did not use `alloc_size` to avoid a Valgrind error.
     memcpy(reinterpret_cast<void*>(forward_address), obj, obj_size);
     if (kUseBakerReadBarrier) {
       obj->AssertReadBarrierState();
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 512cde4..a24ca32 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -45,8 +45,9 @@
   }
 
   ~MemoryToolLargeObjectMapSpace() OVERRIDE {
-    // Keep valgrind happy if there is any large objects such as dex cache arrays which aren't
-    // freed since they are held live by the class linker.
+    // Historical note: We were deleting large objects to keep Valgrind happy if there were
+    // any large objects such as Dex cache arrays which aren't freed since they are held live
+    // by the class linker.
     MutexLock mu(Thread::Current(), lock_);
     for (auto& m : large_objects_) {
       delete m.second.mem_map;
diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h
index 8282f3d..c022171 100644
--- a/runtime/gc/space/memory_tool_malloc_space-inl.h
+++ b/runtime/gc/space/memory_tool_malloc_space-inl.h
@@ -30,11 +30,14 @@
 namespace memory_tool_details {
 
 template <size_t kMemoryToolRedZoneBytes, bool kUseObjSizeForUsable>
-inline mirror::Object* AdjustForValgrind(void* obj_with_rdz, size_t num_bytes,
-                                         size_t bytes_allocated, size_t usable_size,
-                                         size_t bytes_tl_bulk_allocated,
-                                         size_t* bytes_allocated_out, size_t* usable_size_out,
-                                         size_t* bytes_tl_bulk_allocated_out) {
+inline mirror::Object* AdjustForMemoryTool(void* obj_with_rdz,
+                                           size_t num_bytes,
+                                           size_t bytes_allocated,
+                                           size_t usable_size,
+                                           size_t bytes_tl_bulk_allocated,
+                                           size_t* bytes_allocated_out,
+                                           size_t* usable_size_out,
+                                           size_t* bytes_tl_bulk_allocated_out) {
   if (bytes_allocated_out != nullptr) {
     *bytes_allocated_out = bytes_allocated;
   }
@@ -84,24 +87,31 @@
           bool kUseObjSizeForUsable>
 mirror::Object*
 MemoryToolMallocSpace<S,
-                    kMemoryToolRedZoneBytes,
-                    kAdjustForRedzoneInAllocSize,
-                    kUseObjSizeForUsable>::AllocWithGrowth(
-    Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out,
+                      kMemoryToolRedZoneBytes,
+                      kAdjustForRedzoneInAllocSize,
+                      kUseObjSizeForUsable>::AllocWithGrowth(
+    Thread* self,
+    size_t num_bytes,
+    size_t* bytes_allocated_out,
+    size_t* usable_size_out,
     size_t* bytes_tl_bulk_allocated_out) {
   size_t bytes_allocated;
   size_t usable_size;
   size_t bytes_tl_bulk_allocated;
-  void* obj_with_rdz = S::AllocWithGrowth(self, num_bytes + 2 * kMemoryToolRedZoneBytes,
-                                          &bytes_allocated, &usable_size,
+  void* obj_with_rdz = S::AllocWithGrowth(self,
+                                          num_bytes + 2 * kMemoryToolRedZoneBytes,
+                                          &bytes_allocated,
+                                          &usable_size,
                                           &bytes_tl_bulk_allocated);
   if (obj_with_rdz == nullptr) {
     return nullptr;
   }
 
-  return memory_tool_details::AdjustForValgrind<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
-      obj_with_rdz, num_bytes,
-      bytes_allocated, usable_size,
+  return memory_tool_details::AdjustForMemoryTool<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
+      obj_with_rdz,
+      num_bytes,
+      bytes_allocated,
+      usable_size,
       bytes_tl_bulk_allocated,
       bytes_allocated_out,
       usable_size_out,
@@ -113,27 +123,35 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 mirror::Object* MemoryToolMallocSpace<S,
-                                    kMemoryToolRedZoneBytes,
-                                    kAdjustForRedzoneInAllocSize,
-                                    kUseObjSizeForUsable>::Alloc(
-    Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out,
+                                      kMemoryToolRedZoneBytes,
+                                      kAdjustForRedzoneInAllocSize,
+                                      kUseObjSizeForUsable>::Alloc(
+    Thread* self,
+    size_t num_bytes,
+    size_t* bytes_allocated_out,
+    size_t* usable_size_out,
     size_t* bytes_tl_bulk_allocated_out) {
   size_t bytes_allocated;
   size_t usable_size;
   size_t bytes_tl_bulk_allocated;
-  void* obj_with_rdz = S::Alloc(self, num_bytes + 2 * kMemoryToolRedZoneBytes,
-                                &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated);
+  void* obj_with_rdz = S::Alloc(self,
+                                num_bytes + 2 * kMemoryToolRedZoneBytes,
+                                &bytes_allocated,
+                                &usable_size,
+                                &bytes_tl_bulk_allocated);
   if (obj_with_rdz == nullptr) {
     return nullptr;
   }
 
-  return memory_tool_details::AdjustForValgrind<kMemoryToolRedZoneBytes,
-                                             kUseObjSizeForUsable>(obj_with_rdz, num_bytes,
-                                                                   bytes_allocated, usable_size,
-                                                                   bytes_tl_bulk_allocated,
-                                                                   bytes_allocated_out,
-                                                                   usable_size_out,
-                                                                   bytes_tl_bulk_allocated_out);
+  return memory_tool_details::AdjustForMemoryTool<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
+      obj_with_rdz,
+      num_bytes,
+      bytes_allocated,
+      usable_size,
+      bytes_tl_bulk_allocated,
+      bytes_allocated_out,
+      usable_size_out,
+      bytes_tl_bulk_allocated_out);
 }
 
 template <typename S,
@@ -141,24 +159,31 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 mirror::Object* MemoryToolMallocSpace<S,
-                                    kMemoryToolRedZoneBytes,
-                                    kAdjustForRedzoneInAllocSize,
-                                    kUseObjSizeForUsable>::AllocThreadUnsafe(
-    Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out,
+                                      kMemoryToolRedZoneBytes,
+                                      kAdjustForRedzoneInAllocSize,
+                                      kUseObjSizeForUsable>::AllocThreadUnsafe(
+    Thread* self,
+    size_t num_bytes,
+    size_t* bytes_allocated_out,
+    size_t* usable_size_out,
     size_t* bytes_tl_bulk_allocated_out) {
   size_t bytes_allocated;
   size_t usable_size;
   size_t bytes_tl_bulk_allocated;
-  void* obj_with_rdz = S::AllocThreadUnsafe(self, num_bytes + 2 * kMemoryToolRedZoneBytes,
-                                            &bytes_allocated, &usable_size,
+  void* obj_with_rdz = S::AllocThreadUnsafe(self,
+                                            num_bytes + 2 * kMemoryToolRedZoneBytes,
+                                            &bytes_allocated,
+                                            &usable_size,
                                             &bytes_tl_bulk_allocated);
   if (obj_with_rdz == nullptr) {
     return nullptr;
   }
 
-  return memory_tool_details::AdjustForValgrind<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
-      obj_with_rdz, num_bytes,
-      bytes_allocated, usable_size,
+  return memory_tool_details::AdjustForMemoryTool<kMemoryToolRedZoneBytes, kUseObjSizeForUsable>(
+      obj_with_rdz,
+      num_bytes,
+      bytes_allocated,
+      usable_size,
       bytes_tl_bulk_allocated,
       bytes_allocated_out,
       usable_size_out,
@@ -170,12 +195,14 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 size_t MemoryToolMallocSpace<S,
-                           kMemoryToolRedZoneBytes,
-                           kAdjustForRedzoneInAllocSize,
-                           kUseObjSizeForUsable>::AllocationSize(
+                             kMemoryToolRedZoneBytes,
+                             kAdjustForRedzoneInAllocSize,
+                             kUseObjSizeForUsable>::AllocationSize(
     mirror::Object* obj, size_t* usable_size) {
-  size_t result = S::AllocationSize(reinterpret_cast<mirror::Object*>(
-      reinterpret_cast<uint8_t*>(obj) - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)),
+  size_t result = S::AllocationSize(
+      reinterpret_cast<mirror::Object*>(
+          reinterpret_cast<uint8_t*>(obj)
+              - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)),
       usable_size);
   if (usable_size != nullptr) {
     if (kUseObjSizeForUsable) {
@@ -192,10 +219,9 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 size_t MemoryToolMallocSpace<S,
-                           kMemoryToolRedZoneBytes,
-                           kAdjustForRedzoneInAllocSize,
-                           kUseObjSizeForUsable>::Free(
-    Thread* self, mirror::Object* ptr) {
+                             kMemoryToolRedZoneBytes,
+                             kAdjustForRedzoneInAllocSize,
+                             kUseObjSizeForUsable>::Free(Thread* self, mirror::Object* ptr) {
   void* obj_after_rdz = reinterpret_cast<void*>(ptr);
   uint8_t* obj_with_rdz = reinterpret_cast<uint8_t*>(obj_after_rdz) - kMemoryToolRedZoneBytes;
 
@@ -220,10 +246,10 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 size_t MemoryToolMallocSpace<S,
-                           kMemoryToolRedZoneBytes,
-                           kAdjustForRedzoneInAllocSize,
-                           kUseObjSizeForUsable>::FreeList(
-    Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
+                             kMemoryToolRedZoneBytes,
+                             kAdjustForRedzoneInAllocSize,
+                             kUseObjSizeForUsable>::FreeList(
+                                 Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
   size_t freed = 0;
   for (size_t i = 0; i < num_ptrs; i++) {
     freed += Free(self, ptrs[i]);
@@ -238,11 +264,12 @@
           bool kUseObjSizeForUsable>
 template <typename... Params>
 MemoryToolMallocSpace<S,
-                    kMemoryToolRedZoneBytes,
-                    kAdjustForRedzoneInAllocSize,
-                    kUseObjSizeForUsable>::MemoryToolMallocSpace(
-    MemMap* mem_map, size_t initial_size, Params... params) : S(mem_map, initial_size, params...) {
-  // Don't want to change the valgrind states of the mem map here as the allocator is already
+                      kMemoryToolRedZoneBytes,
+                      kAdjustForRedzoneInAllocSize,
+                      kUseObjSizeForUsable>::MemoryToolMallocSpace(
+                          MemMap* mem_map, size_t initial_size, Params... params)
+                          : S(mem_map, initial_size, params...) {
+  // Don't want to change the memory tool states of the mem map here as the allocator is already
   // initialized at this point and that may interfere with what the allocator does internally. Note
   // that the tail beyond the initial size is mprotected.
 }
@@ -252,9 +279,9 @@
           bool kAdjustForRedzoneInAllocSize,
           bool kUseObjSizeForUsable>
 size_t MemoryToolMallocSpace<S,
-                           kMemoryToolRedZoneBytes,
-                           kAdjustForRedzoneInAllocSize,
-                           kUseObjSizeForUsable>::MaxBytesBulkAllocatedFor(size_t num_bytes) {
+                             kMemoryToolRedZoneBytes,
+                             kAdjustForRedzoneInAllocSize,
+                             kUseObjSizeForUsable>::MaxBytesBulkAllocatedFor(size_t num_bytes) {
   return S::MaxBytesBulkAllocatedFor(num_bytes + 2 * kMemoryToolRedZoneBytes);
 }
 
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index e786536..b0402e4 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -77,7 +77,7 @@
 
   // Everything is set so record in immutable structure and leave
   uint8_t* begin = mem_map->Begin();
-  // TODO: Fix RosAllocSpace to support Valgrind/ASan. There is currently some issues with
+  // TODO: Fix RosAllocSpace to support ASan. There is currently some issues with
   // AllocationSize caused by redzones. b/12944686
   if (running_on_memory_tool) {
     return new MemoryToolMallocSpace<RosAllocSpace, kDefaultMemoryToolRedZoneBytes, false, true>(
@@ -382,12 +382,12 @@
   size_t size = obj->SizeOf<kVerifyNone>();
   bool add_redzones = false;
   if (kMaybeIsRunningOnMemoryTool) {
-    add_redzones = RUNNING_ON_MEMORY_TOOL ? kMemoryToolAddsRedzones : 0;
+    add_redzones = kRunningOnMemoryTool && kMemoryToolAddsRedzones;
     if (add_redzones) {
       size += 2 * kDefaultMemoryToolRedZoneBytes;
     }
   } else {
-    DCHECK_EQ(RUNNING_ON_MEMORY_TOOL, 0U);
+    DCHECK(!kRunningOnMemoryTool);
   }
   size_t size_by_size = rosalloc_->UsableSize(size);
   if (kIsDebugBuild) {
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index 9d16b87..4c17233 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -159,8 +159,8 @@
 
   void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size,
                         size_t maximum_size, bool low_memory_mode) OVERRIDE {
-    return CreateRosAlloc(base, morecore_start, initial_size, maximum_size, low_memory_mode,
-                          RUNNING_ON_MEMORY_TOOL != 0);
+    return CreateRosAlloc(
+        base, morecore_start, initial_size, maximum_size, low_memory_mode, kRunningOnMemoryTool);
   }
   static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size,
                                              size_t maximum_size, bool low_memory_mode,
diff --git a/runtime/image.cc b/runtime/image.cc
index 17fc664..7819c0b 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', '1', '\0' };  // Pre-allocated Throwables.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '2', '\0' };  // Boot image live objects.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index c6fc052..c1cde0a 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -211,8 +211,12 @@
     kOomeWhenThrowingOome,            // Pre-allocated OOME when throwing OOME.
     kOomeWhenHandlingStackOverflow,   // Pre-allocated OOME when handling StackOverflowError.
     kNoClassDefFoundError,            // Pre-allocated NoClassDefFoundError.
-    kClassLoader,                     // App image only.
+    kSpecialRoots,                    // Different for boot image and app image, see aliases below.
     kImageRootsMax,
+
+    // Aliases.
+    kAppImageClassLoader = kSpecialRoots,   // The class loader used to build the app image.
+    kBootImageLiveObjects = kSpecialRoots,  // Array of boot image objects that must be kept live.
   };
 
   enum ImageSections {
@@ -229,8 +233,10 @@
     kSectionCount,  // Number of elements in enum.
   };
 
-  static size_t NumberOfImageRoots(bool app_image) {
-    return app_image ? kImageRootsMax : kImageRootsMax - 1u;
+  static size_t NumberOfImageRoots(bool app_image ATTRIBUTE_UNUSED) {
+    // At the moment, boot image and app image have the same number of roots,
+    // though the meaning of the kSpecialRoots is different.
+    return kImageRootsMax;
   }
 
   ArtMethod* GetImageMethod(ImageMethod index) const;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 27f761a..92d4731 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -1164,7 +1164,7 @@
                                                       ShadowFrame& shadow_frame,
                                                       uint32_t call_site_idx)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  StackHandleScope<7> hs(self);
+  StackHandleScope<5> hs(self);
   // There are three mandatory arguments expected from the call site
   // value array in the DEX file: the bootstrap method handle, the
   // method name to pass to the bootstrap method, and the method type
@@ -1358,75 +1358,80 @@
   }
 
   // Check the call site target is not null as we're going to invoke it.
-  Handle<mirror::CallSite> call_site =
-      hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL())));
-  Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
-  if (UNLIKELY(target.IsNull())) {
+  ObjPtr<mirror::CallSite> call_site =
+      ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL()));
+  ObjPtr<mirror::MethodHandle> target = call_site->GetTarget();
+  if (UNLIKELY(target == nullptr)) {
     ThrowClassCastException("Bootstrap method returned a CallSite with a null target");
     return nullptr;
   }
-  return call_site.Get();
+  return call_site;
 }
 
-template<bool is_range>
+namespace {
+
+ObjPtr<mirror::CallSite> DoResolveCallSite(Thread* self,
+                                           ShadowFrame& shadow_frame,
+                                           uint32_t call_site_idx)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache()));
+
+  // Get the call site from the DexCache if present.
+  ObjPtr<mirror::CallSite> call_site = dex_cache->GetResolvedCallSite(call_site_idx);
+  if (LIKELY(call_site != nullptr)) {
+    return call_site;
+  }
+
+  // Invoke the bootstrap method to get a candidate call site.
+  call_site = InvokeBootstrapMethod(self, shadow_frame, call_site_idx);
+  if (UNLIKELY(call_site == nullptr)) {
+    if (!self->GetException()->IsError()) {
+      // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error.
+      ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
+                                       call_site_idx);
+    }
+    return nullptr;
+  }
+
+  // Attempt to place the candidate call site into the DexCache, return the winning call site.
+  return dex_cache->SetResolvedCallSite(call_site_idx, call_site);
+}
+
+}  // namespace
+
 bool DoInvokeCustom(Thread* self,
                     ShadowFrame& shadow_frame,
-                    const Instruction* inst,
-                    uint16_t inst_data,
-                    JValue* result)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
+                    uint32_t call_site_idx,
+                    const InstructionOperands* operands,
+                    JValue* result) {
   // Make sure to check for async exceptions
   if (UNLIKELY(self->ObserveAsyncException())) {
     return false;
   }
+
   // invoke-custom is not supported in transactions. In transactions
   // there is a limited set of types supported. invoke-custom allows
   // running arbitrary code and instantiating arbitrary types.
   CHECK(!Runtime::Current()->IsActiveTransaction());
-  StackHandleScope<4> hs(self);
-  Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache()));
-  const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
-  MutableHandle<mirror::CallSite>
-      call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx)));
+
+  ObjPtr<mirror::CallSite> call_site = DoResolveCallSite(self, shadow_frame, call_site_idx);
   if (call_site.IsNull()) {
-    call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx));
-    if (UNLIKELY(call_site.IsNull())) {
-      CHECK(self->IsExceptionPending());
-      if (!self->GetException()->IsError()) {
-        // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error.
-        ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
-                                         call_site_idx);
-      }
-      result->SetJ(0);
-      return false;
-    }
-    mirror::CallSite* winning_call_site =
-        dex_cache->SetResolvedCallSite(call_site_idx, call_site.Get());
-    call_site.Assign(winning_call_site);
+    DCHECK(self->IsExceptionPending());
+    return false;
   }
 
+  StackHandleScope<2> hs(self);
   Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
   Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
-  DCHECK_EQ(static_cast<size_t>(inst->VRegA()), target_method_type->NumberOfVRegs());
-  if (is_range) {
-    RangeInstructionOperands operands(inst->VRegC_3rc(), inst->VRegA_3rc());
-    return MethodHandleInvokeExact(self,
-                                   shadow_frame,
-                                   target,
-                                   target_method_type,
-                                   &operands,
-                                   result);
-  } else {
-    uint32_t args[Instruction::kMaxVarArgRegs];
-    inst->GetVarArgs(args, inst_data);
-    VarArgsInstructionOperands operands(args, inst->VRegA_35c());
-    return MethodHandleInvokeExact(self,
-                                   shadow_frame,
-                                   target,
-                                   target_method_type,
-                                   &operands,
-                                   result);
-  }
+  DCHECK_EQ(operands->GetNumberOfOperands(), target_method_type->NumberOfVRegs())
+      << " call_site_idx" << call_site_idx;
+  return MethodHandleInvokeExact(self,
+                                 shadow_frame,
+                                 target,
+                                 target_method_type,
+                                 operands,
+                                 result);
 }
 
 // Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame.
@@ -1847,16 +1852,6 @@
 EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
 #undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
 
-// Explicit DoInvokeCustom template function declarations.
-#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range)               \
-  template REQUIRES_SHARED(Locks::mutator_lock_)                         \
-  bool DoInvokeCustom<_is_range>(                                        \
-      Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,  \
-      uint16_t inst_data, JValue* result)
-EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false);
-EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true);
-#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL
-
 // Explicit DoFilledNewArray template function declarations.
 #define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active)       \
   template REQUIRES_SHARED(Locks::mutator_lock_)                                                  \
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 60bf505..b324b4c 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -242,7 +242,15 @@
                          ShadowFrame& shadow_frame,
                          const Instruction* inst,
                          uint16_t inst_data,
-                         JValue* result);
+                         JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+bool DoInvokeCustom(Thread* self,
+                    ShadowFrame& shadow_frame,
+                    uint32_t call_site_idx,
+                    const InstructionOperands* operands,
+                    JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Performs a custom invoke (invoke-custom/invoke-custom-range).
 template<bool is_range>
@@ -250,7 +258,19 @@
                     ShadowFrame& shadow_frame,
                     const Instruction* inst,
                     uint16_t inst_data,
-                    JValue* result);
+                    JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+  if (is_range) {
+    RangeInstructionOperands operands(inst->VRegC_3rc(), inst->VRegA_3rc());
+    return DoInvokeCustom(self, shadow_frame, call_site_idx, &operands, result);
+  } else {
+    uint32_t args[Instruction::kMaxVarArgRegs];
+    inst->GetVarArgs(args, inst_data);
+    VarArgsInstructionOperands operands(args, inst->VRegA_35c());
+    return DoInvokeCustom(self, shadow_frame, call_site_idx, &operands, result);
+  }
+}
 
 // Handles invoke-virtual-quick and invoke-virtual-quick-range instructions.
 // Returns true on success, otherwise throws an exception and returns false.
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 655713e..01e7496 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -867,11 +867,6 @@
 }
 
 TEST_F(UnstartedRuntimeTest, Pow) {
-  // Valgrind seems to get this wrong, actually. Disable for valgrind.
-  if (RUNNING_ON_MEMORY_TOOL != 0 && kMemoryToolIsValgrind) {
-    return;
-  }
-
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
 
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 5a5634e..0a8e0cd 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -333,7 +333,7 @@
     }
 
     // When running sanitized, let all tasks finish to not leak. Otherwise just clear the queue.
-    if (!RUNNING_ON_MEMORY_TOOL) {
+    if (!kRunningOnMemoryTool) {
       pool->StopWorkers(self);
       pool->RemoveAllTasks(self);
     }
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 96778aa..e64a325 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -157,7 +157,8 @@
   return ref.load(std::memory_order_seq_cst).Read();
 }
 
-inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) {
+inline ObjPtr<CallSite> DexCache::SetResolvedCallSite(uint32_t call_site_idx,
+                                                      ObjPtr<CallSite> call_site) {
   DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
   DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
 
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index bb86004..941248e 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -320,8 +320,8 @@
   // because multiple threads can invoke the bootstrap method each
   // producing a call site, but the method handle invocation on the
   // call site must be on a common agreed value.
-  CallSite* SetResolvedCallSite(uint32_t call_site_idx, CallSite* resolved) WARN_UNUSED
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  ObjPtr<CallSite> SetResolvedCallSite(uint32_t call_site_idx, ObjPtr<CallSite> resolved)
+      REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED;
 
   StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr64<StringDexCacheType*>(StringsOffset());
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 14f3f45..b3a47c3 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -289,8 +289,10 @@
                      ArtMethod* current_method,
                      void* ucontext_ptr,
                      bool skip_frames) {
-  // b/18119146
-  if (RUNNING_ON_MEMORY_TOOL != 0) {
+  // Historical note: This was disabled when running under Valgrind (b/18119146).
+  // TODO: Valgrind is no longer supported, but Address Sanitizer is:
+  // check whether this test works with ASan.
+  if (kRunningOnMemoryTool) {
     return;
   }
 
diff --git a/runtime/oat.h b/runtime/oat.h
index 72eb27d..40f4edd 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: Rewrite TypeLookupTable.
-  static constexpr uint8_t kOatVersion[] = { '1', '4', '7', '\0' };
+  // Last oat version changed reason: compiler support invoke-custom
+  static constexpr uint8_t kOatVersion[] = { '1', '4', '8', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 7efd000..3b4d177 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -240,7 +240,7 @@
       exit_(nullptr),
       abort_(nullptr),
       stats_enabled_(false),
-      is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL),
+      is_running_on_memory_tool_(kRunningOnMemoryTool),
       instrumentation_(),
       main_thread_group_(nullptr),
       system_thread_group_(nullptr),
@@ -1368,8 +1368,8 @@
     case InstructionSet::kMips:
     case InstructionSet::kMips64:
       implicit_null_checks_ = true;
-      // Installing stack protection does not play well with valgrind.
-      implicit_so_checks_ = !(RUNNING_ON_MEMORY_TOOL && kMemoryToolIsValgrind);
+      // Historical note: Installing stack protection was not playing well with Valgrind.
+      implicit_so_checks_ = true;
       break;
     default:
       // Keep the defaults.
@@ -1384,8 +1384,8 @@
       // These need to be in a specific order.  The null point check handler must be
       // after the suspend check and stack overflow check handlers.
       //
-      // Note: the instances attach themselves to the fault manager and are handled by it. The manager
-      //       will delete the instance on Shutdown().
+      // Note: the instances attach themselves to the fault manager and are handled by it. The
+      //       manager will delete the instance on Shutdown().
       if (implicit_suspend_checks_) {
         new SuspensionHandler(&fault_manager);
       }
@@ -2051,6 +2051,7 @@
   pre_allocated_OutOfMemoryError_when_handling_stack_overflow_
       .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
   pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+  VisitImageRoots(visitor);
   verifier::MethodVerifier::VisitStaticRoots(visitor);
   VisitTransactionRoots(visitor);
 }
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 72d9919..54769f9 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -339,8 +339,8 @@
 };
 
 TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) {
-  // SigQuit induces a dump. ASAN isn't happy with libunwind reading memory.
-  TEST_DISABLED_FOR_MEMORY_TOOL_ASAN();
+  // SigQuit induces a dump. ASan isn't happy with libunwind reading memory.
+  TEST_DISABLED_FOR_MEMORY_TOOL();
 
   // The runtime needs to be started for the signal handler.
   Thread* self = Thread::Current();
diff --git a/runtime/thread.cc b/runtime/thread.cc
index afaf945..4a53425 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1116,21 +1116,10 @@
   Runtime* runtime = Runtime::Current();
   bool implicit_stack_check = !runtime->ExplicitStackOverflowChecks() && !runtime->IsAotCompiler();
 
-  // Valgrind on arm doesn't give the right values here. Do not install the guard page, and
-  // effectively disable stack overflow checks (we'll get segfaults, potentially) by setting
-  // stack_begin to 0.
-  const bool valgrind_on_arm =
-      (kRuntimeISA == InstructionSet::kArm || kRuntimeISA == InstructionSet::kArm64) &&
-      kMemoryToolIsValgrind &&
-      RUNNING_ON_MEMORY_TOOL != 0;
-  if (valgrind_on_arm) {
-    tlsPtr_.stack_begin = nullptr;
-  }
-
   ResetDefaultStackEnd();
 
   // Install the protected region if we are doing implicit overflow checks.
-  if (implicit_stack_check && !valgrind_on_arm) {
+  if (implicit_stack_check) {
     // The thread might have protected region at the bottom.  We need
     // to install our own region so we need to move the limits
     // of the stack to make room for it.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5961748..61ddded 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -212,8 +212,6 @@
                                         bool allow_soft_failures,
                                         HardFailLogMode log_level,
                                         std::string* error) {
-  SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def));
-
   // A class must not be abstract and final.
   if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) {
     *error = "Verifier rejected class ";
@@ -223,6 +221,7 @@
   }
 
   ClassAccessor accessor(*dex_file, class_def);
+  SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(accessor.GetDescriptor());
 
   int64_t previous_method_idx[2] = { -1, -1 };
   MethodVerifier::FailureData failure_data;
@@ -3054,10 +3053,7 @@
       // Step 2. Check the register arguments correspond to the expected arguments for the
       // method handle produced by step 1. The dex file verifier has checked ranges for
       // the first three arguments and CheckCallSite has checked the method handle type.
-      CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
-      it.Next();  // Skip to name.
-      it.Next();  // Skip to method type of the method handle
-      const dex::ProtoIndex proto_idx(it.GetJavaValue().c);
+      const dex::ProtoIndex proto_idx = dex_file_->GetProtoIndexForCallSite(call_site_idx);
       const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx);
       DexFileParameterIterator param_it(*dex_file_, proto_id);
       // Treat method as static as it has yet to be determined.
@@ -3073,8 +3069,6 @@
         work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
       }
       just_set_result = true;
-      // TODO: Add compiler support for invoke-custom (b/35337872).
-      Fail(VERIFY_ERROR_FORCE_INTERPRETER);
       break;
     }
     case Instruction::NEG_INT:
@@ -4008,24 +4002,41 @@
   CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
   // Check essential arguments are provided. The dex file verifier has verified indicies of the
   // main values (method handle, name, method_type).
-  if (it.Size() < 3) {
+  static const size_t kRequiredArguments = 3;
+  if (it.Size() < kRequiredArguments) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
                                       << " has too few arguments: "
-                                      << it.Size() << " < 3";
+                                      << it.Size() << " < " << kRequiredArguments;
     return false;
   }
 
-  // Get and check the first argument: the method handle (index range
-  // checked by the dex file verifier).
-  uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
-  if (method_handle_idx > dex_file_->NumMethodHandles()) {
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx
-                                      << " method handle index invalid " << method_handle_idx
-                                      << " >= "  << dex_file_->NumMethodHandles();
-    return false;
+  std::pair<const EncodedArrayValueIterator::ValueType, size_t> type_and_max[kRequiredArguments] =
+      { { EncodedArrayValueIterator::ValueType::kMethodHandle, dex_file_->NumMethodHandles() },
+        { EncodedArrayValueIterator::ValueType::kString, dex_file_->NumStringIds() },
+        { EncodedArrayValueIterator::ValueType::kMethodType, dex_file_->NumProtoIds() }
+      };
+  uint32_t index[kRequiredArguments];
+
+  // Check arguments have expected types and are within permitted ranges.
+  for (size_t i = 0; i < kRequiredArguments; ++i) {
+    if (it.GetValueType() != type_and_max[i].first) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx
+                                        << " argument " << i << " has wrong type "
+                                        << it.GetValueType() << "!=" << type_and_max[i].first;
+      return false;
+    }
+    index[i] = static_cast<uint32_t>(it.GetJavaValue().i);
+    if (index[i] >= type_and_max[i].second) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx
+                                        << " argument " << i << " bad index "
+                                        << index[i] << " >= " << type_and_max[i].second;
+      return false;
+    }
+    it.Next();
   }
 
-  const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx);
+  // Check method handle kind is valid.
+  const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(index[0]);
   if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
                                       << " argument 0 method handle type is not InvokeStatic: "
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 7b54b2f..c64e7bb 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -76,6 +76,7 @@
 jclass WellKnownClasses::java_util_function_Consumer;
 jclass WellKnownClasses::libcore_reflect_AnnotationFactory;
 jclass WellKnownClasses::libcore_reflect_AnnotationMember;
+jclass WellKnownClasses::libcore_util_EmptyArray;
 jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk;
 jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer;
 
@@ -136,7 +137,6 @@
 jfieldID WellKnownClasses::java_lang_Throwable_stackTrace;
 jfieldID WellKnownClasses::java_lang_Throwable_stackState;
 jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions;
-jfieldID WellKnownClasses::java_lang_Throwable_UNASSIGNED_STACK;
 jfieldID WellKnownClasses::java_nio_ByteBuffer_address;
 jfieldID WellKnownClasses::java_nio_ByteBuffer_hb;
 jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly;
@@ -145,6 +145,7 @@
 jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity;
 jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress;
 jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST;
+jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
 jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data;
 jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length;
 jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset;
@@ -330,6 +331,7 @@
   java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer");
   libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory");
   libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember");
+  libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray");
   org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
   org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
 
@@ -383,7 +385,6 @@
   java_lang_Throwable_stackTrace = CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;");
   java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
   java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
-  java_lang_Throwable_UNASSIGNED_STACK = CacheField(env, java_lang_Throwable, true, "UNASSIGNED_STACK", "[Ljava/lang/StackTraceElement;");
   java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J");
   java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B");
   java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z");
@@ -392,6 +393,7 @@
   java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I");
   java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J");
   java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
+  libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
   org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B");
   org_apache_harmony_dalvik_ddmc_Chunk_length = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I");
   org_apache_harmony_dalvik_ddmc_Chunk_offset = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I");
@@ -460,6 +462,7 @@
   java_nio_DirectByteBuffer = nullptr;
   libcore_reflect_AnnotationFactory = nullptr;
   libcore_reflect_AnnotationMember = nullptr;
+  libcore_util_EmptyArray = nullptr;
   org_apache_harmony_dalvik_ddmc_Chunk = nullptr;
   org_apache_harmony_dalvik_ddmc_DdmServer = nullptr;
 
@@ -519,7 +522,6 @@
   java_lang_Throwable_stackTrace = nullptr;
   java_lang_Throwable_stackState = nullptr;
   java_lang_Throwable_suppressedExceptions = nullptr;
-  java_lang_Throwable_UNASSIGNED_STACK = nullptr;
   java_nio_ByteBuffer_address = nullptr;
   java_nio_ByteBuffer_hb = nullptr;
   java_nio_ByteBuffer_isReadOnly = nullptr;
@@ -528,6 +530,7 @@
   java_nio_DirectByteBuffer_capacity = nullptr;
   java_nio_DirectByteBuffer_effectiveDirectAddress = nullptr;
   java_util_Collections_EMPTY_LIST = nullptr;
+  libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr;
   org_apache_harmony_dalvik_ddmc_Chunk_data = nullptr;
   org_apache_harmony_dalvik_ddmc_Chunk_length = nullptr;
   org_apache_harmony_dalvik_ddmc_Chunk_offset = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index bed8770..c81062f 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -86,6 +86,7 @@
   static jclass java_nio_DirectByteBuffer;
   static jclass libcore_reflect_AnnotationFactory;
   static jclass libcore_reflect_AnnotationMember;
+  static jclass libcore_util_EmptyArray;
   static jclass org_apache_harmony_dalvik_ddmc_Chunk;
   static jclass org_apache_harmony_dalvik_ddmc_DdmServer;
 
@@ -146,7 +147,6 @@
   static jfieldID java_lang_Throwable_stackTrace;
   static jfieldID java_lang_Throwable_stackState;
   static jfieldID java_lang_Throwable_suppressedExceptions;
-  static jfieldID java_lang_Throwable_UNASSIGNED_STACK;
   static jfieldID java_nio_ByteBuffer_address;
   static jfieldID java_nio_ByteBuffer_hb;
   static jfieldID java_nio_ByteBuffer_isReadOnly;
@@ -156,6 +156,7 @@
   static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress;
 
   static jfieldID java_util_Collections_EMPTY_LIST;
+  static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_data;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset;
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 7ada47d..a91d348 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -114,8 +114,6 @@
     jint,
     jboolean) {
 #if __linux__
-  // TODO: What to do on Valgrind?
-
   std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
   if (!bt->Unwind(0, nullptr)) {
     printf("Cannot unwind in process.\n");
@@ -191,7 +189,6 @@
     jboolean,
     jint pid_int) {
 #if __linux__
-  // TODO: What to do on Valgrind?
   pid_t pid = static_cast<pid_t>(pid_int);
 
   // OK, this is painful. debuggerd uses ptrace to unwind other processes.
diff --git a/test/952-invoke-custom/src/TestReturnValues.java b/test/952-invoke-custom/src/TestReturnValues.java
new file mode 100644
index 0000000..8450a44
--- /dev/null
+++ b/test/952-invoke-custom/src/TestReturnValues.java
@@ -0,0 +1,330 @@
+/*
+ * 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 annotations.BootstrapMethod;
+import annotations.CalledByIndy;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+class TestReturnValues extends TestBase {
+    static CallSite bsm(MethodHandles.Lookup lookup, String name, MethodType methodType)
+            throws Throwable {
+        MethodHandle mh = lookup.findStatic(TestReturnValues.class, name, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    //
+    // Methods that pass through a single argument.
+    // Used to check return path.
+    //
+    static byte passThrough(byte value) {
+        return value;
+    }
+
+    static char passThrough(char value) {
+        return value;
+    }
+
+    static double passThrough(double value) {
+        return value;
+    }
+
+    static float passThrough(float value) {
+        return value;
+    }
+
+    static int passThrough(int value) {
+        return value;
+    }
+
+    static Object passThrough(Object value) {
+        return value;
+    }
+
+    static Object[] passThrough(Object[] value) {
+        return value;
+    }
+
+    static long passThrough(long value) {
+        return value;
+    }
+
+    static short passThrough(short value) {
+        return value;
+    }
+
+    static void passThrough() {}
+
+    static boolean passThrough(boolean value) {
+        return value;
+    }
+
+    // byte
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = byte.class,
+            parameterTypes = {byte.class})
+    private static byte passThroughCallSite(byte value) {
+        assertNotReached();
+        return (byte) 0;
+    }
+
+    // char
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = char.class,
+            parameterTypes = {char.class})
+    private static char passThroughCallSite(char value) {
+        assertNotReached();
+        return 'Z';
+    }
+
+    // double
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = double.class,
+            parameterTypes = {double.class})
+    private static double passThroughCallSite(double value) {
+        assertNotReached();
+        return Double.NaN;
+    }
+
+    // float
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = float.class,
+            parameterTypes = {float.class})
+    private static float passThroughCallSite(float value) {
+        assertNotReached();
+        return Float.NaN;
+    }
+
+    // int
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = int.class,
+            parameterTypes = {int.class})
+    private static int passThroughCallSite(int value) {
+        assertNotReached();
+        return 0;
+    }
+
+    // long
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = long.class,
+            parameterTypes = {long.class})
+    private static long passThroughCallSite(long value) {
+        assertNotReached();
+        return Long.MIN_VALUE;
+    }
+
+    // Object
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = Object.class,
+            parameterTypes = {Object.class})
+    private static Object passThroughCallSite(Object value) {
+        assertNotReached();
+        return null;
+    }
+
+    // Object[]
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = Object[].class,
+            parameterTypes = {Object[].class})
+    private static Object[] passThroughCallSite(Object[] value) {
+        assertNotReached();
+        return null;
+    }
+
+    // short
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = short.class,
+            parameterTypes = {short.class})
+    private static short passThroughCallSite(short value) {
+        assertNotReached();
+        return (short) 0;
+    }
+
+    // void
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = void.class,
+            parameterTypes = {})
+    private static void passThroughCallSite() {
+        assertNotReached();
+    }
+
+    // boolean
+    @CalledByIndy(
+            bootstrapMethod =
+                    @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+            fieldOrMethodName = "passThrough",
+            returnType = boolean.class,
+            parameterTypes = {boolean.class})
+    private static boolean passThroughCallSite(boolean value) {
+        assertNotReached();
+        return false;
+    }
+
+    private static void testByteReturnValues() {
+        byte[] values = {Byte.MIN_VALUE, Byte.MAX_VALUE};
+        for (byte value : values) {
+            assertEquals(value, (byte) passThroughCallSite(value));
+        }
+    }
+
+    private static void testCharReturnValues() {
+        char[] values = {
+            Character.MIN_VALUE,
+            Character.MAX_HIGH_SURROGATE,
+            Character.MAX_LOW_SURROGATE,
+            Character.MAX_VALUE
+        };
+        for (char value : values) {
+            assertEquals(value, (char) passThroughCallSite(value));
+        }
+    }
+
+    private static void testDoubleReturnValues() {
+        double[] values = {
+            Double.MIN_VALUE,
+            Double.MIN_NORMAL,
+            Double.NaN,
+            Double.POSITIVE_INFINITY,
+            Double.NEGATIVE_INFINITY,
+            Double.MAX_VALUE
+        };
+        for (double value : values) {
+            assertEquals(value, (double) passThroughCallSite(value));
+        }
+    }
+
+    private static void testFloatReturnValues() {
+        float[] values = {
+            Float.MIN_VALUE,
+            Float.MIN_NORMAL,
+            Float.NaN,
+            Float.POSITIVE_INFINITY,
+            Float.NEGATIVE_INFINITY,
+            Float.MAX_VALUE
+        };
+        for (float value : values) {
+            assertEquals(value, (float) passThroughCallSite(value));
+        }
+    }
+
+    private static void testIntReturnValues() {
+        int[] values = {Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.SIZE, -Integer.SIZE};
+        for (int value : values) {
+            assertEquals(value, (int) passThroughCallSite(value));
+        }
+    }
+
+    private static void testLongReturnValues() {
+        long[] values = {Long.MIN_VALUE, Long.MAX_VALUE, (long) Long.SIZE, (long) -Long.SIZE};
+        for (long value : values) {
+            assertEquals(value, (long) passThroughCallSite(value));
+        }
+    }
+
+    private static void testObjectReturnValues() {
+        Object[] values = {null, "abc", Integer.valueOf(123)};
+        for (Object value : values) {
+            assertEquals(value, (Object) passThroughCallSite(value));
+        }
+
+        Object[] otherValues = (Object[]) passThroughCallSite(values);
+        assertEquals(values.length, otherValues.length);
+        for (int i = 0; i < otherValues.length; ++i) {
+            assertEquals(values[i], otherValues[i]);
+        }
+    }
+
+    private static void testShortReturnValues() {
+        short[] values = {
+            Short.MIN_VALUE, Short.MAX_VALUE, (short) Short.SIZE, (short) -Short.SIZE
+        };
+        for (short value : values) {
+            assertEquals(value, (short) passThroughCallSite(value));
+        }
+    }
+
+    private static void testVoidReturnValues() {
+        long l = Long.MIN_VALUE;
+        double d = Double.MIN_VALUE;
+        passThroughCallSite(); // Initializes call site
+        assertEquals(Long.MIN_VALUE, l);
+        assertEquals(Double.MIN_VALUE, d);
+
+        l = Long.MAX_VALUE;
+        d = Double.MAX_VALUE;
+        passThroughCallSite(); // re-uses existing call site
+        assertEquals(Long.MAX_VALUE, l);
+        assertEquals(Double.MAX_VALUE, d);
+    }
+
+    private static void testBooleanReturnValues() {
+        boolean[] values = {true, false, true, false, false};
+        for (boolean value : values) {
+            assertEquals(value, (boolean) passThroughCallSite(value));
+        }
+    }
+
+    public static void test() {
+        System.out.println(TestReturnValues.class.getName());
+        // Two passes here - the first is for the call site creation and invoke path, the second
+        // for the lookup and invoke path.
+        for (int pass = 0; pass < 2; ++pass) {
+            testByteReturnValues(); // B
+            testCharReturnValues(); // C
+            testDoubleReturnValues(); // D
+            testFloatReturnValues(); // F
+            testIntReturnValues(); // I
+            testLongReturnValues(); // J
+            testObjectReturnValues(); // L
+            testShortReturnValues(); // S
+            testVoidReturnValues(); // S
+            testBooleanReturnValues(); // Z
+        }
+    }
+}
diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt
index 6954c22..a8b609b 100644
--- a/test/956-methodhandles/expected.txt
+++ b/test/956-methodhandles/expected.txt
@@ -15,6 +15,7 @@
 Chatty.chatter()
 Chatty.chatter()
 String constructors done.
+testReturnValues done.
 testReferenceReturnValueConversions done.
 testPrimitiveReturnValueConversions done.
 Hi
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index dee818a..11d6ead 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -102,6 +102,7 @@
     testAsType();
     testConstructors();
     testStringConstructors();
+    testReturnValues();
     testReturnValueConversions();
     testVariableArity();
     testVariableArity_MethodHandles_bind();
@@ -873,6 +874,89 @@
     System.out.println("String constructors done.");
   }
 
+  private static void testReturnValues() throws Throwable {
+    Lookup lookup = MethodHandles.lookup();
+
+    // byte
+    MethodHandle mhByteValue =
+        lookup.findVirtual(Byte.class, "byteValue", MethodType.methodType(byte.class));
+    assertEquals((byte) -77, (byte) mhByteValue.invokeExact(Byte.valueOf((byte) -77)));
+    assertEquals((byte) -77, (byte) mhByteValue.invoke(Byte.valueOf((byte) -77)));
+
+    // char
+    MethodHandle mhCharacterValue =
+        lookup.findStaticGetter(Character.class, "MAX_SURROGATE", char.class);
+    assertEquals(Character.MAX_SURROGATE, (char) mhCharacterValue.invokeExact());
+    assertEquals(Character.MAX_SURROGATE, (char) mhCharacterValue.invoke());
+
+    // double
+    MethodHandle mhSin =
+        lookup.findStatic(
+            Math.class, "sin", MethodType.methodType(double.class, double.class));
+    for (double i = -Math.PI; i <= Math.PI; i += Math.PI / 8) {
+      assertEquals(Math.sin(i), (double) mhSin.invokeExact(i));
+      assertEquals(Math.sin(i), (double) mhSin.invoke(i));
+    }
+
+    // float
+    MethodHandle mhAbsFloat =
+        lookup.findStatic(
+            Math.class, "abs", MethodType.methodType(float.class, float.class));
+    assertEquals(Math.abs(-3.3e6f), (float) mhAbsFloat.invokeExact(-3.3e6f));
+    assertEquals(Math.abs(-3.3e6f), (float) mhAbsFloat.invoke(-3.3e6f));
+
+    // int
+    MethodHandle mhAbsInt =
+        lookup.findStatic(Math.class, "abs", MethodType.methodType(int.class, int.class));
+    assertEquals(Math.abs(-1000), (int) mhAbsInt.invokeExact(-1000));
+    assertEquals(Math.abs(-1000), (int) mhAbsInt.invoke(-1000));
+
+    // long
+    MethodHandle mhMaxLong =
+        lookup.findStatic(
+            Math.class,
+            "max",
+            MethodType.methodType(long.class, long.class, long.class));
+    assertEquals(
+        Long.MAX_VALUE, (long) mhMaxLong.invokeExact(Long.MAX_VALUE, Long.MAX_VALUE / 2));
+    assertEquals(Long.MAX_VALUE, (long) mhMaxLong.invoke(Long.MAX_VALUE, Long.MAX_VALUE / 2));
+    assertEquals(0x0123456789abcdefL, (long) mhMaxLong.invokeExact(0x0123456789abcdefL, 0L));
+    assertEquals(0x0123456789abcdefL, (long) mhMaxLong.invoke(0x0123456789abcdefL, 0L));
+
+    // ref
+    MethodHandle mhShortValueOf =
+        lookup.findStatic(
+            Short.class, "valueOf", MethodType.methodType(Short.class, short.class));
+    assertEquals(
+        (short) -7890, ((Short) mhShortValueOf.invokeExact((short) -7890)).shortValue());
+    assertEquals((short) -7890, ((Short) mhShortValueOf.invoke((short) -7890)).shortValue());
+
+    // array
+    int [] array = {Integer.MIN_VALUE, -1, 0, +1, Integer.MAX_VALUE};
+    MethodHandle mhCopyOf =
+            lookup.findStatic(
+                Arrays.class, "copyOf", MethodType.methodType(int[].class, int[].class, int.class));
+    assertTrue(Arrays.equals(array, (int[]) mhCopyOf.invokeExact(array, array.length)));
+    assertTrue(Arrays.equals(array, (int[]) mhCopyOf.invoke(array, array.length)));
+
+    // short
+    MethodHandle mhShortValue =
+        lookup.findVirtual(Short.class, "shortValue", MethodType.methodType(short.class));
+    assertEquals((short) 12131, (short) mhShortValue.invokeExact(Short.valueOf((short) 12131)));
+    assertEquals((short) 12131, (short) mhShortValue.invoke(Short.valueOf((short) 12131)));
+
+    // boolean
+    MethodHandle mhBooleanValue =
+        lookup.findVirtual(
+            Boolean.class, "booleanValue", MethodType.methodType(boolean.class));
+    assertEquals(true, (boolean) mhBooleanValue.invokeExact(Boolean.valueOf(true)));
+    assertEquals(true, (boolean) mhBooleanValue.invoke(Boolean.valueOf(true)));
+    assertEquals(false, (boolean) mhBooleanValue.invokeExact(Boolean.valueOf(false)));
+    assertEquals(false, (boolean) mhBooleanValue.invoke(Boolean.valueOf(false)));
+
+    System.out.println("testReturnValues done.");
+  }
+
   private static void testReferenceReturnValueConversions() throws Throwable {
     MethodHandle mh = MethodHandles.lookup().findStatic(
         Float.class, "valueOf", MethodType.methodType(Float.class, String.class));
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 71f4cc0..68efcaf 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -266,14 +266,16 @@
         }
     },
     'art-gtest-valgrind32': {
-      # Disabled: x86 valgrind does not understand SSE4.x
+      # Disabled: Valgrind is no longer supported.
+      # Historical note: This was already disabled, as x86 valgrind did not understand SSE4.x
       # 'make' : 'valgrind-test-art-host32',
         'env': {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gtest-valgrind64': {
-        'make' : 'valgrind-test-art-host64',
+      # Disabled: Valgrind is no longer supported.
+      # 'make' : 'valgrind-test-art-host64',
         'env': {
             'ART_USE_READ_BARRIER' : 'false'
         }
diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt
deleted file mode 100644
index a97d03c..0000000
--- a/test/valgrind-suppressions.txt
+++ /dev/null
@@ -1,87 +0,0 @@
-{
-   b/27596582
-   Memcheck:Cond
-   fun:index
-   fun:expand_dynamic_string_token
-   fun:_dl_map_object
-   fun:map_doit
-   fun:_dl_catch_error
-   fun:do_preload
-   fun:dl_main
-   fun:_dl_sysdep_start
-   fun:_dl_start_final
-   fun:_dl_start
-   obj:/lib/x86_64-linux-gnu/ld-2.19.so
-}
-
-{
-   b/31275764
-   Memcheck:Leak
-   match-leak-kinds: definite
-   fun:malloc
-   ...
-   fun:_ZN3art7Runtime17InitNativeMethodsEv
-}
-
-# SigQuit runs libbacktrace
-{
-   BackTraceReading64
-   Memcheck:Addr8
-   fun:access_mem_unrestricted
-   fun:_Uelf64_memory_read
-   fun:_Uelf64_valid_object_memory
-   fun:map_create_list
-   fun:unw_map_local_create
-   fun:_ZN14UnwindMapLocal5BuildEv
-   fun:_ZN12BacktraceMap6CreateEib
-}
-{
-   BackTraceReading32
-   Memcheck:Addr4
-   fun:access_mem_unrestricted
-   fun:_Uelf32_memory_read
-   fun:_Uelf32_valid_object_memory
-   fun:map_create_list
-   fun:unw_map_local_create
-   fun:_ZN14UnwindMapLocal5BuildEv
-   fun:_ZN12BacktraceMap6CreateEib
-}
-{
-   BackTraceReading64
-   Memcheck:Addr8
-   fun:access_mem_unrestricted
-   fun:_Uelf64_memory_read
-   fun:_Uelf64_get_load_base
-   fun:map_create_list
-   fun:unw_map_local_create
-   fun:_ZN14UnwindMapLocal5BuildEv
-   fun:_ZN12BacktraceMap6CreateEib
-}
-{
-   BackTraceReading32
-   Memcheck:Addr4
-   fun:access_mem_unrestricted
-   fun:_Uelf32_memory_read
-   fun:_Uelf32_get_load_base
-   fun:map_create_list
-   fun:unw_map_local_create
-   fun:_ZN14UnwindMapLocal5BuildEv
-   fun:_ZN12BacktraceMap6CreateEib
-}
-
-{
-   process_vm_readv
-   Memcheck:Param
-   process_vm_readv(lvec[...])
-   fun:process_vm_readv
-}
-
-# Suppressions for IsAddressMapped check in MemMapTest
-{
-   MemMapTest_IsAddressMapped
-   Memcheck:Param
-   msync(start)
-   ...
-   fun:_ZN3art10MemMapTest15IsAddressMappedEPv
-   ...
-}
diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt
deleted file mode 100644
index 0d63a1c..0000000
--- a/test/valgrind-target-suppressions.txt
+++ /dev/null
@@ -1,76 +0,0 @@
-# Valgrind does not recognize the ashmen ioctl() calls on ARM64, so it assumes that a size
-# parameter is a pointer.
-{
-   ashmem ioctl
-   Memcheck:Param
-   ioctl(generic)
-   ...
-   fun:ioctl
-   fun:ashmem_create_region
-}
-
-# It seems that on ARM64 Valgrind considers the canary value used by the Clang stack protector to
-# be an uninitialized value.
-{
-   jemalloc chunk_alloc_cache
-   Memcheck:Cond
-   fun:je_chunk_alloc_cache
-}
-
-# The VectorImpl class does not hold a pointer to the allocated SharedBuffer structure, but to the
-# beginning of the data, which is effectively an interior pointer. Valgrind has limitations when
-# dealing with interior pointers.
-{
-   VectorImpl
-   Memcheck:Leak
-   match-leak-kinds:possible
-   fun:malloc
-   # The wildcards make this rule work both for 32-bit and 64-bit environments.
-   fun:_ZN7android12SharedBuffer5allocE?
-   fun:_ZN7android10VectorImpl5_growE??
-}
-
-# Clang/LLVM uses memcpy for *x = *y, even though x == y (which is undefined behavior). Ignore.
-# b/29279679, https://llvm.org/bugs/show_bug.cgi?id=11763
-{
-   MemCpySelfAssign
-   Memcheck:Overlap
-   fun:memcpy
-   ...
-   fun:je_malloc_tsd_boot0
-}
-
-# Setenv is known-leaking when overwriting mappings. This is triggered by re-initializing
-# ANDROID_DATA. Ignore all setenv leaks.
-{
-   SetenvAndroidDataReinit
-   Memcheck:Leak
-   match-leak-kinds: definite
-   fun:malloc
-   fun:setenv
-}
-
-{
-   b/31275764
-   Memcheck:Leak
-   match-leak-kinds: definite
-   fun:malloc
-   ...
-   fun:_ZN3art7Runtime17InitNativeMethodsEv
-}
-
-# art::MemMap::MapInternal() uses msync() to check for the existence of memory mappings.
-{
-  art::MemMap::MapInternal()
-  Memcheck:Param
-  msync(start)
-  fun:msync
-  fun:_ZN3art6MemMap11MapInternalEPvmiiilb
-}
-
-{
-   process_vm_readv
-   Memcheck:Param
-   process_vm_readv(lvec[...])
-   fun:process_vm_readv
-}
diff --git a/tools/art b/tools/art
index 1c603d4..781ee2f 100644
--- a/tools/art
+++ b/tools/art
@@ -77,7 +77,6 @@
 Supported OPTIONS include:
   --32                     Use the 32-bit Android Runtime.
   --64                     Use the 64-bit Android Runtime.
-  --callgrind              Launch the Android Runtime in callgrind.
   -d                       Use the debug ART library (libartd.so).
   --debug                  Equivalent to -d.
   --gdb                    Launch the Android Runtime in gdb.
@@ -269,9 +268,6 @@
   --64)
     ART_BINARY=dalvikvm64
     ;;
-  --callgrind)
-    LAUNCH_WRAPPER="valgrind --tool=callgrind"
-    ;;
   -d)
     ;& # Fallthrough
   --debug)