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(®_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)